def print_p12_secretBag(secretbag_der, password, show_pem=False, list_only=False, indent=''): """Parse PKCS#12 secretBag ASN.1 data""" # SecretBag ::= SEQUENCE { # secretTypeId BAG-TYPE.&id ({SecretTypes}), # secretValue [0] EXPLICIT BAG-TYPE.&Type ({SecretTypes} {@secretTypeId}) # } secret_type_id_der, secret_value_der = util_asn1.decode_sequence( secretbag_der, 2) secret_type_id = util_asn1.decode_oid(secret_type_id_der) secret_value_der = util_asn1.decode_object(secret_value_der) print("{}* secret type: {}".format(indent, secret_type_id)) secret_value = util_asn1.decode_octet_string(secret_value_der) if secret_type_id == 'keyBag': print_p12_keybag(secret_value, password, show_pem=show_pem, list_only=list_only, indent=indent) else: raise NotImplementedError( "Unimplemented secretBag type {}".format(secret_type_id))
def print_p12_certBag(certbag_der, show_pem=False, list_only=False, indent=''): """Parse PKCS#12 certBag ASN.1 data""" # CertBag ::= SEQUENCE { # certId BAG-TYPE.&id ({CertTypes}), # certValue [0] EXPLICIT BAG-TYPE.&Type ({CertTypes}{@certId}) # } cert_id_der, cert_value_der = util_asn1.decode_sequence(certbag_der, 2) cert_id = util_asn1.decode_oid(cert_id_der) cert_value_der = util_asn1.decode_object(cert_value_der) if cert_id != 'x509Certificate': raise NotImplementedError("Unknown certificate format {}".format(repr(cert_id))) cert = util_asn1.decode_octet_string(cert_value_der) description = describe_der_certificate(cert) if description: print("{}* Certificate: {}".format(indent, description)) else: print("{}* Certificate: (no description available)".format(indent)) run_openssl_show_cert(cert, list_only=list_only, show_pem=show_pem, indent=indent)
def print_p12_keystore(ks_content, password, show_pem=False, list_only=False): """Parse a PKCS#12 KeyStore file and print it""" # run_process_with_input(['openssl', 'asn1parse', '-i', '-inform', 'DER'], ks_content, fatal=True) # PFX (Personal Information Exchange) is defined as: # PFX ::= SEQUENCE { # version INTEGER {v3(3)}(v3,...), # authSafe ContentInfo, # macData MacData OPTIONAL # } version, authsafe_der, macdata_der = util_asn1.decode_sequence( ks_content, 3) if version != 3: raise NotImplementedError( "Unimplemented PFX version {}".format(version)) # ContentInfo ::= SEQUENCE { # contentType ContentType, # content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL # } # ContentType ::= OBJECT IDENTIFIER authsafe_content_type_der, authsafe_content_der = util_asn1.decode_sequence( authsafe_der, 2) authsafe_content_type = util_asn1.decode_oid(authsafe_content_type_der) if authsafe_content_type != 'pkcs7-data': raise NotImplementedError( "Unimplemented PFX content type {}".format(authsafe_content_type)) authsafe_content_der = util_asn1.decode_object(authsafe_content_der) authsafe_content = util_asn1.decode_octet_string(authsafe_content_der) # MacData ::= SEQUENCE { # mac DigestInfo, # macSalt OCTET STRING, # iterations INTEGER DEFAULT 1 # } macdata_asn1 = util_asn1.decode_sequence(macdata_der) if len(macdata_asn1) == 2: mac_der, mac_salt_der = macdata_asn1 mac_iterations = 1 elif len(macdata_asn1) == 3: mac_der, mac_salt_der, mac_iterations = macdata_asn1 else: raise ValueError( "Unexpected number of items in ASN.1 MacData sequence") mac_salt = util_asn1.decode_octet_string(mac_salt_der) # DigestInfo ::= SEQUENCE { # digestAlgorithm DigestAlgorithmIdentifier, # digest Digest # } # DigestAlgorithmIdentifier ::= AlgorithmIdentifier # Digest ::= OCTET STRING mac_digest_algorithm_der, mac_digest_der = util_asn1.decode_sequence( mac_der, 2) mac_digest_algorithm = util_asn1.decode_x509_algid( mac_digest_algorithm_der) mac_digest = util_asn1.decode_octet_string(mac_digest_der) print("* PKCS#12 Keystore MAC:") print(" * algorithm: {}".format(mac_digest_algorithm)) print(" * salt: {}".format(xx(mac_salt))) print(" * iterations: {}".format(mac_iterations)) print(" * HMAC digest: {}".format(xx(mac_digest))) mac_key = pkcs12_derivation(alg=mac_digest_algorithm, id_byte=3, password=password, salt=mac_salt, iterations=mac_iterations) mac_hmac = hmac.new(key=mac_key, msg=authsafe_content, digestmod=hashlib.sha1).digest() if mac_hmac == mac_digest: print(" (password: {})".format(repr(password))) print(" (HMAC key: {})".format(xx(mac_key))) else: print(" (computed HMAC: {})".format(xx(mac_hmac))) print(" * wrong password (pad HMAC digest)") # AuthenticatedSafe ::= SEQUENCE OF ContentInfo # -- Data if unencrypted # -- EncryptedData if password-encrypted # -- EnvelopedData if public key-encrypted authsafe_seq = util_asn1.decode_sequence(authsafe_content) print("* {} data blocks:".format(len(authsafe_seq))) for blk_index, blk_der in enumerate(authsafe_seq): blk_content_type_der, blk_content_der = util_asn1.decode_sequence( blk_der, 2) blk_content_type = util_asn1.decode_oid(blk_content_type_der) blk_content_der = util_asn1.decode_object( blk_content_der) # tag "cont[0]" if blk_content_type == 'pkcs7-data': safe_contents = util_asn1.decode_octet_string(blk_content_der) print(" [{}] unencrypted safe contents:".format(blk_index + 1)) print_p12_safe_contents(safe_contents, password, show_pem=show_pem, list_only=list_only, indent=" ") elif blk_content_type == 'pkcs7-encryptedData': print(" [{}] encrypted safe contents:".format(blk_index + 1)) # EncryptedData ::= SEQUENCE { # version Version, # encryptedContentInfo EncryptedContentInfo # } encblk_version, encrypted_ci_der = util_asn1.decode_sequence( blk_content_der, 2) if encblk_version != 0: raise NotImplementedError( "Unimplemented PKCS#7 EncryptedData version {}".format( encblk_version)) # EncryptedContentInfo ::= SEQUENCE { # contentType ContentType, # contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, # encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL # } # ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier # EncryptedContent ::= OCTET STRING enc_ctype_der, enc_alg_der, enc_content_der = util_asn1.decode_sequence( encrypted_ci_der, 3) enc_ctype = util_asn1.decode_oid(enc_ctype_der) enc_alg = util_asn1.decode_x509_algid(enc_alg_der) enc_content = util_asn1.decode_object( enc_content_der) # tag "cont[0]" if enc_ctype != 'pkcs7-data': raise NotImplementedError( "Unimplemented PKCS#7 EncryptedData content type {}". format(enc_ctype)) print(" * encryption algorithm: {}".format(enc_alg)) safe_contents = try_pkcs12_decrypt(enc_content, enc_alg, password, indent=" ") if safe_contents is not None: print_p12_safe_contents(safe_contents, password, show_pem=show_pem, list_only=list_only, indent=" ") else: raise NotImplementedError( "Unimplemented bag content type {}".format(blk_content_type))
def print_p12_safe_contents(safe_contents_der, password, show_pem=False, list_only=False, indent=''): """Parse PKCS#12 SafeContents ASN.1 data https://tools.ietf.org/html/rfc7292#section-4.2 The SafeContents type is made up of SafeBags. Each SafeBag holds one piece of information -- a key, a certificate, etc. -- which is identified by an object identifier. """ # SafeContents ::= SEQUENCE OF SafeBag # SafeBag ::= SEQUENCE { # bagId BAG-TYPE.&id ({PKCS12BagSet}) # bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}), # bagAttributes SET OF PKCS12Attribute OPTIONAL # } # PKCS12Attribute ::= SEQUENCE { # attrId ATTRIBUTE.&id ({PKCS12AttrSet}), # attrValues SET OF ATTRIBUTE.&Type ({PKCS12AttrSet}{@attrId}) # } -- This type is compatible with the X.500 type 'Attribute' # PKCS12AttrSet ATTRIBUTE ::= { # friendlyName | -- from PKCS #9 # localKeyId, -- from PKCS #9 # ... -- Other attributes are allowed # } safe_bags = util_asn1.decode_sequence(safe_contents_der) print("{}* {} {}:".format( indent, len(safe_bags), "safe bags" if len(safe_bags) >= 2 else "safe bag")) for idx_safe_bag, safe_bag_der in enumerate(safe_bags): safe_bag = util_asn1.decode_sequence(safe_bag_der, counts=(2, 3)) bag_id = util_asn1.decode_oid(safe_bag[0]) bag_value = util_asn1.decode_object(safe_bag[1]) try: bag_attributes = util_asn1.decode_set( safe_bag[2]) if len(safe_bag) >= 3 else [] except NotImplementedError as exc: # Recover from error caused by old PyCrypto logger.warning("Unable to decode bag attributes: %s", exc) attr_descs = ['?'] else: attr_descs = [] for bag_attribute_der in bag_attributes: attr_id_der, attr_values_der = util_asn1.decode_sequence( bag_attribute_der, 2) attr_id = util_asn1.decode_oid(attr_id_der) attr_values_der = util_asn1.decode_set(attr_values_der) attr_values = [ util_asn1.decode_any_string(v) for v in attr_values_der ] attr_descs.append("{}={}".format( attr_id, ','.join(repr(v) for v in attr_values))) if attr_id == 'localKeyID' and len(attr_values) == 1: m = re.match(r'^Time ([0-9]+)$', attr_values[0]) if m: # Parse the timestamp from the local key ID timestamp = int(m.group(1)) attr_descs.append("date='{}'".format( datetime.datetime.fromtimestamp(timestamp / 1000))) print("{} [{}] {} ({})".format(indent, idx_safe_bag + 1, bag_id, ', '.join(attr_descs))) if bag_id == 'keyBag': print_p12_keybag(bag_value, password, show_pem=show_pem, list_only=list_only, indent=indent + " ") elif bag_id == 'certBag': print_p12_certBag(bag_value, show_pem=show_pem, list_only=list_only, indent=indent + " ") elif bag_id == 'secretBag': print_p12_secretBag(bag_value, password, show_pem=show_pem, list_only=list_only, indent=indent + " ") else: print("{} * bag value: {}".format(indent, repr(bag_value))) raise NotImplementedError("Unimplemented bag id {}".format(bag_id))
def print_ks_private_key(ks_content, password, offset=0, show_pem=False, list_only=False): """Print a private contained in a JKS or a JCEKS""" privkey_size, = struct.unpack('>I', ks_content[offset:offset + 4]) offset += 4 privkey = ks_content[offset:offset + privkey_size] offset += privkey_size print(" * private key ({} bytes)".format(len(privkey))) # run_process_with_input(['openssl', 'asn1parse', '-i', '-inform', 'DER'], privkey, fatal=True) privkey_type_der, privkey_octetstring_der = util_asn1.decode_sequence(privkey, 2) privkey_type_oid_der, privkey_type_params_der = util_asn1.decode_sequence(privkey_type_der) privkey_type_oid = util_asn1.decode_oid(privkey_type_oid_der) privkey_encrypted = util_asn1.decode_octet_string(privkey_octetstring_der) decrypted = None if privkey_type_oid == '1.3.6.1.4.1.42.2.17.1.1': # OID 1.3.6.1.4.1.42.2.17.1.1 is proprietary JavaSoft algorithm # {iso(1) identified-organization(3) dod(6) internet(1) private(4) # enterprise(1) Sun Microsystems (42) products(2) 17 1 1} assert privkey_type_params_der == b'\x05\x00' # NULL in ASN.1 DER notation print(" * JKS encryption") iv = privkey_encrypted[:20] integrity_hash = privkey_encrypted[-20:] print(" * IV: {}".format(xx(iv))) print(" * SHA1 hash: {}".format(xx(integrity_hash))) password_bytes = password.encode('utf-16be') keystream = [] while len(keystream) < len(privkey_encrypted) - 40: iv = hashlib.sha1(password_bytes + iv).digest() keystream += struct.unpack('20B', iv) data_struct_fmt = '{}B'.format(len(privkey_encrypted) - 40) enc_data = struct.unpack(data_struct_fmt, privkey_encrypted[20:-20]) decrypted = struct.pack(data_struct_fmt, *[x ^ k for x, k in zip(enc_data, keystream)]) computed_hash = hashlib.sha1(password_bytes + decrypted).digest() if computed_hash != integrity_hash: print(" * wrong password (bad SHA1 hash)") decrypted = None elif privkey_type_oid == '1.3.6.1.4.1.42.2.19.1': # OID 1.3.6.1.4.1.42.2.19.1 is PBE_WITH_MD5_AND_DES3_CBC (JCEKS) salt_der, iterations = util_asn1.decode_sequence(privkey_type_params_der, 2) salt = util_asn1.decode_octet_string(salt_der) print(" * JCEKS encryption") print(" * salt ({} bytes): {}".format(len(salt), xx(salt))) print(" * iterations: {}".format(iterations)) key, iv = PBEWithMD5AndTripleDES_derivation(password, salt, iterations) crypto_3des = Crypto.Cipher.DES3.new(key, Crypto.Cipher.DES3.MODE_CBC, iv) decrypted = crypto_3des.decrypt(privkey_encrypted) padlen, = struct.unpack('B', decrypted[-1:]) # Check PKCS#5 padding if not (1 <= padlen <= 0x10) or any(x != decrypted[-1] for x in decrypted[-padlen:]): print(" * wrong password (bad PKCS#5 padding)") decrypted = None else: decrypted = decrypted[:-padlen] else: raise ValueError("Unknown private key with OID {}".format(privkey_type_oid)) if decrypted: print(" (password: {})".format(repr(password))) # print(" (key: {})".format(xx(key))) # print(" (iv: {})".format(xx(iv))) util_asn1.show_pkcs8_private_key_info(decrypted, list_only=list_only, show_pem=show_pem, indent=" ") chain_length, = struct.unpack('>I', ks_content[offset:offset + 4]) offset += 4 for chain_index in range(chain_length): offset = print_ks_certificate(ks_content, offset=offset, show_pem=show_pem, list_only=list_only) return offset