updated pycrypto

This commit is contained in:
shim_
2018-05-10 16:56:32 +02:00
parent fb89f1946b
commit 26579a25f1
92 changed files with 2518 additions and 5288 deletions

View File

@@ -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()