4

As the M2Crypto library is not available for Python 3, I'm looking for a way to read in an X509 certificate, extract the public key from it and use it for RSA encryption.

I currently have the following two functions:

from ssl import PEM_cert_to_DER_cert  # standard library
from Crypto.Util import asn1  # http://pycrypto.org
from OpenSSL.crypto import *  # https://pythonhosted.org/pyOpenSSL/

def extract_publickey_1(certstr):
    """ from http://stackoverflow.com/questions/12911373 """
    der = PEM_cert_to_DER_cert(certstr)
    cert = asn1.DerSequence()
    cert.decode(der)
    tbs = asn1.DerSequence()
    tbs.decode(cert[0])
    return tbs[6]

def extract_publickey_2(certstr):
    return dump_privatekey(FILETYPE_ASN1, 
                           load_certificate(FILETYPE_PEM, certstr).get_pubkey())

The first function raises an IndexError for some certificates, specially the ones that were not generated from command-line OpenSSL but rather some cryptographic library (python and c# libs were tested.) It works for command-line OpenSSL generated certificates.

I've examined the output of the second function and it wasn't identical to the first one, but the last 266 bytes of the output is equivalent:

extract_publickey_1(certstr)[-266:] == extract_publickey_2(certstr)[-266:]

returns True.

My question is, what's going on here? Is there a solution to this?

marczellm
  • 1,224
  • 2
  • 18
  • 42
  • It may be easier to investigate your issue if you post one of those certificates that produces error and one that does not; perhaps with smallest key size possible. – Anton Samsonov May 06 '14 at 17:39
  • @AntonSamsonov [This certificate](http://users.itk.ppke.hu/~marma/downloads/marma.crt) works. [This one](http://users.itk.ppke.hu/~marma/downloads/marma2.crt) does not. – marczellm May 06 '14 at 21:31

1 Answers1

4

First of all, you must understand that, because an X.509 certificate is encoded in ASN.1 format, it is represented as a deeply and arbitrary nested collection of various values — and that nesting style and value data-types may be arbitrary even in DER mode. Thus it's very naive to expect a field at a fixed location and in a fixed form, i. e. without further nesting and with a strict data-type (especially when it comes to text strings); not to mention that using “magic constants” is always a bad idea. That's why you should use specialized functions like get_pubkey() whenever possible rather than trying to parse a complex document yourself like you did with asn1 class.

Second, you must understand that, because the X.509 specification is algorithm-agnostic, there are no specific fields for “RSA modulus”, “RSA public exponent” and so on. Instead, there is only one generic “Public key” field which has a nested set of subfields — specifying algorithm OID and its numerical attributes. For example, RSA public keys have 2 attributes: the modulus n and the encryption exponent e; additionally, your extract_publickey_2() function prepends an attribute that is always zero, and I have no idea what does it stand for. Chances are high that your RSA implementation expects numeric arguments rather than byte arrays or a complex ASN.1 value, and that's why you will probably need to extract them with asn1.DerSequence.decode() or a more RSA-specific function.

Anton Samsonov
  • 1,380
  • 17
  • 34