summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLegrandin <helderijs@gmail.com>2013-07-01 22:30:45 +0200
committerDwayne Litzenberger <dlitz@dlitz.net>2013-07-14 21:16:46 -0700
commit791cfaa255b002a65a57bb29a1f3b8ea23e132b8 (patch)
tree65749acefd2b1096c83c8a1778ef2994dc563b6c
parent90d6d3dbcfb02fc441edafe6fafe6e6800009e35 (diff)
downloadpycrypto-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.py317
-rw-r--r--lib/Crypto/PublicKey/__init__.py3
-rw-r--r--lib/Crypto/SelfTest/PublicKey/__init__.py8
-rw-r--r--lib/Crypto/SelfTest/PublicKey/test_import_DSA.py389
-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