diff options
author | Sybren A. Stüvel <sybren@stuvel.eu> | 2012-10-11 17:09:13 +0200 |
---|---|---|
committer | Sybren A. Stüvel <sybren@stuvel.eu> | 2012-10-11 17:09:13 +0200 |
commit | 9884505885e342de141d1f4a3cc0be8dcd0c54b3 (patch) | |
tree | 01afc10572b127d89f1873a02fcbb81cd3efa23b | |
parent | 2daadbbcc3857af75713bed74b327485a6636778 (diff) | |
download | rsa-git-9884505885e342de141d1f4a3cc0be8dcd0c54b3.tar.gz |
Added support for loading public keys from OpenSSL
-rw-r--r-- | .pydevproject | 23 | ||||
-rw-r--r-- | rsa/asn1.py | 35 | ||||
-rw-r--r-- | rsa/key.py | 67 |
3 files changed, 97 insertions, 28 deletions
diff --git a/.pydevproject b/.pydevproject index 6d260cc..af1e4ee 100644 --- a/.pydevproject +++ b/.pydevproject @@ -1,10 +1,13 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<?eclipse-pydev version="1.0"?> - -<pydev_project> -<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property> -<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property> -<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH"> -<path>/python-rsa</path> -</pydev_pathproperty> -</pydev_project> +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?eclipse-pydev version="1.0"?>
+
+<pydev_project>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
+<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
+<path>/python-rsa</path>
+</pydev_pathproperty>
+<pydev_pathproperty name="org.python.pydev.PROJECT_EXTERNAL_SOURCE_PATH">
+<path>C:\cygwin\home\Sybren\python-rsa-venv-py27\Lib\site-packages\pyasn1-0.1.3-py2.7.egg</path>
+</pydev_pathproperty>
+</pydev_project>
diff --git a/rsa/asn1.py b/rsa/asn1.py new file mode 100644 index 0000000..706e6cf --- /dev/null +++ b/rsa/asn1.py @@ -0,0 +1,35 @@ +'''ASN.1 definitions. + +Not all ASN.1-handling code use these definitions, but when it does, they should be here. +''' + +from pyasn1.type import univ, namedtype, tag + +class PubKeyHeader(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('oid', univ.ObjectIdentifier()), + namedtype.NamedType('parameters', univ.Null()), + ) + +class OpenSSLPubKey(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('header', PubKeyHeader()), + + # This little hack (the implicit tag) allows us to get a Bit String as Octet String + namedtype.NamedType('key', univ.OctetString().subtype( + implicitTag=tag.Tag(tagClass=0, tagFormat=0, tagId=3))), + ) + + +class AsnPubKey(univ.Sequence): + '''ASN.1 contents of DER encoded public key: + + RSAPublicKey ::= SEQUENCE { + modulus INTEGER, -- n + publicExponent INTEGER, -- e + ''' + + componentType = namedtype.NamedTypes( + namedtype.NamedType('modulus', univ.Integer()), + namedtype.NamedType('publicExponent', univ.Integer()), + ) @@ -26,7 +26,7 @@ of pyasn1. ''' import logging -from rsa._compat import b +from rsa._compat import b, bytes_type import rsa.prime import rsa.pem @@ -34,6 +34,8 @@ import rsa.common log = logging.getLogger(__name__) + + class AbstractKey(object): '''Abstract superclass for private and public keys.''' @@ -153,16 +155,10 @@ class PublicKey(AbstractKey): ''' from pyasn1.codec.der import decoder - (priv, _) = decoder.decode(keyfile) - - # ASN.1 contents of DER encoded public key: - # - # RSAPublicKey ::= SEQUENCE { - # modulus INTEGER, -- n - # publicExponent INTEGER, -- e - - as_ints = tuple(int(x) for x in priv) - return cls(*as_ints) + from rsa.asn1 import AsnPubKey + + (priv, _) = decoder.decode(keyfile, asn1Spec=AsnPubKey()) + return cls(n=priv['modulus'], e=priv['publicExponent']) def _save_pkcs1_der(self): '''Saves the public key in PKCS#1 DER format. @@ -170,14 +166,8 @@ class PublicKey(AbstractKey): @returns: the DER-encoded public key. ''' - from pyasn1.type import univ, namedtype from pyasn1.codec.der import encoder - - class AsnPubKey(univ.Sequence): - componentType = namedtype.NamedTypes( - namedtype.NamedType('modulus', univ.Integer()), - namedtype.NamedType('publicExponent', univ.Integer()), - ) + from rsa.asn1 import AsnPubKey # Create the ASN object asn_key = AsnPubKey() @@ -210,6 +200,47 @@ class PublicKey(AbstractKey): der = self._save_pkcs1_der() return rsa.pem.save_pem(der, 'RSA PUBLIC KEY') + @classmethod + def load_pkcs1_openssl_pem(cls, keyfile): + '''Loads a PKCS#1.5 PEM-encoded public key file from OpenSSL. + + These files can be recognised in that they start with BEGIN PUBLIC KEY + rather than BEGIN RSA PUBLIC KEY. + + The contents of the file before the "-----BEGIN PUBLIC KEY-----" and + after the "-----END PUBLIC KEY-----" lines is ignored. + + @param keyfile: contents of a PEM-encoded file that contains the public + key, from OpenSSL. + @return: a PublicKey object + ''' + + der = rsa.pem.load_pem(keyfile, 'PUBLIC KEY') + return cls.load_pkcs1_openssl_der(der) + + @classmethod + def load_pkcs1_openssl_der(cls, keyfile): + '''Loads a PKCS#1 DER-encoded public key file from OpenSSL. + + @param keyfile: contents of a DER-encoded file that contains the public + key, from OpenSSL. + @return: a PublicKey object + ''' + + from rsa.asn1 import OpenSSLPubKey + from pyasn1.codec.der import decoder + from pyasn1.type import univ + + (keyinfo, _) = decoder.decode(keyfile, asn1Spec=OpenSSLPubKey()) + + if keyinfo['header']['oid'] != univ.ObjectIdentifier('1.2.840.113549.1.1.1'): + raise TypeError("This is not a DER-encoded OpenSSL-compatible public key") + + return cls._load_pkcs1_der(keyinfo['key'][1:]) + + + + class PrivateKey(AbstractKey): '''Represents a private RSA key. |