def _importKeyDER(self, externKey): der = DerSequence() der.decode(externKey, True) if len(der)==9 and der.hasOnlyInts() and der[0]==0: # ASN.1 RSAPrivateKey element del der[6:8] # Remove d mod (p-1) and d mod (q-1) del der[0] # Remove version return self.construct(der[:]) if len(der)==2: # ASN.1 SubjectPublicKeyInfo element if der[0]=='\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01\x05\x00': bitmap = DerObject() bitmap.decode(der[1], True) if bitmap.typeTag=='\x03' and bitmap.payload[0]=='\x00': der.decode(bitmap.payload[1:], True) if len(der)==2 and der.hasOnlyInts(): return self.construct(der[:]) raise ValueError("RSA key format is not supported")
def _import_pkcs1_private(encoded, *kwargs): # RSAPrivateKey ::= SEQUENCE { # version Version, # modulus INTEGER, -- n # publicExponent INTEGER, -- e # privateExponent INTEGER, -- d # prime1 INTEGER, -- p # prime2 INTEGER, -- q # exponent1 INTEGER, -- d mod (p-1) # exponent2 INTEGER, -- d mod (q-1) # coefficient INTEGER -- (inverse of q) mod p # } # # Version ::= INTEGER der = DerSequence().decode(encoded, nr_elements=9, only_ints_expected=True) if der[0] != 0: raise ValueError("No PKCS#1 encoding of an RSA private key") return construct(der[1:6] + [Integer(der[4]).inverse(der[5])])
def sign(self, msg_hash): """Compute the DSA/ECDSA signature of a message. Args: msg_hash (hash object): The hash that was carried out over the message. The object belongs to the :mod:`Crypto.Hash` package. Under mode ``'fips-186-3'``, the hash must be a FIPS approved secure hash (SHA-2 or SHA-3). :return: The signature as ``bytes`` :raise ValueError: if the hash algorithm is incompatible to the (EC)DSA key :raise TypeError: if the (EC)DSA key has no private half """ if not self._key.has_private(): raise TypeError("Private key is needed to sign") if not self._valid_hash(msg_hash): raise ValueError("Hash is not sufficiently strong") # Generate the nonce k (critical!) nonce = self._compute_nonce(msg_hash) # Perform signature using the raw API z = Integer.from_bytes(msg_hash.digest()[:self._order_bytes]) sig_pair = self._key._sign(z, nonce) # Encode the signature into a single byte string if self._encoding == 'binary': output = b"".join( [long_to_bytes(x, self._order_bytes) for x in sig_pair]) else: # Dss-sig ::= SEQUENCE { # r INTEGER, # s INTEGER # } # Ecdsa-Sig-Value ::= SEQUENCE { # r INTEGER, # s INTEGER # } output = DerSequence(sig_pair).encode() return output
def process(self, data): key = normalize_rsa_key(data, force_public=self.args.public) out = self.args.output if out is RSAFormat.PEM: yield key.export_key('PEM') return if out is RSAFormat.DER: yield key.export_key('DER') return components = { 'Modulus': key.n, 'Exponent': key.e, } if key.has_private(): decoded = DerSequence() decoded.decode(key.export_key('DER')) it = itertools.islice(decoded, 3, None) for v in ('D', 'P', 'Q', 'DP', 'DQ', 'InverseQ'): try: components[v] = next(it) except StopIteration: break if out is RSAFormat.XKMS: for tag in components: components[tag] = base64.b64encode( number.long_to_bytes(components[tag])).decode('ascii') tags = '\n'.join(F'\t<{tag}>{value}</{tag}>' for tag, value in components.items()) yield F'<RSAKeyPair>\n{tags}\n</RSAKeyPair>'.encode(self.codec) return components['BitSize'] = key.n.bit_length() for tag, value in components.items(): if value.bit_length() > 32: components[tag] = F'{value:X}' if out is RSAFormat.JSON: yield json.dumps(components, indent=4).encode(self.codec) return if out is RSAFormat.TEXT: table = list(flattened(components)) for key, value in table: value = F'0x{value}' if isinstance(value, str) else str(value) value = '\n'.join(F'{L}' for L in textwrap.wrap(value, 80)) yield F'-- {key+" ":-<77}\n{value!s}'.encode(self.codec)
def sign(self, msg_hash): """Produce the DSS signature of a message. :Parameters: msg_hash : hash object The hash that was carried out over the message. The object belongs to the `Crypto.Hash` package. Under mode *'fips-186-3'*, the hash must be a FIPS approved secure hash (SHA-1 or a member of the SHA-2 family), of cryptographic strength appropriate for the DSA key. For instance, a 3072/256 DSA key can only be used in combination with SHA-512. :Return: The signature encoded as a byte string. :Raise ValueError: If the hash algorithm is incompatible to the DSA key. :Raise TypeError: If the DSA key has no private half. """ if not self._valid_hash(msg_hash): raise ValueError("Hash is not sufficiently strong") # Generate the nonce k (critical!) nonce = self._compute_nonce(msg_hash) # Perform signature using the raw API z = Integer.from_bytes(msg_hash.digest()[:self._order_bytes]) sig_pair = self._key._sign(z, nonce) # Encode the signature into a single byte string if self._encoding == 'binary': output = b("").join([long_to_bytes(x, self._order_bytes) for x in sig_pair]) else: # Dss-sig ::= SEQUENCE { # r OCTET STRING, # s OCTET STRING # } output = DerSequence(sig_pair).encode() return output
def privKeyXML(pemPrivateKeyFile): with open (pemPrivateKeyFile, 'rb') as pkFile: pemPrivKey = pkFile.read() print(type(pemPrivKey)) pemPrivKey = pemPrivKey.decode("utf-8") lines = pemPrivKey.replace(" ", '').split() # print('555555555') print(len(lines)) keyDer = DerSequence() keyDer.decode(a2b_base64(''.join(lines[1:-1]))) # print(type(standard_b64encode(number.long_to_bytes(keyDer[1])))) xml = '<RSAKeyValue>' xml += '<Modulus>' xml += standard_b64encode(number.long_to_bytes(keyDer[1])).decode("utf-8") xml += '</Modulus>' xml += '<Exponent>' xml += standard_b64encode(number.long_to_bytes(keyDer[2])).decode("utf-8") xml += '</Exponent>' xml += '<D>' xml += standard_b64encode(number.long_to_bytes(keyDer[3])).decode("utf-8") xml += '</D>' xml += '<P>' xml += standard_b64encode(number.long_to_bytes(keyDer[4])).decode("utf-8") xml += '</P>' xml += '<Q>' xml += standard_b64encode(number.long_to_bytes(keyDer[5])).decode("utf-8") xml += '</Q>' xml += '<DP>' xml += standard_b64encode(number.long_to_bytes(keyDer[6])).decode("utf-8") xml += '</DP>' xml += '<DQ>' xml += standard_b64encode(number.long_to_bytes(keyDer[7])).decode("utf-8") xml += '</DQ>' xml += '<InverseQ>' xml += standard_b64encode(number.long_to_bytes(keyDer[8])).decode("utf-8") xml += '</InverseQ>' xml += '</RSAKeyValue>' fileName = basename(pemPrivateKeyFile) with open (fileName+'.xml', 'w') as pkFile: pkFile.write(xml) return
def _importKeyDER(self, externKey): """Import an RSA key (public or private half), encoded in DER form.""" try: 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: # 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[0] # Remove version return self.construct(der[:]) # Keep on trying PKCS#1, but now for a public key if len(der)==2: # 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 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, IndexError: pass
def _import_private_der(encoded, passphrase, curve_name=None): # ECPrivateKey ::= SEQUENCE { # version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), # privateKey OCTET STRING, # parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, # publicKey [1] BIT STRING OPTIONAL # } private_key = DerSequence().decode(encoded, nr_elements=(3, 4)) if private_key[0] != 1: raise ValueError("Incorrect ECC private key version") try: curve_name = DerObjectId(explicit=0).decode(private_key[2]).value except ValueError: pass if curve_name != _curve.oid: raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_name) scalar_bytes = DerOctetString().decode(private_key[1]).payload order_bytes = _curve.order.size_in_bytes() if len(scalar_bytes) != order_bytes: raise ValueError("Private key is too small") d = Integer.from_bytes(scalar_bytes) # Decode public key (if any, it must be P-256) if len(private_key) == 4: public_key_enc = DerBitString(explicit=1).decode(private_key[3]).value public_key = _import_public_der(curve_name, public_key_enc) point_x = public_key.pointQ.x point_y = public_key.pointQ.y else: point_x = point_y = None return construct(curve="P-256", d=d, point_x=point_x, point_y=point_y)
def sign_bytes_dsa(byte_array, path_to_private_key_pem_file): # Use this method for DSA keys key = open(path_to_private_key_pem_file, 'r').read() # Import the key dsa_key = DSA.importKey(key) # Create a digest of nonce + cnonce # This only seems to work with SHA1 (SHA256 gives us a 401 error) buf = buffer(byte_array) digest = SHA1.new(buf).digest() # Digitally sign the digest with our private key # The corresponding public key is in our admin handle on the server k = random.StrongRandom().randint(1, dsa_key.q - 1) sign = dsa_key.sign(digest, k) # Signature bytes from a DSA key need to be DER-encoded # This signature is in two parts (r and s) seq = DerSequence() seq.append(sign[0]) seq.append(sign[1]) return seq.encode()
def testDecode4(self): # One very long integer der = DerSequence() der.decode(b('0\x82\x01\x05')+ b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) self.assertEquals(len(der),1) self.assertEquals(der[0],2**2048)
def extractCerts (pem): # pem may be multiple certs, so we remove the # 'begin cert' and 'end cert' comments, and then # group the cert lines together, one big line for each cert lines = pem.replace(" ",'').split() encodedCerts = [] for line in lines: if '-' in line: # Skip this. Append a new blank cert line encodedCerts.append('') else: encodedCerts[len(encodedCerts)-1] = encodedCerts[len(encodedCerts)-1] + line # remove all blank cert strings certs = [] for encodedCert in encodedCerts: if len(encodedCert) > 0: der = a2b_base64(encodedCert) cert = DerSequence() cert.decode(der) certs.append(cert) return certs
def testEncode4(self): # One very long integer der = DerSequence() der.append(2**2048) self.assertEquals(der.encode(), '0\x82\x01\x05' '\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00' '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' '\x00\x00\x00\x00\x00\x00\x00\x00\x00')
def exportKey(self, format='PEM', passphrase=None, pkcs=1, protection=None, randfunc=None): """Export this RSA key. Args: format (string): The format to use for wrapping the key: - *'PEM'*. (*Default*) Text encoding, done according to `RFC1421`_/`RFC1423`_. - *'DER'*. Binary encoding. - *'OpenSSH'*. Textual encoding, done according to OpenSSH specification. Only suitable for public keys (not private keys). passphrase (string): (*For private keys only*) The pass phrase used for protecting the output. pkcs (integer): (*For private keys only*) The ASN.1 structure to use for serializing the key. Note that even in case of PEM encoding, there is an inner ASN.1 DER structure. With ``pkcs=1`` (*default*), the private key is encoded in a simple `PKCS#1`_ structure (``RSAPrivateKey``). With ``pkcs=8``, the private key is encoded in a `PKCS#8`_ structure (``PrivateKeyInfo``). .. note:: This parameter is ignored for a public key. For DER and PEM, an ASN.1 DER ``SubjectPublicKeyInfo`` structure is always used. protection (string): (*For private keys only*) The encryption scheme to use for protecting the private key. If ``None`` (default), the behavior depends on :attr:`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 :func:`Crypto.Protocol.KDF.PBKDF2` with 8 bytes salt, and 1 000 iterations of :mod:`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 :attr:`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 :mod:`Crypto.IO.PKCS8` module (see :attr:`wrap_algo` parameter). randfunc (callable): A function that provides random bytes. Only used for PEM encoding. The default is :func:`Crypto.Random.get_random_bytes`. Returns: byte string: the encoded key Raises: ValueError:when the format is unknown or when you try to encrypt a private key with *DER* format and PKCS#1. .. warning:: 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 .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ if passphrase is not None: passphrase = tobytes(passphrase) if randfunc is None: randfunc = Random.get_random_bytes if format == 'OpenSSH': e_bytes, n_bytes = [x.to_bytes() for x in (self._e, self._n)] if bord(e_bytes[0]) & 0x80: e_bytes = bchr(0) + e_bytes if bord(n_bytes[0]) & 0x80: n_bytes = bchr(0) + n_bytes keyparts = [b('ssh-rsa'), e_bytes, n_bytes] keystring = b('').join( [struct.pack(">I", len(kp)) + kp for kp in keyparts]) return b('ssh-rsa ') + binascii.b2a_base64(keystring)[:-1] # DER format is always used, even in case of PEM, which simply # encodes it into BASE64. if self.has_private(): binary_key = DerSequence([ 0, self.n, self.e, self.d, self.p, self.q, self.d % (self.p - 1), self.d % (self.q - 1), Integer(self.q).inverse(self.p) ]).encode() if pkcs == 1: key_type = 'RSA PRIVATE KEY' if format == 'DER' and passphrase: raise ValueError("PKCS#1 private key cannot be encrypted") else: # PKCS#8 if format == 'PEM' and protection is None: key_type = 'PRIVATE KEY' binary_key = PKCS8.wrap(binary_key, oid, None) else: key_type = 'ENCRYPTED PRIVATE KEY' if not protection: protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' binary_key = PKCS8.wrap(binary_key, oid, passphrase, protection) passphrase = None else: key_type = "PUBLIC KEY" binary_key = _create_subject_public_key_info( oid, DerSequence([self.n, self.e])) if format == 'DER': return binary_key if format == 'PEM': pem_str = PEM.encode(binary_key, key_type, passphrase, randfunc) return tobytes(pem_str) raise ValueError( "Unknown key format '%s'. Cannot export the RSA key." % format)
def testErrDecode2(self): # Wrong payload type der = DerSequence() self.assertRaises(ValueError, der.decode, '\x30\x00\x00', True)
def testDecode5(self): # One single-byte integer (looks negative) der = DerSequence() der.decode('0\x04\x02\x02\x00\xff') self.assertEquals(len(der),1) self.assertEquals(der[0],0xFFL)
def testDecode2(self): # One single-byte integer (non-zero) der = DerSequence() der.decode('0\x03\x02\x01\x7f') self.assertEquals(len(der),1) self.assertEquals(der[0],127)
def exportKey(self, format='PEM', passphrase=None, pkcs=1): """Export this RSA key. :Parameter format: The format to use for wrapping the key. - *'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 :Parameter passphrase: In case of PEM, the pass phrase to derive the encryption key from. :Type passphrase: string :Parameter pkcs: The PKCS standard to follow for assembling the key. You have two choices: - 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. PKCS standards are not relevant for the *OpenSSH* format. :Type pkcs: integer :Return: A byte string with the encoded public or private half. :Raise ValueError: When the format is unknown. .. _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 """ if passphrase is not None: passphrase = tobytes(passphrase) if format=='OpenSSH': eb = long_to_bytes(self.e) 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] # DER format is always used, even in case of PEM, which simply # encodes it into BASE64. der = DerSequence() if self.has_private(): 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 = "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 der.encode() if format=='PEM': 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)
def _importKeyDER(key_data, passphrase, params): """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 = DerInteger().decode(key_data).value p, q, g = list(DerSequence().decode(params)) # Dss-Parms tup = (pow(g, x, p), g, p, q, x) return construct(tup) der = DerSequence().decode(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 construct(tup) # Try SubjectPublicKeyInfo if len(der) == 2: try: algo = DerSequence().decode(der[0]) algo_oid = DerObjectId().decode(algo[0]).value params = DerSequence().decode(algo[1]) # Dss-Parms if algo_oid == oid and len(params) == 3 and\ params.hasOnlyInts(): bitmap = DerBitString().decode(der[1]) pub_key = DerInteger().decode(bitmap.value) tup = [pub_key.value] tup += [params[comp] for comp in (2, 0, 1)] return construct(tup) except (ValueError, EOFError): pass # Try to see if this is an X.509 DER certificate # (Certificate ASN.1 type) if len(der) == 3: from Crypto.PublicKey import _extract_sp_info try: sp_info = _extract_sp_info(der) return _importKeyDER(sp_info, passphrase, None) except ValueError: pass # Try unencrypted PKCS#8 p8_pair = PKCS8.unwrap(key_data, passphrase) if p8_pair[0] == oid: return _importKeyDER(p8_pair[1], passphrase, p8_pair[2]) except (ValueError, EOFError): pass raise ValueError("DSA key format is not supported")
if bord(externKey[0])==0x30: # This is probably a DER encoded key return self._importKeyDER(externKey) raise ValueError("RSA key format is not supported") #: 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( [ b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01'), DerNull().encode() ] ).encode() _impl = RSAImplementation() #: #: Randomly generate a fresh, new RSA key object. #: #: See `RSAImplementation.generate`. #: generate = _impl.generate #: #: Construct an RSA key object from a tuple of valid RSA components. #: #: See `RSAImplementation.construct`. #: construct = _impl.construct
def wrap(private_key, key_oid, passphrase=None, protection=None, prot_params=None, key_params=None, randfunc=None): """Wrap a private key into a PKCS#8 blob (clear or encrypted). :Parameters: private_key : byte string The private key encoded in binary form. The actual encoding is algorithm specific. In most cases, it is DER. key_oid : string The object identifier (OID) of the private key to wrap. It is a dotted string, like "``1.2.840.113549.1.1.1``" (for RSA keys). passphrase : (binary) string The secret passphrase from which the wrapping key is derived. Set it only if encryption is required. protection : string The identifier of the algorithm to use for securely wrapping the key. The default value is '``PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC``'. prot_params : dictionary Parameters for the protection algorithm. +------------------+-----------------------------------------------+ | Key | Description | +==================+===============================================+ | iteration_count | The KDF algorithm is repeated several times to| | | slow down brute force attacks on passwords | | | (called *N* or CPU/memory cost in scrypt). | | | | | | The default value for PBKDF2 is 1 000. | | | The default value for scrypt is 16 384. | +------------------+-----------------------------------------------+ | salt_size | Salt is used to thwart dictionary and rainbow | | | attacks on passwords. The default value is 8 | | | bytes. | +------------------+-----------------------------------------------+ | block_size | *(scrypt only)* Memory-cost (r). The default | | | value is 8. | +------------------+-----------------------------------------------+ | parallelization | *(scrypt only)* CPU-cost (p). The default | | | value is 1. | +------------------+-----------------------------------------------+ key_params : DER object The algorithm parameters associated to the private key. It is required for algorithms like DSA, but not for others like RSA. randfunc : callable Random number generation function; it should accept a single integer N and return a string of random data, N bytes long. If not specified, a new RNG will be instantiated from ``Crypto.Random``. :Return: The PKCS#8-wrapped private key (possibly encrypted), as a binary string. """ if key_params is None: key_params = DerNull() # # PrivateKeyInfo ::= SEQUENCE { # version Version, # privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, # privateKey PrivateKey, # attributes [0] IMPLICIT Attributes OPTIONAL # } # pk_info = DerSequence([ 0, DerSequence([DerObjectId(key_oid), key_params]), DerOctetString(private_key) ]) pk_info_der = pk_info.encode() if passphrase is None: return pk_info_der if not passphrase: raise ValueError("Empty passphrase") # Encryption with PBES2 passphrase = tobytes(passphrase) if protection is None: protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' return PBES2.encrypt(pk_info_der, passphrase, protection, prot_params, randfunc)
def testEncode3(self): # One multi-byte integer (non-zero) der = DerSequence() der.append(0x180L) self.assertEquals(der.encode(), '0\x04\x02\x02\x01\x80')
def testInit1(self): der = DerSequence([1, DerInteger(2), b('0\x00')]) self.assertEquals(der.encode(), b('0\x08\x02\x01\x01\x02\x01\x020\x00'))
def testEncode5(self): # One single-byte integer (looks negative) der = DerSequence() der.append(0xFFL) self.assertEquals(der.encode(), '0\x04\x02\x02\x00\xff')
def testEncode5(self): der = DerSequence() der += 1 der += b('\x30\x00') self.assertEquals(der.encode(), b('\x30\x05\x02\x01\x01\x30\x00'))
def testDecode3(self): # One multi-byte integer (non-zero) der = DerSequence() der.decode('0\x04\x02\x02\x01\x80') self.assertEquals(len(der),1) self.assertEquals(der[0],0x180L)
def testDecode9(self): # Verify that decode returns itself der = DerSequence() self.assertEqual(der, der.decode(b('0\x06\x24\x02\xb6\x63\x12\x00')))
def testErrDecode1(self): # Not a sequence der = DerSequence() self.assertRaises(ValueError, der.decode, '') self.assertRaises(ValueError, der.decode, '\x00') self.assertRaises(ValueError, der.decode, '\x30')
def testErrDecode2(self): der = DerSequence() # Too much data self.assertRaises(ValueError, der.decode, b('\x30\x00\x00'))
def testErrDecode3(self): # Wrong length format der = DerSequence() self.assertRaises(ValueError, der.decode, '\x30\x04\x02\x01\x01\x00') self.assertRaises(ValueError, der.decode, '\x30\x81\x03\x02\x01\x01') self.assertRaises(ValueError, der.decode, '\x30\x04\x02\x81\x01\x01')
def exportKey(self, format='PEM', pkcs8=None, passphrase=None, protection=None, randfunc=None): """Export this DSA key. Args: format (string): The encoding for the output: - *'PEM'* (default). ASCII as per `RFC1421`_/ `RFC1423`_. - *'DER'*. Binary ASN.1 encoding. - *'OpenSSH'*. ASCII one-liner as per `RFC4253`_. Only suitable for public keys, not for private keys. passphrase (string): *Private keys only*. The pass phrase to protect the output. pkcs8 (boolean): *Private keys only*. If ``True`` (default), the key is encoded with `PKCS#8`_. If ``False``, it is encoded in the custom OpenSSL/OpenSSH container. protection (string): *Only in combination with a pass phrase*. The encryption scheme to use to protect the output. If :data:`pkcs8` takes value ``True``, this is the PKCS#8 algorithm to use for deriving the secret and encrypting the private DSA key. For a complete list of algorithms, see :mod:`Crypto.IO.PKCS8`. The default is *PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC*. If :data:`pkcs8` is ``False``, the obsolete PEM encryption scheme is used. It is based on MD5 for key derivation, and Triple DES for encryption. Parameter :data:`protection` is then ignored. The combination ``format='DER'`` and ``pkcs8=False`` is not allowed if a passphrase is present. randfunc (callable): A function that returns random bytes. By default it is :func:`Crypto.Random.get_random_bytes`. Returns: byte string : the encoded key Raises: ValueError : when the format is unknown or when you try to encrypt a private key with *DER* format and OpenSSL/OpenSSH. .. warning:: 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 randfunc is None: randfunc = Random.get_random_bytes if format == 'OpenSSH': tup1 = [self._key[x].to_bytes() for x in ('p', 'q', 'g', 'y')] def func(x): if (bord(x[0]) & 0x80): return bchr(0) + x else: return x tup2 = list(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 = DerSequence([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=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 = DerSequence(ints).encode() key_type = "DSA PRIVATE" else: if pkcs8: raise ValueError("PKCS#8 is only meaningful for private keys") binary_key = _create_subject_public_key_info( oid, DerInteger(self.y), params) key_type = "PUBLIC" if format == 'DER': return binary_key if format == 'PEM': pem_str = PEM.encode(binary_key, key_type + " KEY", passphrase, randfunc) return tobytes(pem_str) raise ValueError( "Unknown key format '%s'. Cannot export the DSA key." % format)