def load_encrypted_keydata(self, keydata): """Load a key from encrypted keydata (in PEM format).""" lines = keydata.strip().replace(' ', '').splitlines() if not lines[1].startswith('Proc-Type:4,ENCRYPTED'): raise TypeError('Unsupported encryption') dek = lines[2].split(':') if len(dek) != 2 or dek[0] != 'DEK-Info': raise ValueError('PEM encryption method not supported') algo, salt = dek[1].split(',') salt = binascii.unhexlify(salt) if algo == 'DES-CBC': key = PBKDF1(self.passphrase, salt, 8, 1, MD5) obj = DES.new(key, DES.MODE_CBC, salt) elif algo == 'DES-EDE3-CBC': key = PBKDF1(self.passphrase, salt, 16, 1, MD5) key += PBKDF1(key + self.passphrase, salt, 8, 1, MD5) obj = DES3.new(key, DES3.MODE_CBC, salt) elif algo == 'AES-128-CBC': key = PBKDF1(self.passphrase, salt[:8], 16, 1, MD5) obj = AES.new(key, AES.MODE_CBC, salt) else: raise TypeError('%s: cipher not supported' % (algo, )) lines = lines[3:-1] data = base64.b64decode(''.join(lines)) return unpad(obj.decrypt(data), obj.block_size)
def encode(data, marker, passphrase=None, randfunc=None): """Encode a piece of binary data into PEM format. Args: data (byte string): The piece of binary data to encode. marker (string): The marker for the PEM block (e.g. "PUBLIC KEY"). Note that there is no official master list for all allowed markers. Still, you can refer to the OpenSSL_ source code. passphrase (byte string): If given, the PEM block will be encrypted. The key is derived from the passphrase. randfunc (callable): Random number generation function; it accepts an integer N and returns a byte string of random data, N bytes long. If not given, a new one is instantiated. Returns: The PEM block, as a string. .. _OpenSSL: https://github.com/openssl/openssl/blob/master/include/openssl/pem.h """ if randfunc is None: randfunc = get_random_bytes out = "-----BEGIN %s-----\n" % marker if passphrase: # We only support 3DES for encryption salt = randfunc(8) key = PBKDF1(passphrase, salt, 16, 1, MD5) key += PBKDF1(key + passphrase, salt, 8, 1, MD5) objenc = DES3.new(key, DES3.MODE_CBC, salt) out += "Proc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,%s\n\n" %\ tostr(hexlify(salt).upper()) # Encrypt with PKCS#7 padding data = objenc.encrypt(pad(data, objenc.block_size)) elif passphrase is not None: raise ValueError("Empty password") # Each BASE64 line can take up to 64 characters (=48 bytes of data) # b2a_base64 adds a new line character! chunks = [ tostr(b2a_base64(data[i:i + 48])) for i in range(0, len(data), 48) ] out += "".join(chunks) out += "-----END %s-----" % marker return out
def decrypt(data, passphrase): encrypted_private_key_info = decode_der(DerSequence, data) encrypted_algorithm = decode_der(DerSequence, encrypted_private_key_info[0]) encrypted_data = decode_der(DerOctetString, encrypted_private_key_info[1]).payload pbe_oid = decode_der(DerObjectId, encrypted_algorithm[0]).value cipher_params = {} if pbe_oid == '1.2.840.113549.1.5.3': hashmod = MD5 ciphermod = DES elif pbe_oid == '1.2.840.113549.1.5.6': hashmod = MD5 ciphermod = ARC2 cipher_params['effective_keylen'] = 64 elif pbe_oid == '1.2.840.113549.1.5.10': hashmod = SHA1 ciphermod = DES elif pbe_oid == '1.2.840.113549.1.5.11': hashmod = SHA1 ciphermod = ARC2 cipher_params['effective_keylen'] = 64 else: raise ValueError('Unknown OID') pbe_params = decode_der(DerSequence, encrypted_algorithm[1]) salt = decode_der(DerOctetString, pbe_params[0]).payload iterations = pbe_params[1] key_iv = PBKDF1(passphrase, salt, 16, iterations, hashmod) key, iv = key_iv[:8], key_iv[8:] cipher = ciphermod.new(key, ciphermod.MODE_CBC, iv, **cipher_params) pt = cipher.decrypt(encrypted_data) return unpad(pt, cipher.block_size)
def process(self, data): from Crypto.Protocol.KDF import PBKDF1 return multidecode( data, lambda pwd: (PBKDF1(pwd, self.args.salt, dkLen=self.args.size, count=self.args.iter, hashAlgo=self.hash)))
def encode(data, marker, passphrase=None, randfunc=None): if randfunc is None: randfunc = get_random_bytes out = '-----BEGIN %s-----\n' % marker if passphrase: salt = randfunc(8) key = PBKDF1(passphrase, salt, 16, 1, MD5) key += PBKDF1(key + passphrase, salt, 8, 1, MD5) objenc = DES3.new(key, DES3.MODE_CBC, salt) out += 'Proc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,%s\n\n' % tostr( hexlify(salt).upper()) data = objenc.encrypt(pad(data, objenc.block_size)) chunks = [ tostr(b2a_base64(data[i:i + 48])) for i in range(0, len(data), 48) ] out += ''.join(chunks) out += '-----END %s-----' % marker return out
def decrypt(data, passphrase): """Decrypt a piece of data using a passphrase and *PBES1*. The algorithm to use is automatically detected. :Parameters: data : byte string The piece of data to decrypt. passphrase : byte string The passphrase to use for decrypting the data. :Returns: The decrypted data, as a binary string. """ encrypted_private_key_info = decode_der(DerSequence, data) encrypted_algorithm = decode_der( DerSequence, encrypted_private_key_info[0] ) encrypted_data = decode_der( DerOctetString, encrypted_private_key_info[1] ).payload pbe_oid = decode_der(DerObjectId, encrypted_algorithm[0]).value cipher_params = {} if pbe_oid == "1.2.840.113549.1.5.3": # PBE_MD5_DES_CBC hashmod = MD5 ciphermod = DES elif pbe_oid == "1.2.840.113549.1.5.6": # PBE_MD5_RC2_CBC hashmod = MD5 ciphermod = ARC2 cipher_params['effective_keylen'] = 64 elif pbe_oid == "1.2.840.113549.1.5.10": # PBE_SHA1_DES_CBC hashmod = SHA1 ciphermod = DES elif pbe_oid == "1.2.840.113549.1.5.11": # PBE_SHA1_RC2_CBC hashmod = SHA1 ciphermod = ARC2 cipher_params['effective_keylen'] = 64 else: raise PbesError("Unknown OID for PBES1") pbe_params = decode_der(DerSequence, encrypted_algorithm[1]) salt = decode_der(DerOctetString, pbe_params[0]).payload iterations = pbe_params[1] key_iv = PBKDF1(passphrase, salt, 16, iterations, hashmod) key, iv = key_iv[:8], key_iv[8:] cipher = ciphermod.new(key, ciphermod.MODE_CBC, iv, **cipher_params) pt = cipher.decrypt(encrypted_data) return unpad(pt, cipher.block_size)
def decode(pem_data, passphrase=None): r = re.compile('\\s*-----BEGIN (.*)-----\n') m = r.match(pem_data) if not m: raise ValueError('Not a valid PEM pre boundary') marker = m.group(1) r = re.compile('-----END (.*)-----\\s*$') m = r.search(pem_data) if not m or m.group(1) != marker: raise ValueError('Not a valid PEM post boundary') lines = pem_data.replace(' ', '').split() if lines[1].startswith('Proc-Type:4,ENCRYPTED'): if not passphrase: raise ValueError('PEM is encrypted, but no passphrase available') DEK = lines[2].split(':') if len(DEK) != 2 or DEK[0] != 'DEK-Info': raise ValueError('PEM encryption format not supported.') algo, salt = DEK[1].split(',') salt = unhexlify(tobytes(salt)) if algo == 'DES-CBC': key = PBKDF1(passphrase, salt, 8, 1, MD5) objdec = DES.new(key, DES.MODE_CBC, salt) elif algo == 'DES-EDE3-CBC': key = PBKDF1(passphrase, salt, 16, 1, MD5) key += PBKDF1(key + passphrase, salt, 8, 1, MD5) objdec = DES3.new(key, DES3.MODE_CBC, salt) elif algo == 'AES-128-CBC': key = PBKDF1(passphrase, salt[:8], 16, 1, MD5) objdec = AES.new(key, AES.MODE_CBC, salt) else: raise ValueError('Unsupport PEM encryption algorithm.') lines = lines[2:] else: objdec = None data = a2b_base64(b(''.join(lines[1:-1]))) enc_flag = False if objdec: data = unpad(objdec.decrypt(data), objdec.block_size) enc_flag = True return (data, marker, enc_flag)
def encrypt_key_object(private_key): data = private_key.exportKey(format='DER') out = "-----BEGIN RSA PRIVATE KEY-----\n" salt = Random.get_random_bytes(16) # Doing some AES-128-CBC here key = PBKDF1(CONF.get('encryption_key'), salt[:8], 16, 1, MD5) objenc = AES.new(key, AES.MODE_CBC, salt) out += "Proc-Type: 4,ENCRYPTED\nDEK-Info: AES-128-CBC,%s\n\n" % (tostr( hexlify(salt).upper())) data = objenc.encrypt(pad(data, objenc.block_size)) chunks = [ tostr(b2a_base64(data[i:i + 48])) for i in range(0, len(data), 48) ] out += "".join(chunks) out += "-----END RSA PRIVATE KEY-----" return out
def importKey(self, externKey, passphrase=None): """Import an RSA key (public or private half), encoded in standard form. :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) - 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` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. 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 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). .. _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 """ externKey = tobytes(externKey) if passphrase is not None: passphrase = tobytes(passphrase) if externKey.startswith(b('-----')): # This is probably a PEM encoded key lines = externKey.replace(b(" "),b('')).split() keyobj = None # 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(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:] e = bytes_to_long(keyparts[1]) n = bytes_to_long(keyparts[2]) return self.construct([n, e]) if bord(externKey[0])==0x30: # This is probably a DER encoded key return self._importKeyDER(externKey) raise ValueError("RSA key format is not supported")
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 do_crypto_setup(mp, salt = b'madhubala'): key = PBKDF1(mp, salt, 16, 100, SHA256) ctr = Counter.new(128, initial_value=long(254)) aes = AES.new(key, AES.MODE_CTR, counter=ctr) return aes
def test1(self): v = self._testData[0] res = PBKDF1(v[0], t2b(v[1]), v[2], v[3], SHA1) self.assertEqual(res, t2b(v[4]))
def decode(pem_data, passphrase=None): """Decode a PEM block into binary. :Parameters: pem_data : string The PEM block. passphrase : byte string If given and the PEM block is encrypted, the key will be derived from the passphrase. :Returns: A tuple with the binary data, the marker string, and a boolean to indicate if decryption was performed. :Raises ValueError: If decoding fails, if the PEM file is encrypted and no passphrase has been provided or if the passphrase is incorrect. """ # Verify Pre-Encapsulation Boundary r = re.compile("\s*-----BEGIN (.*)-----\n") m = r.match(pem_data) if not m: raise ValueError("Not a valid PEM pre boundary") marker = m.group(1) # Verify Post-Encapsulation Boundary r = re.compile("-----END (.*)-----\s*$") m = r.search(pem_data) if not m or m.group(1) != marker: raise ValueError("Not a valid PEM post boundary") # Removes spaces and slit on lines lines = pem_data.replace(" ", '').split() # Decrypts, if necessary if lines[1].startswith('Proc-Type:4,ENCRYPTED'): if not passphrase: raise ValueError("PEM is encrypted, but no passphrase available") DEK = lines[2].split(':') if len(DEK) != 2 or DEK[0] != 'DEK-Info': raise ValueError("PEM encryption format not supported.") algo, salt = DEK[1].split(',') salt = unhexlify(tobytes(salt)) if algo == "DES-CBC": # This is EVP_BytesToKey in OpenSSL key = PBKDF1(passphrase, salt, 8, 1, MD5) objdec = DES.new(key, DES.MODE_CBC, salt) elif algo == "DES-EDE3-CBC": # Note that EVP_BytesToKey is note exactly the same as PBKDF1 key = PBKDF1(passphrase, salt, 16, 1, MD5) key += PBKDF1(key + passphrase, salt, 8, 1, MD5) objdec = DES3.new(key, DES3.MODE_CBC, salt) elif algo == "AES-128-CBC": key = PBKDF1(passphrase, salt[:8], 16, 1, MD5) objdec = AES.new(key, AES.MODE_CBC, salt) else: raise ValueError("Unsupport PEM encryption algorithm.") lines = lines[2:] else: objdec = None # Decode body data = a2b_base64(b(''.join(lines[1:-1]))) enc_flag = False if objdec: data = unpad(objdec.decrypt(data), objdec.block_size) enc_flag = True return (data, marker, enc_flag)
def do_crypto_setup(): key = PBKDF1(PRIVATE_HONEY_ENC_KEY, PRIVATE_SALT, 16, 100, SHA256) ctr = Counter.new(8 * SECURITY_PARAM, initial_value=long(254)) aes = AES.new(key, AES.MODE_CTR, counter=ctr) return aes