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.
- 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.
- Two direct download links for files of format .P12:
- https://badssl.com/certs/badssl.com-client.p12 (password: badssl.com)
- https://itv.mit-xperts.com/clientssl/issue/dload/sample.p12 (password: test; note: this is inferred from the sample demo page as a potential direct link, but if it 404s, access the page at https://itv.mit-xperts.com/clientssl/issue/dload/sample.php to trigger the download)
- Ghost blog embedded HTML JavaScript for drag and drop .P12 file dump:
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.
- 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.
- 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");
// }
}
- 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.
- 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.