Extract Public Key in Python OpenSSL (pyOpenSSL) from certificate or other connection information
Question: I'm currently trying to write a python server script which should authenticate
the current client based on its public key. Since I'm using twisted, the
[example in the twisted
documenteation](http://twistedmatrix.com/documents/11.0.0/core/howto/ssl.html#auto7)
got me started.

While I can generate keys, connect and communicate using the example code, I
have not yet found a way to get the public key of the client in a usable
format. In [this stackexchange
question](http://stackoverflow.com/questions/10362965/how-to-get-public-key-
using-pyopenssl) somebody extracts the public key from an
`OpenSSL.crypto.PKey` object but cannot transform it to a readable format.
Since in I have access to the `PKey` object of the x509 certificate in the
`verifyCallback` method or via `self.transport.getPeerCertificate()` from any
method of my Protocol, this would be a good way to go. The (not accepted)
answer suggests to try `crypto.dump_privatekey(PKey)`. Unfortunately, this
does not really yield the expected result: While the `BEGIN PRIVATE KEY` and
`BEGIN PRIVATE KEY` in the answer could be fixed by an easy text replacement
function, the base64 string seems not match the public key. I've extracted the
public key with `openssl rsa -in client.key -pubout > client.pub` as mentioned
[here](http://stackoverflow.com/questions/10271197/openssl-how-to-extract-
public-key). It does not match the result of the `dump_privatekey` function.

While there still is an [open bug towards OpenSSL on
launchpad](https://bugs.launchpad.net/pyopenssl/+bug/780089), it is not yet
fixed. It was reported 19 Month ago, and there is some recent (October 2012)
activity on it, I do not have any hope of a fast fix in the repos.

Do you have any other ideas how I could get the public key in a format
comparable to the `client.pub` file I have mentioned above? Perhaps there is a
twisted or OpenSSL connection specific object which holds this information.
Please note that I have to store the public key in the protocol object such
that I can access it later.

## Why is no Answer accepted?

### M2Crypto by J.F. Sebastian

Sorry, that I had not thought of a possibility where I cannot correlate the
certificate to the connection. I've added the requirement that I have to store
the public key inside the protocol instance. Thus, using `peerX509.as_pem()`
inside the `postConnectionCheck` function as suggested by J.F. Sebastian does
not work. Furthermore, at least in version 0.21.1-2ubuntu3 of python-m2crypto
I have to call `peerX509.get_rsa().as_pem()` to get the right public key.
Using `peerX509.as_pem(None)` (since `peerX509.as_pem()` still wants a
passphrase) yields excactly the same output as `crypto.dump_privatekey(PKey)`
in PyOpenSSL. Maybe there is a bug.

Besides this, the answer showed me a possible way to write another workaround
by using the following `Echo` protocol class:

    
    
    class Echo(Protocol):
        def dataReceived(self, data):
            """As soon as any data is received, write it back."""
            if self.transport.checked and not self.pubkeyStored:
                self.pubkeyStored = True
                x509 = m2.ssl_get_peer_cert(self.transport.ssl._ptr())
                if x509 is not None:
                    x509 = X509.X509(x509, 1)
                    pk = x509.get_pubkey()
                    self.pubkey = pk.get_rsa().as_pem()
                    print pk.as_pem(None)
                print self.pubkey
            self.transport.write(data)
    

As you can see this uses some internal classes which I'd like to prevent. I'm
hesitating submitting a small patch which would add a `getCert` method to the
`TLSProtocolWrapper` class in M2Crypto.SSL.TwistedProtocolWrapper. Even if it
was accepted upstream, it would break compatibility of my script with any but
the most cut-of-the-edge versions of m2crypto. What would you do?

### External OpenSSL call by me

Well, its an ugly workaround based on external system commands just which
seems to me even worse than accessing non-public attributes.


Answer: Some of previous answers produce (apparently?) working PEM public key files,
but so far as I've tried, none of them produce the same output that the
'openssl rsa -pubout -in priv.key' does. This is pretty important to my test
suite, and after poking around in the (0.15.1) PyOpenSSL code, this works well
for both standard PKey objects and the public-key-only PKey objects created by
the x509.get_pubkey() method:

    
    
    from OpenSSL import crypto
    from OpenSSL._util import lib as cryptolib
    
    
    def pem_publickey(pkey):
        """ Format a public key as a PEM """
        bio = crypto._new_mem_buf()
        cryptolib.PEM_write_bio_PUBKEY(bio, pkey._pkey)
        return crypto._bio_to_string(bio)
    
    
    key = crypto.PKey()
    key.generate_key(crypto.TYPE_RSA, 2048)
    print pem_publickey(key)
    

