Here's the code I use to extract a public key from a certificate. I use it for public key pinning, so it uses a SSL*
from the connection.
Essentially, you call len1 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), NULL)
one time to get a length, and then call len2 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), buffer)
a second time to retrieve the certificate. You can use len1
and len2
as a basic sanity check to ensure you got back the expected number of bytes.
int pin_peer_pubkey(SSL* ssl)
{
if(NULL == ssl) return FALSE;
X509* cert = NULL;
/* Scratch */
int len1 = 0, len2 = 0;
unsigned char *buff1 = NULL;
/* Result is returned to caller */
int ret = 0, result = FALSE;
do
{
/* http://www.openssl.org/docs/ssl/SSL_get_peer_certificate.html */
cert = SSL_get_peer_certificate(ssl);
if(!(cert != NULL))
break; /* failed */
/* Begin Gyrations to get the subjectPublicKeyInfo */
/* Thanks to Viktor Dukhovni on the OpenSSL mailing list */
/* http://groups.google.com/group/mailing.openssl.users/browse_thread/thread/d61858dae102c6c7 */
len1 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), NULL);
if(!(len1 > 0))
break; /* failed */
/* scratch */
unsigned char* temp = NULL;
/* http://www.openssl.org/docs/crypto/buffer.html */
buff1 = temp = OPENSSL_malloc(len1);
if(!(buff1 != NULL))
break; /* failed */
/* http://www.openssl.org/docs/crypto/d2i_X509.html */
len2 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &temp);
/* These checks are verifying we got back the same values as when we sized the buffer. */
/* Its pretty weak since they should always be the same. But it gives us something to test. */
if(!((len1 == len2) && (temp != NULL) && ((temp - buff1) == len1)))
break; /* failed */
/* End Gyrations */
/* Do something with the public key */
...
ret = TRUE;
} while(0);
/* http://www.openssl.org/docs/crypto/buffer.html */
if(NULL != buff1)
OPENSSL_free(buff1);
/* http://www.openssl.org/docs/crypto/X509_new.html */
if(NULL != cert)
X509_free(cert);
return result;
}
Sending a Public Key Encoded in Base 64 in Ruby using WSSE in SOAP...
Well, that's a protocol detail you can work out. You can send it as raw bytes, Base64 encode (RFC 4648, Section 4) it or Base64URL encode (RFC 4648, Section 5) it and send it. Its up to you.