updated pycrypto
This commit is contained in:
@@ -59,20 +59,14 @@ verification.
|
||||
|
||||
>>> from Crypto.Random import random
|
||||
>>> from Crypto.PublicKey import DSA
|
||||
>>> from Crypto.Hash import SHA256
|
||||
>>> from Crypto.Hash import SHA
|
||||
>>>
|
||||
>>> message = "Hello"
|
||||
>>> key = DSA.generate(2048)
|
||||
>>> f = open("public_key.pem", "w")
|
||||
>>> f.write(key.publickey().exportKey(key))
|
||||
>>> h = SHA256.new(message).digest()
|
||||
>>> key = DSA.generate(1024)
|
||||
>>> h = SHA.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:
|
||||
@@ -85,64 +79,20 @@ verification.
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
__all__ = ['generate', 'construct', 'error', 'DSAImplementation',
|
||||
'_DSAobj', 'importKey']
|
||||
|
||||
import binascii
|
||||
import struct
|
||||
__all__ = ['generate', 'construct', 'error', 'DSAImplementation', '_DSAobj']
|
||||
|
||||
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.
|
||||
|
||||
@@ -162,12 +112,9 @@ class _DSAobj(pubkey.pubkey):
|
||||
#: - **x**, the private key.
|
||||
keydata = ['y', 'g', 'p', 'q', 'x']
|
||||
|
||||
def __init__(self, implementation, key, randfunc=None):
|
||||
def __init__(self, implementation, key):
|
||||
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:
|
||||
@@ -270,8 +217,6 @@ 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):
|
||||
@@ -291,124 +236,6 @@ 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.
|
||||
@@ -416,7 +243,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.
|
||||
|
||||
@@ -543,139 +370,9 @@ 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:
|
||||
|
@@ -111,7 +111,6 @@ __all__ = ['generate', 'construct', 'error', 'ElGamalobj']
|
||||
|
||||
from Crypto.PublicKey.pubkey import *
|
||||
from Crypto.Util import number
|
||||
from Crypto import Random
|
||||
|
||||
class error (Exception):
|
||||
pass
|
||||
@@ -243,11 +242,6 @@ class ElGamalobj(pubkey):
|
||||
#: - **x**, the private key.
|
||||
keydata=['p', 'g', 'y', 'x']
|
||||
|
||||
def __init__(self, randfunc=None):
|
||||
if randfunc is None:
|
||||
randfunc = Random.new().read
|
||||
self._randfunc = randfunc
|
||||
|
||||
def encrypt(self, plaintext, K):
|
||||
"""Encrypt a piece of data with ElGamal.
|
||||
|
||||
@@ -337,11 +331,8 @@ class ElGamalobj(pubkey):
|
||||
def _decrypt(self, M):
|
||||
if (not hasattr(self, 'x')):
|
||||
raise TypeError('Private key not available in this object')
|
||||
r = number.getRandomRange(2, self.p-1, self._randfunc)
|
||||
a_blind = (M[0] * pow(self.g, r, self.p)) % self.p
|
||||
ax=pow(a_blind, self.x, self.p)
|
||||
plaintext_blind = (M[1] * inverse(ax, self.p ) ) % self.p
|
||||
plaintext = (plaintext_blind * pow(self.y, r, self.p)) % self.p
|
||||
ax=pow(M[0], self.x, self.p)
|
||||
plaintext=(M[1] * inverse(ax, self.p ) ) % self.p
|
||||
return plaintext
|
||||
|
||||
def _sign(self, M, K):
|
||||
|
@@ -46,7 +46,7 @@ them from known components, exporting them, and importing them.
|
||||
>>>
|
||||
>>> key = RSA.generate(2048)
|
||||
>>> f = open('mykey.pem','w')
|
||||
>>> f.write(key.exportKey('PEM'))
|
||||
>>> f.write(RSA.exportKey('PEM'))
|
||||
>>> f.close()
|
||||
...
|
||||
>>> f = open('mykey.pem','r')
|
||||
@@ -65,39 +65,31 @@ it is recommended to use one of the standardized schemes instead (like
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
__all__ = ['generate', 'construct', 'error', 'importKey', 'RSAImplementation',
|
||||
'_RSAobj', 'oid' , 'algorithmIdentifier' ]
|
||||
__all__ = ['generate', 'construct', 'error', 'importKey', 'RSAImplementation', '_RSAobj']
|
||||
|
||||
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.Util.python_compat import *
|
||||
from Crypto.Util.number import getRandomRange, bytes_to_long, long_to_bytes
|
||||
|
||||
from Crypto.PublicKey import _RSA, _slowmath, pubkey
|
||||
from Crypto.IO import PKCS8, PEM
|
||||
from Crypto import Random
|
||||
|
||||
from Crypto.Util.asn1 import *
|
||||
|
||||
from Crypto.Util.asn1 import DerObject, DerSequence, DerNull
|
||||
import binascii
|
||||
import struct
|
||||
|
||||
from Crypto.Util.number import inverse
|
||||
|
||||
from Crypto.Util.number import inverse
|
||||
|
||||
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
|
||||
|
||||
class _RSAobj(pubkey.pubkey):
|
||||
"""Class defining an actual RSA key.
|
||||
|
||||
@@ -294,8 +286,6 @@ class _RSAobj(pubkey.pubkey):
|
||||
def __setstate__(self, d):
|
||||
if not hasattr(self, 'implementation'):
|
||||
self.implementation = RSAImplementation()
|
||||
if not hasattr(self, '_randfunc'):
|
||||
self._randfunc = Random.new().read
|
||||
t = []
|
||||
for k in self.keydata:
|
||||
if not d.has_key(k):
|
||||
@@ -315,66 +305,36 @@ class _RSAobj(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', passphrase=None, pkcs=1, protection=None):
|
||||
def exportKey(self, format='PEM', passphrase=None, pkcs=1):
|
||||
"""Export this RSA key.
|
||||
|
||||
:Parameters:
|
||||
format : string
|
||||
The format to use for wrapping the key:
|
||||
:Parameter format: The format to use for wrapping the key.
|
||||
|
||||
- *'DER'*. Binary encoding.
|
||||
- *'DER'*. Binary encoding, always unencrypted.
|
||||
- *'PEM'*. Textual encoding, done according to `RFC1421`_/`RFC1423`_.
|
||||
Unencrypted (default) or encrypted.
|
||||
- *'OpenSSH'*. Textual encoding, done according to OpenSSH specification.
|
||||
Only suitable for public keys (not private keys).
|
||||
:Type format: string
|
||||
|
||||
passphrase : string
|
||||
For private keys only. The pass phrase used for deriving the encryption
|
||||
key.
|
||||
:Parameter passphrase: In case of PEM, the pass phrase to derive the encryption key from.
|
||||
:Type passphrase: string
|
||||
|
||||
pkcs : integer
|
||||
For *DER* and *PEM* format only.
|
||||
The PKCS standard to follow for assembling the components of the key.
|
||||
You have two choices:
|
||||
:Parameter pkcs: The PKCS standard to follow for assembling the key.
|
||||
You have two choices:
|
||||
|
||||
- **1** (default): the public key is embedded into
|
||||
an X.509 ``SubjectPublicKeyInfo`` DER SEQUENCE.
|
||||
The private key is embedded into a `PKCS#1`_
|
||||
``RSAPrivateKey`` DER SEQUENCE.
|
||||
- **8**: the private key is embedded into a `PKCS#8`_
|
||||
``PrivateKeyInfo`` DER SEQUENCE. This value cannot be used
|
||||
for public keys.
|
||||
- with **1**, the public key is embedded into an X.509 `SubjectPublicKeyInfo` DER SEQUENCE.
|
||||
The private key is embedded into a `PKCS#1`_ `RSAPrivateKey` DER SEQUENCE.
|
||||
This mode is the default.
|
||||
- with **8**, the private key is embedded into a `PKCS#8`_ `PrivateKeyInfo` DER SEQUENCE.
|
||||
This mode is not available for public keys.
|
||||
|
||||
protection : string
|
||||
The encryption scheme to use for protecting the private key.
|
||||
PKCS standards are not relevant for the *OpenSSH* format.
|
||||
:Type pkcs: integer
|
||||
|
||||
If ``None`` (default), the behavior depends on ``format``:
|
||||
|
||||
- For *DER*, the *PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC*
|
||||
scheme is used. The following operations are performed:
|
||||
|
||||
1. A 16 byte Triple DES key is derived from the passphrase
|
||||
using `Crypto.Protocol.KDF.PBKDF2` with 8 bytes salt,
|
||||
and 1 000 iterations of `Crypto.Hash.HMAC`.
|
||||
2. The private key is encrypted using CBC.
|
||||
3. The encrypted key is encoded according to PKCS#8.
|
||||
|
||||
- For *PEM*, the obsolete PEM encryption scheme is used.
|
||||
It is based on MD5 for key derivation, and Triple DES for encryption.
|
||||
|
||||
Specifying a value for ``protection`` is only meaningful for PKCS#8
|
||||
(that is, ``pkcs=8``) and only if a pass phrase is present too.
|
||||
|
||||
The supported schemes for PKCS#8 are listed in the
|
||||
`Crypto.IO.PKCS8` module (see ``wrap_algo`` parameter).
|
||||
|
||||
:Return: A byte string with the encoded public or private half
|
||||
of the key.
|
||||
:Return: A byte string with the encoded public or private half.
|
||||
:Raise ValueError:
|
||||
When the format is unknown or when you try to encrypt a private
|
||||
key with *DER* format and PKCS#1.
|
||||
:attention:
|
||||
If you don't provide a pass phrase, the private key will be
|
||||
exported in the clear!
|
||||
When the format is unknown.
|
||||
|
||||
.. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt
|
||||
.. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt
|
||||
@@ -388,52 +348,59 @@ class _RSAobj(pubkey.pubkey):
|
||||
nb = long_to_bytes(self.n)
|
||||
if bord(eb[0]) & 0x80: eb=bchr(0x00)+eb
|
||||
if bord(nb[0]) & 0x80: nb=bchr(0x00)+nb
|
||||
keyparts = [ b('ssh-rsa'), eb, nb ]
|
||||
keystring = b('').join([ struct.pack(">I",len(kp))+kp for kp in keyparts])
|
||||
return b('ssh-rsa ')+binascii.b2a_base64(keystring)[:-1]
|
||||
keyparts = [ 'ssh-rsa', eb, nb ]
|
||||
keystring = ''.join([ struct.pack(">I",len(kp))+kp for kp in keyparts])
|
||||
return 'ssh-rsa '+binascii.b2a_base64(keystring)[:-1]
|
||||
|
||||
# DER format is always used, even in case of PEM, which simply
|
||||
# encodes it into BASE64.
|
||||
der = DerSequence()
|
||||
if self.has_private():
|
||||
binary_key = newDerSequence(
|
||||
0,
|
||||
self.n,
|
||||
self.e,
|
||||
self.d,
|
||||
self.p,
|
||||
self.q,
|
||||
self.d % (self.p-1),
|
||||
self.d % (self.q-1),
|
||||
inverse(self.q, self.p)
|
||||
).encode()
|
||||
if pkcs==1:
|
||||
keyType = 'RSA PRIVATE'
|
||||
if format=='DER' and passphrase:
|
||||
raise ValueError("PKCS#1 private key cannot be encrypted")
|
||||
else: # PKCS#8
|
||||
if format=='PEM' and protection is None:
|
||||
keyType = 'PRIVATE'
|
||||
binary_key = PKCS8.wrap(binary_key, oid, None)
|
||||
else:
|
||||
keyType = 'ENCRYPTED PRIVATE'
|
||||
if not protection:
|
||||
protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC'
|
||||
binary_key = PKCS8.wrap(binary_key, oid, passphrase, protection)
|
||||
passphrase = None
|
||||
keyType= { 1: 'RSA PRIVATE', 8: 'PRIVATE' }[pkcs]
|
||||
der[:] = [ 0, self.n, self.e, self.d, self.p, self.q,
|
||||
self.d % (self.p-1), self.d % (self.q-1),
|
||||
inverse(self.q, self.p) ]
|
||||
if pkcs==8:
|
||||
derkey = der.encode()
|
||||
der = DerSequence([0])
|
||||
der.append(algorithmIdentifier)
|
||||
der.append(DerObject('OCTET STRING', derkey).encode())
|
||||
else:
|
||||
keyType = "RSA PUBLIC"
|
||||
binary_key = newDerSequence(
|
||||
algorithmIdentifier,
|
||||
newDerBitString(
|
||||
newDerSequence( self.n, self.e )
|
||||
)
|
||||
).encode()
|
||||
keyType = "PUBLIC"
|
||||
der.append(algorithmIdentifier)
|
||||
bitmap = DerObject('BIT STRING')
|
||||
derPK = DerSequence( [ self.n, self.e ] )
|
||||
bitmap.payload = bchr(0x00) + derPK.encode()
|
||||
der.append(bitmap.encode())
|
||||
if format=='DER':
|
||||
return binary_key
|
||||
return der.encode()
|
||||
if format=='PEM':
|
||||
pem_str = PEM.encode(binary_key, keyType+" KEY", passphrase, self._randfunc)
|
||||
return tobytes(pem_str)
|
||||
raise ValueError("Unknown key format '%s'. Cannot export the RSA key." % format)
|
||||
pem = b("-----BEGIN " + keyType + " KEY-----\n")
|
||||
objenc = None
|
||||
if passphrase and keyType.endswith('PRIVATE'):
|
||||
# We only support 3DES for encryption
|
||||
import Crypto.Hash.MD5
|
||||
from Crypto.Cipher import DES3
|
||||
from Crypto.Protocol.KDF import PBKDF1
|
||||
salt = self._randfunc(8)
|
||||
key = PBKDF1(passphrase, salt, 16, 1, Crypto.Hash.MD5)
|
||||
key += PBKDF1(key+passphrase, salt, 8, 1, Crypto.Hash.MD5)
|
||||
objenc = DES3.new(key, Crypto.Cipher.DES3.MODE_CBC, salt)
|
||||
pem += b('Proc-Type: 4,ENCRYPTED\n')
|
||||
pem += b('DEK-Info: DES-EDE3-CBC,') + binascii.b2a_hex(salt).upper() + b('\n\n')
|
||||
|
||||
binaryKey = der.encode()
|
||||
if objenc:
|
||||
# Add PKCS#7-like padding
|
||||
padding = objenc.block_size-len(binaryKey)%objenc.block_size
|
||||
binaryKey = objenc.encrypt(binaryKey+bchr(padding)*padding)
|
||||
|
||||
# Each BASE64 line can take up to 64 characters (=48 bytes of data)
|
||||
chunks = [ binascii.b2a_base64(binaryKey[i:i+48]) for i in range(0, len(binaryKey), 48) ]
|
||||
pem += b('').join(chunks)
|
||||
pem += b("-----END " + keyType + " KEY-----")
|
||||
return pem
|
||||
return ValueError("Unknown key format '%s'. Cannot export the RSA key." % format)
|
||||
|
||||
class RSAImplementation(object):
|
||||
"""
|
||||
@@ -572,139 +539,159 @@ class RSAImplementation(object):
|
||||
key = self._math.rsa_construct(*tup)
|
||||
return _RSAobj(self, key)
|
||||
|
||||
def _importKeyDER(self, extern_key, passphrase=None):
|
||||
def _importKeyDER(self, externKey):
|
||||
"""Import an RSA key (public or private half), encoded in DER form."""
|
||||
|
||||
try:
|
||||
|
||||
der = decode_der(DerSequence, extern_key)
|
||||
der = DerSequence()
|
||||
der.decode(externKey, True)
|
||||
|
||||
# Try PKCS#1 first, for a private key
|
||||
if len(der) == 9 and der.hasOnlyInts() and der[0] == 0:
|
||||
if len(der)==9 and der.hasOnlyInts() and der[0]==0:
|
||||
# ASN.1 RSAPrivateKey element
|
||||
del der[6:] # Remove d mod (p-1),
|
||||
# d mod (q-1), and
|
||||
# q^{-1} mod p
|
||||
der.append(inverse(der[4], der[5])) # Add p^{-1} mod q
|
||||
del der[6:] # Remove d mod (p-1), d mod (q-1), and q^{-1} mod p
|
||||
der.append(inverse(der[4],der[5])) # Add p^{-1} mod q
|
||||
del der[0] # Remove version
|
||||
return self.construct(der[:])
|
||||
|
||||
# Keep on trying PKCS#1, but now for a public key
|
||||
if len(der) == 2:
|
||||
try:
|
||||
# The DER object is an RSAPublicKey SEQUENCE with
|
||||
# two elements
|
||||
if der.hasOnlyInts():
|
||||
return self.construct(der[:])
|
||||
# The DER object is a SubjectPublicKeyInfo SEQUENCE
|
||||
# with two elements: an 'algorithmIdentifier' and a
|
||||
# 'subjectPublicKey'BIT STRING.
|
||||
# 'algorithmIdentifier' takes the value given at the
|
||||
# module level.
|
||||
# 'subjectPublicKey' encapsulates the actual ASN.1
|
||||
# RSAPublicKey element.
|
||||
if der[0] == algorithmIdentifier:
|
||||
bitmap = decode_der(DerBitString, der[1])
|
||||
rsaPub = decode_der(DerSequence, bitmap.value)
|
||||
if len(rsaPub) == 2 and rsaPub.hasOnlyInts():
|
||||
return self.construct(rsaPub[:])
|
||||
except (ValueError, EOFError):
|
||||
pass
|
||||
if len(der)==2:
|
||||
# The DER object is an RSAPublicKey SEQUENCE with two elements
|
||||
if der.hasOnlyInts():
|
||||
return self.construct(der[:])
|
||||
# The DER object is a SubjectPublicKeyInfo SEQUENCE with two elements:
|
||||
# an 'algorithm' (or 'algorithmIdentifier') SEQUENCE and a 'subjectPublicKey' BIT STRING.
|
||||
# 'algorithm' takes the value given a few lines above.
|
||||
# 'subjectPublicKey' encapsulates the actual ASN.1 RSAPublicKey element.
|
||||
if der[0]==algorithmIdentifier:
|
||||
bitmap = DerObject()
|
||||
bitmap.decode(der[1], True)
|
||||
if bitmap.isType('BIT STRING') and bord(bitmap.payload[0])==0x00:
|
||||
der.decode(bitmap.payload[1:], True)
|
||||
if len(der)==2 and der.hasOnlyInts():
|
||||
return self.construct(der[:])
|
||||
|
||||
# Try PKCS#8 (possibly encrypted)
|
||||
k = PKCS8.unwrap(extern_key, passphrase)
|
||||
if k[0] == oid:
|
||||
return self._importKeyDER(k[1], passphrase)
|
||||
# Try unencrypted PKCS#8
|
||||
if der[0]==0:
|
||||
# The second element in the SEQUENCE is algorithmIdentifier.
|
||||
# It must say RSA (see above for description).
|
||||
if der[1]==algorithmIdentifier:
|
||||
privateKey = DerObject()
|
||||
privateKey.decode(der[2], True)
|
||||
if privateKey.isType('OCTET STRING'):
|
||||
return self._importKeyDER(privateKey.payload)
|
||||
|
||||
except (ValueError, EOFError):
|
||||
except ValueError, IndexError:
|
||||
pass
|
||||
|
||||
raise ValueError("RSA key format is not supported")
|
||||
|
||||
def importKey(self, extern_key, passphrase=None):
|
||||
"""Import an RSA key (public or private half), encoded in standard
|
||||
form.
|
||||
def importKey(self, externKey, passphrase=None):
|
||||
"""Import an RSA key (public or private half), encoded in standard form.
|
||||
|
||||
:Parameter extern_key:
|
||||
:Parameter externKey:
|
||||
The RSA key to import, encoded as a string.
|
||||
|
||||
An RSA public key can be in any of the following formats:
|
||||
|
||||
- X.509 ``subjectPublicKeyInfo`` DER SEQUENCE (binary or PEM
|
||||
encoding)
|
||||
- `PKCS#1`_ ``RSAPublicKey`` DER SEQUENCE (binary or PEM encoding)
|
||||
- X.509 `subjectPublicKeyInfo` DER SEQUENCE (binary or PEM encoding)
|
||||
- `PKCS#1`_ `RSAPublicKey` DER SEQUENCE (binary or PEM encoding)
|
||||
- OpenSSH (textual public key only)
|
||||
|
||||
An RSA private key can be in any of the following formats:
|
||||
|
||||
- PKCS#1 ``RSAPrivateKey`` DER SEQUENCE (binary or PEM encoding)
|
||||
- `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo``
|
||||
DER SEQUENCE (binary or PEM encoding)
|
||||
- PKCS#1 `RSAPrivateKey` DER SEQUENCE (binary or PEM encoding)
|
||||
- `PKCS#8`_ `PrivateKeyInfo` DER SEQUENCE (binary or PEM encoding)
|
||||
- OpenSSH (textual public key only)
|
||||
|
||||
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.
|
||||
:Type extern_key: string
|
||||
|
||||
In case of PEM encoding, the private key can be encrypted with DES or 3TDES according to a certain ``pass phrase``.
|
||||
Only OpenSSL-compatible pass phrases are supported.
|
||||
:Type externKey: string
|
||||
|
||||
:Parameter passphrase:
|
||||
In case of an encrypted private key, this is the pass phrase from
|
||||
which the decryption key is derived.
|
||||
In case of an encrypted PEM key, this is the pass phrase from which the encryption key is derived.
|
||||
:Type passphrase: string
|
||||
|
||||
|
||||
:Return: An RSA key object (`_RSAobj`).
|
||||
|
||||
:Raise ValueError/IndexError/TypeError:
|
||||
When the given key cannot be parsed (possibly because the pass
|
||||
phrase is wrong).
|
||||
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
|
||||
.. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt
|
||||
.. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt
|
||||
"""
|
||||
extern_key = tobytes(extern_key)
|
||||
externKey = tobytes(externKey)
|
||||
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 externKey.startswith(b('-----')):
|
||||
# This is probably a PEM encoded key
|
||||
lines = externKey.replace(b(" "),b('')).split()
|
||||
keyobj = None
|
||||
|
||||
if extern_key.startswith(b('ssh-rsa ')):
|
||||
# The encrypted PEM format
|
||||
if lines[1].startswith(b('Proc-Type:4,ENCRYPTED')):
|
||||
DEK = lines[2].split(b(':'))
|
||||
if len(DEK)!=2 or DEK[0]!=b('DEK-Info') or not passphrase:
|
||||
raise ValueError("PEM encryption format not supported.")
|
||||
algo, salt = DEK[1].split(b(','))
|
||||
salt = binascii.a2b_hex(salt)
|
||||
import Crypto.Hash.MD5
|
||||
from Crypto.Cipher import DES, DES3
|
||||
from Crypto.Protocol.KDF import PBKDF1
|
||||
if algo==b("DES-CBC"):
|
||||
# This is EVP_BytesToKey in OpenSSL
|
||||
key = PBKDF1(passphrase, salt, 8, 1, Crypto.Hash.MD5)
|
||||
keyobj = DES.new(key, Crypto.Cipher.DES.MODE_CBC, salt)
|
||||
elif algo==b("DES-EDE3-CBC"):
|
||||
# Note that EVP_BytesToKey is note exactly the same as PBKDF1
|
||||
key = PBKDF1(passphrase, salt, 16, 1, Crypto.Hash.MD5)
|
||||
key += PBKDF1(key+passphrase, salt, 8, 1, Crypto.Hash.MD5)
|
||||
keyobj = DES3.new(key, Crypto.Cipher.DES3.MODE_CBC, salt)
|
||||
else:
|
||||
raise ValueError("Unsupport PEM encryption algorithm.")
|
||||
lines = lines[2:]
|
||||
|
||||
der = binascii.a2b_base64(b('').join(lines[1:-1]))
|
||||
if keyobj:
|
||||
der = keyobj.decrypt(der)
|
||||
padding = bord(der[-1])
|
||||
der = der[:-padding]
|
||||
return self._importKeyDER(der)
|
||||
|
||||
if externKey.startswith(b('ssh-rsa ')):
|
||||
# This is probably an OpenSSH key
|
||||
keystring = binascii.a2b_base64(extern_key.split(b(' '))[1])
|
||||
keystring = binascii.a2b_base64(externKey.split(b(' '))[1])
|
||||
keyparts = []
|
||||
while len(keystring) > 4:
|
||||
l = struct.unpack(">I", keystring[:4])[0]
|
||||
keyparts.append(keystring[4:4 + l])
|
||||
keystring = keystring[4 + l:]
|
||||
while len(keystring)>4:
|
||||
l = struct.unpack(">I",keystring[:4])[0]
|
||||
keyparts.append(keystring[4:4+l])
|
||||
keystring = keystring[4+l:]
|
||||
e = bytes_to_long(keyparts[1])
|
||||
n = bytes_to_long(keyparts[2])
|
||||
return self.construct([n, e])
|
||||
|
||||
if bord(extern_key[0]) == 0x30:
|
||||
if bord(externKey[0])==0x30:
|
||||
# This is probably a DER encoded key
|
||||
return self._importKeyDER(extern_key, passphrase)
|
||||
|
||||
return self._importKeyDER(externKey)
|
||||
|
||||
raise ValueError("RSA key format is not supported")
|
||||
|
||||
#: `Object ID`_ for the RSA encryption algorithm. This OID often indicates
|
||||
#: a generic RSA key, even when such key will be actually used for digital
|
||||
#: signatures.
|
||||
#:
|
||||
#: .. _`Object ID`: http://www.alvestrand.no/objectid/1.2.840.113549.1.1.1.html
|
||||
oid = "1.2.840.113549.1.1.1"
|
||||
|
||||
#: This is the standard DER object that qualifies a cryptographic algorithm
|
||||
#: in ASN.1-based data structures (e.g. X.509 certificates).
|
||||
#: This is the ASN.1 DER object that qualifies an algorithm as
|
||||
#: compliant to PKCS#1 (that is, the standard RSA).
|
||||
# It is found in all 'algorithm' fields (also called 'algorithmIdentifier').
|
||||
# It is a SEQUENCE with the oid assigned to RSA and with its parameters (none).
|
||||
# 0x06 0x09 OBJECT IDENTIFIER, 9 bytes of payload
|
||||
# 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x01 0x01
|
||||
# rsaEncryption (1 2 840 113549 1 1 1) (PKCS #1)
|
||||
# 0x05 0x00 NULL
|
||||
algorithmIdentifier = DerSequence(
|
||||
[DerObjectId(oid).encode(), # algorithm field
|
||||
DerNull().encode()] # parameters field
|
||||
[ b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01'),
|
||||
DerNull().encode() ]
|
||||
).encode()
|
||||
|
||||
_impl = RSAImplementation()
|
||||
|
@@ -30,7 +30,7 @@ __revision__ = "$Id$"
|
||||
from Crypto.PublicKey.pubkey import *
|
||||
from Crypto.Util import number
|
||||
from Crypto.Util.number import bytes_to_long, long_to_bytes
|
||||
from Crypto.Hash import SHA1
|
||||
from Crypto.Hash import SHA
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
class error (Exception):
|
||||
@@ -38,8 +38,8 @@ class error (Exception):
|
||||
|
||||
def generateQ(randfunc):
|
||||
S=randfunc(20)
|
||||
hash1=SHA1.new(S).digest()
|
||||
hash2=SHA1.new(long_to_bytes(bytes_to_long(S)+1)).digest()
|
||||
hash1=SHA.new(S).digest()
|
||||
hash2=SHA.new(long_to_bytes(bytes_to_long(S)+1)).digest()
|
||||
q = bignum(0)
|
||||
for i in range(0,20):
|
||||
c=bord(hash1[i])^bord(hash2[i])
|
||||
@@ -77,7 +77,7 @@ def generate_py(bits, randfunc, progress_func=None):
|
||||
powL1=pow(bignum(2), bits-1)
|
||||
while C<4096:
|
||||
for k in range(0, n+1):
|
||||
V[k]=bytes_to_long(SHA1.new(S+bstr(N)+bstr(k)).digest())
|
||||
V[k]=bytes_to_long(SHA.new(S+bstr(N)+bstr(k)).digest())
|
||||
W=V[n] % powb
|
||||
for k in range(n-1, -1, -1):
|
||||
W=(W<<160L)+V[k]
|
||||
|
@@ -36,9 +36,6 @@ Crypto.PublicKey.RSA (Signing, encryption, and blinding)
|
||||
:undocumented: _DSA, _RSA, _fastmath, _slowmath, pubkey
|
||||
"""
|
||||
|
||||
class KeyFormatError(ValueError):
|
||||
pass
|
||||
|
||||
__all__ = ['RSA', 'DSA', 'ElGamal']
|
||||
__revision__ = "$Id$"
|
||||
|
||||
|
Reference in New Issue
Block a user