Task 504: .P12 File Format

Task 504: .P12 File Format

The .P12 file format is the file extension commonly used for PKCS#12 archives, as defined in RFC 7292. It is a binary format for bundling cryptography objects such as private keys, X.509 certificates, and related secrets, with optional encryption and integrity protection. The format uses ASN.1 with BER encoding.

  1. List of all the properties of this file format intrinsic to its file system:
  • Version: An INTEGER field, typically set to 3 (v3) for the current standard, with extensibility for future versions.
  • AuthSafe: A ContentInfo structure (from PKCS#7) that holds the authenticated contents. Its contentType is typically 'data' or 'signedData', and the content is a BER-encoded AuthenticatedSafe, which is a SEQUENCE OF ContentInfo (allowing multiple independently protected sections).
  • MacData: An OPTIONAL structure for password-based integrity, consisting of:
  • Mac: A DigestInfo containing the MAC value (using HMAC with SHA-1, SHA-256, or similar).
  • MacSalt: An OCTET STRING for salt in key derivation.
  • Iterations: An INTEGER for iteration count in key derivation (default 1, but recommended >=1024).
  • AuthenticatedSafe: A SEQUENCE OF ContentInfo, where each ContentInfo can be:
  • Unencrypted (contentType 'data', content as BER-encoded SafeContents).
  • Password-encrypted (contentType 'encryptedData', encryptedContent as encrypted BER-encoded SafeContents).
  • Public-key-encrypted (contentType 'envelopedData').
  • SafeContents: A SEQUENCE OF SafeBag, representing the core contents.
  • SafeBag: A SEQUENCE for each item, consisting of:
  • BagId: An OBJECT IDENTIFIER identifying the bag type (e.g., {pkcs-12 10 1 1} for keyBag).
  • BagValue: An EXPLICIT [0] ANY DEFINED BY bagId, holding the actual content (type-dependent).
  • BagAttributes: An OPTIONAL SET OF PKCS12Attribute (e.g., friendlyName as BMPString, localKeyId as OCTET STRING).
  • Bag types and their structures (extensible via PKCS12BagSet):
  • keyBag: PrivateKeyInfo (from PKCS#8): version, privateKeyAlgorithm, privateKey (OCTET STRING), attributes (OPTIONAL).
  • pkcs8ShroudedKeyBag: EncryptedPrivateKeyInfo (from PKCS#8): encryptionAlgorithm, encryptedData (OCTET STRING).
  • certBag: SEQUENCE with certId (OBJECT IDENTIFIER, e.g., for X.509), certValue (EXPLICIT [0] OCTET STRING containing DER-encoded certificate).
  • crlBag: SEQUENCE with crlId (OBJECT IDENTIFIER, e.g., for X.509 CRL), crlValue (EXPLICIT [0] OCTET STRING containing DER-encoded CRL).
  • secretBag: SEQUENCE with secretTypeId (OBJECT IDENTIFIER), secretValue (EXPLICIT [0] ANY DEFINED BY secretTypeId).
  • safeContentsBag: Nested SafeContents (for recursion).
  • Encryption parameters (for shrouded or encrypted contents): pkcs-12PbeParams SEQUENCE with salt (OCTET STRING) and iterations (INTEGER), using OIDs like pbeWithSHAAnd3-KeyTripleDES-CBC.
  • Encoding: BER for overall structure; DER for embedded certificates/CRLs.
  • File-level intrinsics: No fixed magic bytes (starts with ASN.1 SEQUENCE tag 0x30), typical MIME type application/x-pkcs12, file extension .p12 or .pfx.
  1. Two direct download links for files of format .P12:
  1. Ghost blog embedded HTML JavaScript for drag and drop .P12 file dump:
Drag and drop a .p12 file here

Note: This is a basic parser using asn1js library via CDN. It assumes an unencrypted file for simplicity; for encrypted .p12 files, you'd need to add password input and use a full PKCS12 library like pkijs or forge for decryption.

  1. Python class for .P12 handling:
from cryptography.hazmat.primitives.serialization.pkcs12 import load_key_and_certificates
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
import os

class P12Handler:
    def __init__(self, filepath, password=None):
        self.filepath = filepath
        self.password = password.encode() if password else None
        self.private_key = None
        self.certificate = None
        self.additional_certs = None
        self.load()

    def load(self):
        with open(self.filepath, 'rb') as f:
            p12_data = f.read()
        self.private_key, self.certificate, self.additional_certs = load_key_and_certificates(
            p12_data, self.password, default_backend()
        )

    def print_properties(self):
        print(f"Version: Typically 3 (PKCS#12 standard)")
        if self.certificate:
            print(f"Certificate Subject: {self.certificate.subject.rfc4514_string()}")
            print(f"Certificate Issuer: {self.certificate.issuer.rfc4514_string()}")
            print(f"Certificate Serial: {self.certificate.serial_number}")
            print(f"Certificate Validity: {self.certificate.not_valid_before} to {self.certificate.not_valid_after}")
        if self.private_key:
            print(f"Private Key Algorithm: {self.private_key.public_key().public_numbers()}")
        print(f"Additional Certs Count: {len(self.additional_certs) if self.additional_certs else 0}")
        # For MacData and other low-level, use asn1crypto if needed; omitted for simplicity

    def write(self, new_filepath, new_password=None):
        new_pass = new_password.encode() if new_password else None
        p12 = serialization.pkcs12.serialize_key_and_certificates(
            b"export",
            self.private_key,
            self.certificate,
            self.additional_certs,
            serialization.BestAvailableEncryption(new_pass) if new_pass else serialization.NoEncryption()
        )
        with open(new_filepath, 'wb') as f:
            f.write(p12)

# Example usage:
# handler = P12Handler('example.p12', password='badssl.com')
# handler.print_properties()
# handler.write('new.p12', new_password='newpass')

Note: Requires the 'cryptography' library (pip install cryptography). For write, it re-exports the loaded key and certs.

  1. Java class for .P12 handling:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;

public class P12Handler {
    private KeyStore keyStore;
    private String filepath;
    private char[] password;

    public P12Handler(String filepath, String password) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
        this.filepath = filepath;
        this.password = password != null ? password.toCharArray() : null;
        keyStore = KeyStore.getInstance("PKCS12");
        try (FileInputStream fis = new FileInputStream(filepath)) {
            keyStore.load(fis, this.password);
        }
    }

    public void printProperties() throws KeyStoreException {
        System.out.println("Version: 3 (PKCS#12 standard)");
        Enumeration<String> aliases = keyStore.aliases();
        while (aliases.hasMoreElements()) {
            String alias = aliases.nextElement();
            if (keyStore.isKeyEntry(alias)) {
                System.out.println("Key Entry Alias: " + alias);
            }
            if (keyStore.isCertificateEntry(alias)) {
                X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
                System.out.println("Cert Alias: " + alias);
                System.out.println("Cert Subject: " + cert.getSubjectDN());
                System.out.println("Cert Issuer: " + cert.getIssuerDN());
                System.out.println("Cert Serial: " + cert.getSerialNumber());
                System.out.println("Cert Validity: " + cert.getNotBefore() + " to " + cert.getNotAfter());
            }
        }
        // MacData not directly accessible; use BouncyCastle for low-level if needed
    }

    public void write(String newFilepath, String newPassword) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
        char[] newPass = newPassword != null ? newPassword.toCharArray() : null;
        try (FileOutputStream fos = new FileOutputStream(newFilepath)) {
            keyStore.store(fos, newPass);
        }
    }

    // Example usage:
    // public static void main(String[] args) throws Exception {
    //     P12Handler handler = new P12Handler("example.p12", "badssl.com");
    //     handler.printProperties();
    //     handler.write("new.p12", "newpass");
    // }
}
  1. JavaScript class for .P12 handling (node.js environment):
const fs = require('fs');
const forge = require('node-forge');

class P12Handler {
  constructor(filepath, password = null) {
    this.filepath = filepath;
    this.password = password;
    this.p12 = null;
    this.load();
  }

  load() {
    const p12Der = fs.readFileSync(this.filepath);
    this.p12 = forge.pkcs12.pkcs12FromAsn1(forge.asn1.fromDer(p12Der.toString('binary')), this.password);
  }

  printProperties() {
    console.log('Version: ' + this.p12.version);
    console.log('Safe Contents Count: ' + this.p12.safeContents.length);
    this.p12.safeContents.forEach((safe, idx) => {
      console.log(`Safe ${idx} Authenticated: ${safe.authenticated}`);
      console.log(`Safe ${idx} Bags Count: ${safe.safeBags.length}`);
      safe.safeBags.forEach((bag) => {
        console.log('Bag Type: ' + bag.type);
        if (bag.type === forge.pki.oids.certBag && bag.cert) {
          console.log('Cert Subject: ' + bag.cert.subject.attributes.map(a => a.shortName + '=' + a.value).join(', '));
          console.log('Cert Issuer: ' + bag.cert.issuer.attributes.map(a => a.shortName + '=' + a.value).join(', '));
          console.log('Cert Serial: ' + bag.cert.serialNumber);
          console.log('Cert Validity: ' + bag.cert.validity.notBefore + ' to ' + bag.cert.validity.notAfter);
        }
        if (bag.type === forge.pki.oids.pkcs8ShroudedKeyBag && bag.key) {
          console.log('Key Algorithm: ' + bag.key.algorithm);
        }
        if (bag.attributes.friendlyName) {
          console.log('Friendly Name: ' + bag.attributes.friendlyName[0]);
        }
      });
    });
    if (this.p12.macData) {
      console.log('MacData Iterations: ' + this.p12.macData.iter);
    }
  }

  write(newFilepath, newPassword = null) {
    const asn1 = forge.pkcs12.toPkcs12Asn1(null, this.p12.safeContents, newPassword);
    const der = forge.asn1.toDer(asn1).getBytes();
    fs.writeFileSync(newFilepath, der, 'binary');
  }
}

// Example usage:
// const handler = new P12Handler('example.p12', 'badssl.com');
// handler.printProperties();
// handler.write('new.p12', 'newpass');

Note: Requires 'node-forge' library (npm install node-forge). Handles read/write with optional password.

  1. C class (using C++ for class structure; assumes OpenSSL library):
#include <iostream>
#include <openssl/pkcs12.h>
#include <openssl/err.h>
#include <openssl/x509v3.h>

class P12Handler {
private:
    std::string filepath;
    const char* password;
    PKCS12* p12;

public:
    P12Handler(const std::string& filepath, const char* password = nullptr) : filepath(filepath), password(password), p12(nullptr) {
        OpenSSL_add_all_algorithms();
        load();
    }

    ~P12Handler() {
        if (p12) PKCS12_free(p12);
    }

    void load() {
        FILE* fp = fopen(filepath.c_str(), "rb");
        if (!fp) {
            std::cerr << "Error opening file" << std::endl;
            return;
        }
        p12 = d2i_PKCS12_fp(fp, nullptr);
        fclose(fp);
        if (!p12) {
            std::cerr << "Error loading P12" << std::endl;
        }
    }

    void printProperties() {
        if (!p12) return;
        EVP_PKEY* pkey = nullptr;
        X509* cert = nullptr;
        STACK_OF(X509)* ca = nullptr;
        if (PKCS12_parse(p12, password, &pkey, &cert, &ca)) {
            std::cout << "Version: " << PKCS12_get_attr_gen(p12, NID_pkcs9_contentType) << std::endl;  // Approx version via attrs
            if (cert) {
                char subj[256], issuer[256];
                X509_NAME_oneline(X509_get_subject_name(cert), subj, sizeof(subj));
                X509_NAME_oneline(X509_get_issuer_name(cert), issuer, sizeof(issuer));
                std::cout << "Cert Subject: " << subj << std::endl;
                std::cout << "Cert Issuer: " << issuer << std::endl;
                std::cout << "Cert Serial: " << ASN1_INTEGER_get(X509_get0_serialNumber(cert)) << std::endl;
            }
            std::cout << "Additional Certs Count: " << sk_X509_num(ca) << std::endl;
            if (pkey) {
                std::cout << "Private Key Present: Yes" << std::endl;
            }
            // MacData via low-level access if needed
            EVP_PKEY_free(pkey);
            X509_free(cert);
            sk_X509_pop_free(ca, X509_free);
        } else {
            std::cerr << "Error parsing (wrong password?)" << std::endl;
            ERR_print_errors_fp(stderr);
        }
    }

    void write(const std::string& newFilepath, const char* newPassword = nullptr) {
        if (!p12) return;
        FILE* fp = fopen(newFilepath.c_str(), "wb");
        if (!fp) {
            std::cerr << "Error opening output file" << std::endl;
            return;
        }
        i2d_PKCS12_fp(fp, p12);  // Writes existing; for new, rebuild with PKCS12_create
        fclose(fp);
    }
};

// Example usage:
// int main() {
//     P12Handler handler("example.p12", "badssl.com");
//     handler.printProperties();
//     handler.write("new.p12", "newpass");
//     return 0;
// }

Note: Requires OpenSSL library linked (e.g., -lssl -lcrypto). The write is a simple re-export; for full read/write with modifications, use PKCS12_create.