diff options
author | Legrandin <helderijs@gmail.com> | 2013-07-01 22:30:45 +0200 |
---|---|---|
committer | Dwayne Litzenberger <dlitz@dlitz.net> | 2013-07-14 21:16:46 -0700 |
commit | 791cfaa255b002a65a57bb29a1f3b8ea23e132b8 (patch) | |
tree | 65749acefd2b1096c83c8a1778ef2994dc563b6c | |
parent | 90d6d3dbcfb02fc441edafe6fafe6e6800009e35 (diff) | |
download | pycrypto-791cfaa255b002a65a57bb29a1f3b8ea23e132b8.tar.gz |
Add support for import/export of DSA keys
This patch adds methods importKey() to DSA module
and exportKey() to _DSAobj object.
Public and private keys can be imported/exported
in a variety of formats:
* DER vs PEM
* PKCS#8 vs OpenSSL vs OpenSSH/OpenSSL
* Encrypted vs clear
-rw-r--r-- | lib/Crypto/PublicKey/DSA.py | 317 | ||||
-rw-r--r-- | lib/Crypto/PublicKey/__init__.py | 3 | ||||
-rw-r--r-- | lib/Crypto/SelfTest/PublicKey/__init__.py | 8 | ||||
-rw-r--r-- | lib/Crypto/SelfTest/PublicKey/test_import_DSA.py | 389 | ||||
-rw-r--r-- | lib/Crypto/SelfTest/PublicKey/test_import_RSA.py (renamed from lib/Crypto/SelfTest/PublicKey/test_importKey.py) | 0 |
5 files changed, 709 insertions, 8 deletions
diff --git a/lib/Crypto/PublicKey/DSA.py b/lib/Crypto/PublicKey/DSA.py index d6bffd6..1818def 100644 --- a/lib/Crypto/PublicKey/DSA.py +++ b/lib/Crypto/PublicKey/DSA.py @@ -59,14 +59,20 @@ verification. >>> from Crypto.Random import random >>> from Crypto.PublicKey import DSA - >>> from Crypto.Hash import SHA + >>> from Crypto.Hash import SHA256 >>> >>> message = "Hello" - >>> key = DSA.generate(1024) - >>> h = SHA.new(message).digest() + >>> key = DSA.generate(2048) + >>> f = open("public_key.pem", "w") + >>> f.write(key.publickey().exportKey(key)) + >>> h = SHA256.new(message).digest() >>> k = random.StrongRandom().randint(1,key.q-1) >>> sig = key.sign(h,k) >>> ... + >>> ... + >>> f = open("public_key.pem", "r") + >>> h = SHA256.new(message).digest() + >>> key = DSA.importKey(f.read()) >>> if key.verify(h,sig): >>> print "OK" >>> else: @@ -79,20 +85,64 @@ verification. __revision__ = "$Id$" -__all__ = ['generate', 'construct', 'error', 'DSAImplementation', '_DSAobj'] +__all__ = ['generate', 'construct', 'error', 'DSAImplementation', + '_DSAobj', 'importKey'] + +import binascii +import struct import sys if sys.version_info[0] == 2 and sys.version_info[1] == 1: from Crypto.Util.py21compat import * +from Crypto.Util.py3compat import * -from Crypto.PublicKey import _DSA, _slowmath, pubkey from Crypto import Random +from Crypto.IO import PKCS8, PEM +from Crypto.Util.number import bytes_to_long, long_to_bytes +from Crypto.PublicKey import _DSA, _slowmath, pubkey, KeyFormatError +from Crypto.Util.asn1 import DerObject, DerSequence,\ + DerInteger, DerObjectId, DerBitString, newDerSequence, newDerBitString try: from Crypto.PublicKey import _fastmath except ImportError: _fastmath = None +def decode_der(obj_class, binstr): + """Instantiate a DER object class, decode a DER binary string in it, + and return the object.""" + der = obj_class() + der.decode(binstr) + return der + +# ; The following ASN.1 types are relevant for DSA +# +# SubjectPublicKeyInfo ::= SEQUENCE { +# algorithm AlgorithmIdentifier, +# subjectPublicKey BIT STRING +# } +# +# id-dsa ID ::= { iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 1 } +# +# ; See RFC3279 +# Dss-Parms ::= SEQUENCE { +# p INTEGER, +# q INTEGER, +# g INTEGER +# } +# +# DSAPublicKey ::= INTEGER +# +# DSSPrivatKey_OpenSSL ::= SEQUENCE +# version INTEGER, +# p INTEGER, +# q INTEGER, +# g INTEGER, +# y INTEGER, +# x INTEGER +# } +# + class _DSAobj(pubkey.pubkey): """Class defining an actual DSA key. @@ -112,9 +162,12 @@ class _DSAobj(pubkey.pubkey): #: - **x**, the private key. keydata = ['y', 'g', 'p', 'q', 'x'] - def __init__(self, implementation, key): + def __init__(self, implementation, key, randfunc=None): self.implementation = implementation self.key = key + if randfunc is None: + randfunc = Random.new().read + self._randfunc = randfunc def __getattr__(self, attrname): if attrname in self.keydata: @@ -217,6 +270,8 @@ class _DSAobj(pubkey.pubkey): def __setstate__(self, d): if not hasattr(self, 'implementation'): self.implementation = DSAImplementation() + if not hasattr(self, '_randfunc'): + self._randfunc = Random.new().read t = [] for k in self.keydata: if not d.has_key(k): @@ -236,6 +291,124 @@ class _DSAobj(pubkey.pubkey): # PY3K: This is meant to be text, do not change to bytes (data) return "<%s @0x%x %s>" % (self.__class__.__name__, id(self), ",".join(attrs)) + def exportKey(self, format='PEM', pkcs8=None, passphrase=None, + protection=None): + """Export this DSA key. + + :Parameters: + format : string + The format to use for wrapping the key: + + - *'DER'*. Binary encoding. + - *'PEM'*. Textual encoding, done according to `RFC1421`_/ + `RFC1423`_ (default). + - *'OpenSSH'*. Textual encoding, one line of text, see `RFC4253`_. + Only suitable for public keys, not private keys. + + passphrase : string + For private keys only. The pass phrase to use for deriving + the encryption key. + + pkcs8 : boolean + For private keys only. If ``True`` (default), the key is arranged + according to `PKCS#8`_ and if `False`, according to the custom + OpenSSL/OpenSSH encoding. + + protection : string + The encryption scheme to use for protecting the private key. + It is only meaningful when a pass phrase is present too. + + If ``pkcs8`` takes value ``True``, ``protection`` is the PKCS#8 + algorithm to use for deriving the secret and encrypting + the private DSA key. + For a complete list of algorithms, see `Crypto.IO.PKCS8`. + The default is *PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC*. + + If ``pkcs8`` is ``False``, the obsolete PEM encryption scheme is + used. It is based on MD5 for key derivation, and Triple DES for + encryption. Parameter ``protection`` is ignored. + + The combination ``format='DER'`` and ``pkcs8=False`` is not allowed + if a passphrase is present. + + :Return: A byte string with the encoded public or private half + of the key. + :Raise ValueError: + When the format is unknown or when you try to encrypt a private + key with *DER* format and OpenSSL/OpenSSH. + :attention: + If you don't provide a pass phrase, the private key will be + exported in the clear! + + .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt + .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt + .. _RFC4253: http://www.ietf.org/rfc/rfc4253.txt + .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt + """ + if passphrase is not None: + passphrase = tobytes(passphrase) + if format == 'OpenSSH': + tup1 = [long_to_bytes(x) for x in (self.p, self.q, self.g, self.y)] + + def func(x): + if (bord(x[0]) & 0x80): + return bchr(0) + x + else: + return x + + tup2 = map(func, tup1) + keyparts = [b('ssh-dss')] + tup2 + keystring = b('').join( + [struct.pack(">I", len(kp)) + kp for kp in keyparts] + ) + return b('ssh-dss ') + binascii.b2a_base64(keystring)[:-1] + + # DER format is always used, even in case of PEM, which simply + # encodes it into BASE64. + params = newDerSequence(self.p, self.q, self.g) + if self.has_private(): + if pkcs8 is None: + pkcs8 = True + if pkcs8: + if not protection: + protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' + private_key = DerInteger(self.x).encode() + binary_key = PKCS8.wrap( + private_key, oid, passphrase, + protection, key_params=params, + randfunc=self._randfunc + ) + if passphrase: + key_type = 'ENCRYPTED PRIVATE' + else: + key_type = 'PRIVATE' + passphrase = None + else: + if format != 'PEM' and passphrase: + raise ValueError("DSA private key cannot be encrypted") + ints = [0, self.p, self.q, self.g, self.y, self.x] + binary_key = newDerSequence(*ints).encode() + key_type = "DSA PRIVATE" + else: + if pkcs8: + raise ValueError("PKCS#8 is only meaningful for private keys") + binary_key = newDerSequence( + newDerSequence(DerObjectId(oid), params), + newDerBitString(DerInteger(self.y)) + ).encode() + key_type = "DSA PUBLIC" + + if format == 'DER': + return binary_key + if format == 'PEM': + pem_str = PEM.encode( + binary_key, key_type + " KEY", + passphrase, self._randfunc + ) + return tobytes(pem_str) + raise ValueError("Unknown key format '%s'. Cannot export the DSA key." % format) + + class DSAImplementation(object): """ A DSA key factory. @@ -243,7 +416,7 @@ class DSAImplementation(object): This class is only internally used to implement the methods of the `Crypto.PublicKey.DSA` module. """ - + def __init__(self, **kwargs): """Create a new DSA key factory. @@ -370,9 +543,139 @@ class DSAImplementation(object): key = self._math.dsa_construct(*tup) return _DSAobj(self, key) + def _importKeyDER(self, key_data, passphrase=None, params=None): + """Import a DSA key (public or private half), encoded in DER form.""" + + try: + # + # Dss-Parms ::= SEQUENCE { + # p OCTET STRING, + # q OCTET STRING, + # g OCTET STRING + # } + # + + # Try a simple private key first + if params: + x = decode_der(DerInteger, key_data).value + params = decode_der(DerSequence, params) # Dss-Parms + p, q, g = list(params) + y = pow(g, x, p) + tup = (y, g, p, q, x) + return self.construct(tup) + + der = decode_der(DerSequence, key_data) + + # Try OpenSSL format for private keys + if len(der) == 6 and der.hasOnlyInts() and der[0] == 0: + tup = [der[comp] for comp in (4, 3, 1, 2, 5)] + return self.construct(tup) + + # Try SubjectPublicKeyInfo + if len(der) == 2: + try: + algo = decode_der(DerSequence, der[0]) + algo_oid = decode_der(DerObjectId, algo[0]).value + params = decode_der(DerSequence, algo[1]) # Dss-Parms + + if algo_oid == oid and len(params) == 3 and\ + params.hasOnlyInts(): + bitmap = decode_der(DerBitString, der[1]) + pub_key = decode_der(DerInteger, bitmap.value) + tup = [pub_key.value] + tup += [params[comp] for comp in (2, 0, 1)] + return self.construct(tup) + except (ValueError, EOFError): + pass + + # Try unencrypted PKCS#8 + p8_pair = PKCS8.unwrap(key_data, passphrase) + if p8_pair[0] == oid: + return self._importKeyDER(p8_pair[1], passphrase, p8_pair[2]) + + except (ValueError, EOFError): + pass + + raise KeyFormatError("DSA key format is not supported") + + def importKey(self, extern_key, passphrase=None): + """Import a DSA key (public or private). + + :Parameters: + extern_key : (byte) string + The DSA key to import. + + An DSA *public* key can be in any of the following formats: + + - X.509 ``subjectPublicKeyInfo`` (binary or PEM) + - OpenSSH (one line of text, see `RFC4253`_) + + A DSA *private* key can be in any of the following formats: + + - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` + DER SEQUENCE (binary or PEM encoding) + - OpenSSL/OpenSSH (binary or PEM) + + For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. + + The private key may be encrypted by means of a certain pass phrase + either at the PEM level or at the PKCS#8 level. + + passphrase : string + In case of an encrypted private key, this is the pass phrase + from which the decryption key is derived. + + :Return: A DSA key object (`_DSAobj`). + :Raise KeyFormatError: + When the given key cannot be parsed (possibly because + the pass phrase is wrong). + + .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt + .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt + .. _RFC4253: http://www.ietf.org/rfc/rfc4253.txt + .. _PKCS#8: http://www.ietf.org/rfc/rfc5208.txt + """ + + extern_key = tobytes(extern_key) + if passphrase is not None: + passphrase = tobytes(passphrase) + + if extern_key.startswith(b('-----')): + # This is probably a PEM encoded key + (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) + if enc_flag: + passphrase = None + return self._importKeyDER(der, passphrase) + + if extern_key.startswith(b('ssh-dss ')): + # This is probably a public OpenSSH key + keystring = binascii.a2b_base64(extern_key.split(b(' '))[1]) + keyparts = [] + while len(keystring) > 4: + length = struct.unpack(">I", keystring[:4])[0] + keyparts.append(keystring[4:4 + length]) + keystring = keystring[4 + length:] + if keyparts[0] == b("ssh-dss"): + tup = [bytes_to_long(keyparts[x]) for x in (4, 3, 1, 2)] + return self.construct(tup) + + if bord(extern_key[0]) == 0x30: + # This is probably a DER encoded key + return self._importKeyDER(extern_key, passphrase) + + raise KeyFormatError("DSA key format is not supported") + +#: `Object ID`_ for a DSA key. +#: +#: id-dsa ID ::= { iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 1 } +#: +#: .. _`Object ID`: http://www.alvestrand.no/objectid/1.2.840.10040.4.1.html +oid = "1.2.840.10040.4.1" + _impl = DSAImplementation() generate = _impl.generate construct = _impl.construct +importKey = _impl.importKey error = _impl.error # vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/lib/Crypto/PublicKey/__init__.py b/lib/Crypto/PublicKey/__init__.py index 503809f..df60c25 100644 --- a/lib/Crypto/PublicKey/__init__.py +++ b/lib/Crypto/PublicKey/__init__.py @@ -36,6 +36,9 @@ Crypto.PublicKey.RSA (Signing, encryption, and blinding) :undocumented: _DSA, _RSA, _fastmath, _slowmath, pubkey """ +class KeyFormatError(ValueError): + pass + __all__ = ['RSA', 'DSA', 'ElGamal'] __revision__ = "$Id$" diff --git a/lib/Crypto/SelfTest/PublicKey/__init__.py b/lib/Crypto/SelfTest/PublicKey/__init__.py index 61ba53f..23a13a4 100644 --- a/lib/Crypto/SelfTest/PublicKey/__init__.py +++ b/lib/Crypto/SelfTest/PublicKey/__init__.py @@ -32,7 +32,13 @@ def get_tests(config={}): tests = [] from Crypto.SelfTest.PublicKey import test_DSA; tests += test_DSA.get_tests(config=config) from Crypto.SelfTest.PublicKey import test_RSA; tests += test_RSA.get_tests(config=config) - from Crypto.SelfTest.PublicKey import test_importKey; tests += test_importKey.get_tests(config=config) + + from Crypto.SelfTest.PublicKey import test_import_DSA + tests +=test_import_DSA.get_tests(config=config) + + from Crypto.SelfTest.PublicKey import test_import_RSA + tests += test_import_RSA.get_tests(config=config) + from Crypto.SelfTest.PublicKey import test_ElGamal; tests += test_ElGamal.get_tests(config=config) return tests diff --git a/lib/Crypto/SelfTest/PublicKey/test_import_DSA.py b/lib/Crypto/SelfTest/PublicKey/test_import_DSA.py new file mode 100644 index 0000000..02f7d91 --- /dev/null +++ b/lib/Crypto/SelfTest/PublicKey/test_import_DSA.py @@ -0,0 +1,389 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/PublicKey/test_import_DSA.py: Self-test for importing DSA keys +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +import unittest + +from Crypto.PublicKey import DSA, KeyFormatError +from Crypto.SelfTest.st_common import * +from Crypto.Util.py3compat import * + +from binascii import unhexlify + +class ImportKeyTests(unittest.TestCase): + + y = 92137165128186062214622779787483327510946462589285775188003362705875131352591574106484271700740858696583623951844732128165434284507709057439633739849986759064015013893156866539696757799934634945787496920169462601722830899660681779448742875054459716726855443681559131362852474817534616736104831095601710736729L + p = 162452170958135306109773853318304545923250830605675936228618290525164105310663722368377131295055868997377338797580997938253236213714988311430600065853662861806894003694743806769284131194035848116051021923956699231855223389086646903420682639786976554552864568460372266462812137447840653688476258666833303658691L + q = 988791743931120302950649732173330531512663554851L + g = 85583152299197514738065570254868711517748965097380456700369348466136657764813442044039878840094809620913085570225318356734366886985903212775602770761953571967834823306046501307810937486758039063386311593890777319935391363872375452381836756832784184928202587843258855704771836753434368484556809100537243908232L + x = 540873410045082450874416847965843801027716145253L + + def setUp(self): + + # It is easier to write test vectors in text form, + # and convert them to byte strigs dynamically here + for mname, mvalue in ImportKeyTests.__dict__.items(): + if mname[:4] in ('der_', 'pem_', 'ssh_'): + if mname[:4] == 'der_': + mvalue = unhexlify(mvalue) + mvalue = tobytes(mvalue) + setattr(self, mname, mvalue) + + # 1. SubjectPublicKeyInfo + der_public=\ + '308201b73082012b06072a8648ce3804013082011e02818100e756ee1717f4b6'+\ + '794c7c214724a19763742c45572b4b3f8ff3b44f3be9f44ce039a2757695ec91'+\ + '5697da74ef914fcd1b05660e2419c761d639f45d2d79b802dbd23e7ab8b81b47'+\ + '9a380e1f30932584ba2a0b955032342ebc83cb5ca906e7b0d7cd6fe656cecb4c'+\ + '8b5a77123a8c6750a481e3b06057aff6aa6eba620b832d60c3021500ad32f48c'+\ + 'd3ae0c45a198a61fa4b5e20320763b2302818079dfdc3d614fe635fceb7eaeae'+\ + '3718dc2efefb45282993ac6749dc83c223d8c1887296316b3b0b54466cf444f3'+\ + '4b82e3554d0b90a778faaf1306f025dae6a3e36c7f93dd5bac4052b92370040a'+\ + 'ca70b8d5820599711900efbc961812c355dd9beffe0981da85c5548074b41c56'+\ + 'ae43fd300d89262e4efd89943f99a651b03888038185000281810083352a69a1'+\ + '32f34843d2a0eb995bff4e2f083a73f0049d2c91ea2f0ce43d144abda48199e4'+\ + 'b003c570a8af83303d45105f606c5c48d925a40ed9c2630c2fa4cdbf838539de'+\ + 'b9a29f919085f2046369f627ca84b2cb1e2c7940564b670f963ab1164d4e2ca2'+\ + 'bf6ffd39f12f548928bf4d2d1b5e6980b4f1be4c92a91986fba559' + + def testImportKey1(self): + key_obj = self.dsa.importKey(self.der_public) + self.failIf(key_obj.has_private()) + self.assertEqual(self.y, key_obj.key.y) + self.assertEqual(self.p, key_obj.key.p) + self.assertEqual(self.q, key_obj.key.q) + self.assertEqual(self.g, key_obj.key.g) + + def testExportKey1(self): + tup = (self.y, self.g, self.p, self.q) + key = self.dsa.construct(tup) + encoded = key.exportKey('DER') + self.assertEqual(self.der_public, encoded) + + # 2. + pem_public="""\ +-----BEGIN DSA PUBLIC KEY----- +MIIBtzCCASsGByqGSM44BAEwggEeAoGBAOdW7hcX9LZ5THwhRyShl2N0LEVXK0s/ +j/O0Tzvp9EzgOaJ1dpXskVaX2nTvkU/NGwVmDiQZx2HWOfRdLXm4AtvSPnq4uBtH +mjgOHzCTJYS6KguVUDI0LryDy1ypBuew181v5lbOy0yLWncSOoxnUKSB47BgV6/2 +qm66YguDLWDDAhUArTL0jNOuDEWhmKYfpLXiAyB2OyMCgYB539w9YU/mNfzrfq6u +NxjcLv77RSgpk6xnSdyDwiPYwYhyljFrOwtURmz0RPNLguNVTQuQp3j6rxMG8CXa +5qPjbH+T3VusQFK5I3AECspwuNWCBZlxGQDvvJYYEsNV3Zvv/gmB2oXFVIB0tBxW +rkP9MA2JJi5O/YmUP5mmUbA4iAOBhQACgYEAgzUqaaEy80hD0qDrmVv/Ti8IOnPw +BJ0skeovDOQ9FEq9pIGZ5LADxXCor4MwPUUQX2BsXEjZJaQO2cJjDC+kzb+DhTne +uaKfkZCF8gRjafYnyoSyyx4seUBWS2cPljqxFk1OLKK/b/058S9UiSi/TS0bXmmA +tPG+TJKpGYb7pVk= +-----END DSA PUBLIC KEY-----""" + + def testImportKey2(self): + for pem in (self.pem_public, tostr(self.pem_public)): + key_obj = self.dsa.importKey(pem) + self.failIf(key_obj.has_private()) + self.assertEqual(self.y, key_obj.key.y) + self.assertEqual(self.p, key_obj.key.p) + self.assertEqual(self.q, key_obj.key.q) + self.assertEqual(self.g, key_obj.key.g) + + def testExportKey2(self): + tup = (self.y, self.g, self.p, self.q) + key = self.dsa.construct(tup) + encoded = key.exportKey('PEM') + self.assertEqual(self.pem_public, encoded) + + # 3. OpenSSL/OpenSSH format + der_private=\ + '308201bb02010002818100e756ee1717f4b6794c7c214724a19763742c45572b'+\ + '4b3f8ff3b44f3be9f44ce039a2757695ec915697da74ef914fcd1b05660e2419'+\ + 'c761d639f45d2d79b802dbd23e7ab8b81b479a380e1f30932584ba2a0b955032'+\ + '342ebc83cb5ca906e7b0d7cd6fe656cecb4c8b5a77123a8c6750a481e3b06057'+\ + 'aff6aa6eba620b832d60c3021500ad32f48cd3ae0c45a198a61fa4b5e2032076'+\ + '3b2302818079dfdc3d614fe635fceb7eaeae3718dc2efefb45282993ac6749dc'+\ + '83c223d8c1887296316b3b0b54466cf444f34b82e3554d0b90a778faaf1306f0'+\ + '25dae6a3e36c7f93dd5bac4052b92370040aca70b8d5820599711900efbc9618'+\ + '12c355dd9beffe0981da85c5548074b41c56ae43fd300d89262e4efd89943f99'+\ + 'a651b038880281810083352a69a132f34843d2a0eb995bff4e2f083a73f0049d'+\ + '2c91ea2f0ce43d144abda48199e4b003c570a8af83303d45105f606c5c48d925'+\ + 'a40ed9c2630c2fa4cdbf838539deb9a29f919085f2046369f627ca84b2cb1e2c'+\ + '7940564b670f963ab1164d4e2ca2bf6ffd39f12f548928bf4d2d1b5e6980b4f1'+\ + 'be4c92a91986fba55902145ebd9a3f0b82069d98420986b314215025756065' + + def testImportKey3(self): + key_obj = self.dsa.importKey(self.der_private) + self.failUnless(key_obj.has_private()) + self.assertEqual(self.y, key_obj.key.y) + self.assertEqual(self.p, key_obj.key.p) + self.assertEqual(self.q, key_obj.key.q) + self.assertEqual(self.g, key_obj.key.g) + self.assertEqual(self.x, key_obj.key.x) + + def testExportKey3(self): + tup = (self.y, self.g, self.p, self.q, self.x) + key = self.dsa.construct(tup) + encoded = key.exportKey('DER', pkcs8=False) + self.assertEqual(self.der_private, encoded) + + # 4. + pem_private="""\ +-----BEGIN DSA PRIVATE KEY----- +MIIBuwIBAAKBgQDnVu4XF/S2eUx8IUckoZdjdCxFVytLP4/ztE876fRM4DmidXaV +7JFWl9p075FPzRsFZg4kGcdh1jn0XS15uALb0j56uLgbR5o4Dh8wkyWEuioLlVAy +NC68g8tcqQbnsNfNb+ZWzstMi1p3EjqMZ1CkgeOwYFev9qpuumILgy1gwwIVAK0y +9IzTrgxFoZimH6S14gMgdjsjAoGAed/cPWFP5jX8636urjcY3C7++0UoKZOsZ0nc +g8Ij2MGIcpYxazsLVEZs9ETzS4LjVU0LkKd4+q8TBvAl2uaj42x/k91brEBSuSNw +BArKcLjVggWZcRkA77yWGBLDVd2b7/4JgdqFxVSAdLQcVq5D/TANiSYuTv2JlD+Z +plGwOIgCgYEAgzUqaaEy80hD0qDrmVv/Ti8IOnPwBJ0skeovDOQ9FEq9pIGZ5LAD +xXCor4MwPUUQX2BsXEjZJaQO2cJjDC+kzb+DhTneuaKfkZCF8gRjafYnyoSyyx4s +eUBWS2cPljqxFk1OLKK/b/058S9UiSi/TS0bXmmAtPG+TJKpGYb7pVkCFF69mj8L +ggadmEIJhrMUIVAldWBl +-----END DSA PRIVATE KEY-----""" + + def testImportKey4(self): + for pem in (self.pem_private, tostr(self.pem_private)): + key_obj = self.dsa.importKey(pem) + self.failUnless(key_obj.has_private()) + self.assertEqual(self.y, key_obj.key.y) + self.assertEqual(self.p, key_obj.key.p) + self.assertEqual(self.q, key_obj.key.q) + self.assertEqual(self.g, key_obj.key.g) + self.assertEqual(self.x, key_obj.key.x) + + def testExportKey4(self): + tup = (self.y, self.g, self.p, self.q, self.x) + key = self.dsa.construct(tup) + encoded = key.exportKey('PEM', pkcs8=False) + self.assertEqual(self.pem_private, encoded) + + # 5. PKCS8 (unencrypted) + der_pkcs8=\ + '3082014a0201003082012b06072a8648ce3804013082011e02818100e756ee17'+\ + '17f4b6794c7c214724a19763742c45572b4b3f8ff3b44f3be9f44ce039a27576'+\ + '95ec915697da74ef914fcd1b05660e2419c761d639f45d2d79b802dbd23e7ab8'+\ + 'b81b479a380e1f30932584ba2a0b955032342ebc83cb5ca906e7b0d7cd6fe656'+\ + 'cecb4c8b5a77123a8c6750a481e3b06057aff6aa6eba620b832d60c3021500ad'+\ + '32f48cd3ae0c45a198a61fa4b5e20320763b2302818079dfdc3d614fe635fceb'+\ + '7eaeae3718dc2efefb45282993ac6749dc83c223d8c1887296316b3b0b54466c'+\ + 'f444f34b82e3554d0b90a778faaf1306f025dae6a3e36c7f93dd5bac4052b923'+\ + '70040aca70b8d5820599711900efbc961812c355dd9beffe0981da85c5548074'+\ + 'b41c56ae43fd300d89262e4efd89943f99a651b03888041602145ebd9a3f0b82'+\ + '069d98420986b314215025756065' + + def testImportKey5(self): + key_obj = self.dsa.importKey(self.der_pkcs8) + self.failUnless(key_obj.has_private()) + self.assertEqual(self.y, key_obj.key.y) + self.assertEqual(self.p, key_obj.key.p) + self.assertEqual(self.q, key_obj.key.q) + self.assertEqual(self.g, key_obj.key.g) + self.assertEqual(self.x, key_obj.key.x) + + def testExportKey5(self): + tup = (self.y, self.g, self.p, self.q, self.x) + key = self.dsa.construct(tup) + encoded = key.exportKey('DER') + self.assertEqual(self.der_pkcs8, encoded) + encoded = key.exportKey('DER', pkcs8=True) + self.assertEqual(self.der_pkcs8, encoded) + + # 6. + pem_pkcs8="""\ +-----BEGIN PRIVATE KEY----- +MIIBSgIBADCCASsGByqGSM44BAEwggEeAoGBAOdW7hcX9LZ5THwhRyShl2N0LEVX +K0s/j/O0Tzvp9EzgOaJ1dpXskVaX2nTvkU/NGwVmDiQZx2HWOfRdLXm4AtvSPnq4 +uBtHmjgOHzCTJYS6KguVUDI0LryDy1ypBuew181v5lbOy0yLWncSOoxnUKSB47Bg +V6/2qm66YguDLWDDAhUArTL0jNOuDEWhmKYfpLXiAyB2OyMCgYB539w9YU/mNfzr +fq6uNxjcLv77RSgpk6xnSdyDwiPYwYhyljFrOwtURmz0RPNLguNVTQuQp3j6rxMG +8CXa5qPjbH+T3VusQFK5I3AECspwuNWCBZlxGQDvvJYYEsNV3Zvv/gmB2oXFVIB0 +tBxWrkP9MA2JJi5O/YmUP5mmUbA4iAQWAhRevZo/C4IGnZhCCYazFCFQJXVgZQ== +-----END PRIVATE KEY-----""" + + def testImportKey6(self): + for pem in (self.pem_pkcs8, tostr(self.pem_pkcs8)): + key_obj = self.dsa.importKey(pem) + self.failUnless(key_obj.has_private()) + self.assertEqual(self.y, key_obj.key.y) + self.assertEqual(self.p, key_obj.key.p) + self.assertEqual(self.q, key_obj.key.q) + self.assertEqual(self.g, key_obj.key.g) + self.assertEqual(self.x, key_obj.key.x) + + def testExportKey6(self): + tup = (self.y, self.g, self.p, self.q, self.x) + key = self.dsa.construct(tup) + encoded = key.exportKey('PEM') + self.assertEqual(self.pem_pkcs8, encoded) + encoded = key.exportKey('PEM', pkcs8=True) + self.assertEqual(self.pem_pkcs8, encoded) + + # 7. OpenSSH/RFC4253 + ssh_pub="""ssh-dss AAAAB3NzaC1kc3MAAACBAOdW7hcX9LZ5THwhRyShl2N0LEVXK0s/j/O0Tzvp9EzgOaJ1dpXskVaX2nTvkU/NGwVmDiQZx2HWOfRdLXm4AtvSPnq4uBtHmjgOHzCTJYS6KguVUDI0LryDy1ypBuew181v5lbOy0yLWncSOoxnUKSB47BgV6/2qm66YguDLWDDAAAAFQCtMvSM064MRaGYph+kteIDIHY7IwAAAIB539w9YU/mNfzrfq6uNxjcLv77RSgpk6xnSdyDwiPYwYhyljFrOwtURmz0RPNLguNVTQuQp3j6rxMG8CXa5qPjbH+T3VusQFK5I3AECspwuNWCBZlxGQDvvJYYEsNV3Zvv/gmB2oXFVIB0tBxWrkP9MA2JJi5O/YmUP5mmUbA4iAAAAIEAgzUqaaEy80hD0qDrmVv/Ti8IOnPwBJ0skeovDOQ9FEq9pIGZ5LADxXCor4MwPUUQX2BsXEjZJaQO2cJjDC+kzb+DhTneuaKfkZCF8gRjafYnyoSyyx4seUBWS2cPljqxFk1OLKK/b/058S9UiSi/TS0bXmmAtPG+TJKpGYb7pVk=""" + + def testImportKey7(self): + for ssh in (self.ssh_pub, tostr(self.ssh_pub)): + key_obj = self.dsa.importKey(ssh) + self.failIf(key_obj.has_private()) + self.assertEqual(self.y, key_obj.key.y) + self.assertEqual(self.p, key_obj.key.p) + self.assertEqual(self.q, key_obj.key.q) + self.assertEqual(self.g, key_obj.key.g) + + def testExportKey7(self): + tup = (self.y, self.g, self.p, self.q) + key = self.dsa.construct(tup) + encoded = key.exportKey('OpenSSH') + self.assertEqual(self.ssh_pub, encoded) + + # 8. Encrypted OpenSSL/OpenSSH + pem_private_encrypted="""\ +-----BEGIN DSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,70B6908939D65E9F2EB999E8729788CE + +4V6GHRDpCrdZ8MBjbyp5AlGUrjvr2Pn2e2zVxy5RBt4FBj9/pa0ae0nnyUPMLSUU +kKyOR0topRYTVRLElm4qVrb5uNZ3hRwfbklr+pSrB7O9eHz9V5sfOQxyODS07JxK +k1OdOs70/ouMXLF9EWfAZOmWUccZKHNblUwg1p1UrZIz5jXw4dUE/zqhvXh6d+iC +ADsICaBCjCrRQJKDp50h3+ndQjkYBKVH+pj8TiQ79U7lAvdp3+iMghQN6YXs9mdI +gFpWw/f97oWM4GHZFqHJ+VSMNFjBiFhAvYV587d7Lk4dhD8sCfbxj42PnfRgUItc +nnPqHxmhMQozBWzYM4mQuo3XbF2WlsNFbOzFVyGhw1Bx1s91qvXBVWJh2ozrW0s6 +HYDV7ZkcTml/4kjA/d+mve6LZ8kuuR1qCiZx6rkffhh1gDN/1Xz3HVvIy/dQ+h9s +5zp7PwUoWbhqp3WCOr156P6gR8qo7OlT6wMh33FSXK/mxikHK136fV2shwTKQVII +rJBvXpj8nACUmi7scKuTWGeUoXa+dwTZVVe+b+L2U1ZM7+h/neTJiXn7u99PFUwu +xVJtxaV37m3aXxtCsPnbBg== +-----END DSA PRIVATE KEY-----""" + + def testImportKey8(self): + for pem in (self.pem_private_encrypted, tostr(self.pem_private_encrypted)): + key_obj = self.dsa.importKey(pem, "PWDTEST") + self.failUnless(key_obj.has_private()) + self.assertEqual(self.y, key_obj.key.y) + self.assertEqual(self.p, key_obj.key.p) + self.assertEqual(self.q, key_obj.key.q) + self.assertEqual(self.g, key_obj.key.g) + self.assertEqual(self.x, key_obj.key.x) + + def testExportKey8(self): + tup = (self.y, self.g, self.p, self.q, self.x) + key = self.dsa.construct(tup) + encoded = key.exportKey('PEM', pkcs8=False, passphrase="PWDTEST") + key = self.dsa.importKey(encoded, "PWDTEST") + self.assertEqual(self.y, key.key.y) + self.assertEqual(self.p, key.key.p) + self.assertEqual(self.q, key.key.q) + self.assertEqual(self.g, key.key.g) + self.assertEqual(self.x, key.key.x) + + # 9. Encrypted PKCS8 + # pbeWithMD5AndDES-CBC + pem_pkcs8_encrypted="""\ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBcTAbBgkqhkiG9w0BBQMwDgQI0GC3BJ/jSw8CAggABIIBUHc1cXZpExIE9tC7 +7ryiW+5ihtF2Ekurq3e408GYSAu5smJjN2bvQXmzRFBz8W38K8eMf1sbWroZ4+zn +kZSbb9nSm5kAa8lR2+oF2k+WRswMR/PTC3f/D9STO2X0QxdrzKgIHEcSGSHp5jTx +aVvbkCDHo9vhBTl6S3ogZ48As/MEro76+9igUwJ1jNhIQZPJ7e20QH5qDpQFFJN4 +CKl2ENSEuwGiqBszItFy4dqH0g63ZGZV/xt9wSO9Rd7SK/EbA/dklOxBa5Y/VItM +gnIhs9XDMoGYyn6F023EicNJm6g/bVQk81BTTma4tm+12TKGdYm+QkeZvCOMZylr +Wv67cKwO3cAXt5C3QXMDgYR64XvuaT5h7C0igMp2afSXJlnbHEbFxQVJlv83T4FM +eZ4k+NQDbEL8GiHmFxzDWQAuPPZKJWEEEV2p/To+WOh+kSDHQw== +-----END ENCRYPTED PRIVATE KEY-----""" + + def testImportKey9(self): + for pem in (self.pem_pkcs8_encrypted, tostr(self.pem_pkcs8_encrypted)): + key_obj = self.dsa.importKey(pem, "PWDTEST") + self.failUnless(key_obj.has_private()) + self.assertEqual(self.y, key_obj.key.y) + self.assertEqual(self.p, key_obj.key.p) + self.assertEqual(self.q, key_obj.key.q) + self.assertEqual(self.g, key_obj.key.g) + self.assertEqual(self.x, key_obj.key.x) + + # 10. Encrypted PKCS8 + # pkcs5PBES2 / + # pkcs5PBKDF2 (rounds=1000, salt=D725BF1B6B8239F4) / + # des-EDE3-CBC (iv=27A1C66C42AFEECE) + # + der_pkcs8_encrypted=\ + '30820196304006092a864886f70d01050d3033301b06092a864886f70d01050c'+\ + '300e0408d725bf1b6b8239f4020203e8301406082a864886f70d0307040827a1'+\ + 'c66c42afeece048201505cacfde7bf8edabb3e0d387950dc872662ea7e9b1ed4'+\ + '400d2e7e6186284b64668d8d0328c33a9d9397e6f03df7cb68268b0a06b4e22f'+\ + '7d132821449ecf998a8b696dbc6dd2b19e66d7eb2edfeb4153c1771d49702395'+\ + '4f36072868b5fcccf93413a5ac4b2eb47d4b3f681c6bd67ae363ed776f45ae47'+\ + '174a00098a7c930a50f820b227ddf50f9742d8e950d02586ff2dac0e3c372248'+\ + 'e5f9b6a7a02f4004f20c87913e0f7b52bccc209b95d478256a890b31d4c9adec'+\ + '21a4d157a179a93a3dad06f94f3ce486b46dfa7fc15fd852dd7680bbb2f17478'+\ + '7e71bd8dbaf81eca7518d76c1d26256e95424864ba45ca5d47d7c5a421be02fa'+\ + 'b94ab01e18593f66cf9094eb5c94b9ecf3aa08b854a195cf87612fbe5e96c426'+\ + '2b0d573e52dc71ba3f5e468c601e816c49b7d32c698b22175e89aaef0c443770'+\ + '5ef2f88a116d99d8e2869a4fd09a771b84b49e4ccb79aadcb1c9' + + def testImportKey10(self): + key_obj = self.dsa.importKey(self.der_pkcs8_encrypted, "PWDTEST") + self.failUnless(key_obj.has_private()) + self.assertEqual(self.y, key_obj.key.y) + self.assertEqual(self.p, key_obj.key.p) + self.assertEqual(self.q, key_obj.key.q) + self.assertEqual(self.g, key_obj.key.g) + self.assertEqual(self.x, key_obj.key.x) + + def testExportKey10(self): + tup = (self.y, self.g, self.p, self.q, self.x) + key = self.dsa.construct(tup) + randfunc = BytesIO(unhexlify(b("27A1C66C42AFEECE") + b("D725BF1B6B8239F4"))).read + key._randfunc = randfunc + encoded = key.exportKey('DER', pkcs8=True, passphrase="PWDTEST") + self.assertEqual(self.der_pkcs8_encrypted, encoded) + + # ---- + + def testImportError1(self): + self.assertRaises(KeyFormatError, self.dsa.importKey, self.der_pkcs8_encrypted, "wrongpwd") + + def testExportError2(self): + tup = (self.y, self.g, self.p, self.q, self.x) + key = self.dsa.construct(tup) + self.assertRaises(ValueError, key.exportKey, 'DER', pkcs8=False, passphrase="PWDTEST") + +class ImportKeyTestsSlow(ImportKeyTests): + def setUp(self): + ImportKeyTests.setUp(self) + self.dsa = DSA.DSAImplementation(use_fast_math=0) + +class ImportKeyTestsFast(ImportKeyTests): + def setUp(self): + ImportKeyTests.setUp(self) + self.dsa = DSA.DSAImplementation(use_fast_math=1) + +if __name__ == '__main__': + unittest.main() + +def get_tests(config={}): + tests = [] + try: + from Crypto.PublicKey import _fastmath + tests += list_test_cases(ImportKeyTestsFast) + except ImportError: + pass + tests += list_test_cases(ImportKeyTestsSlow) + return tests + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + diff --git a/lib/Crypto/SelfTest/PublicKey/test_importKey.py b/lib/Crypto/SelfTest/PublicKey/test_import_RSA.py index ff65e77..ff65e77 100644 --- a/lib/Crypto/SelfTest/PublicKey/test_importKey.py +++ b/lib/Crypto/SelfTest/PublicKey/test_import_RSA.py |