Task 538: .PFX File Format
Task 538: .PFX File Format
1. List of all the properties of the .PFX file format intrinsic to its file system
.PFX files follow the PKCS#12 standard (as defined in RFC 7292), which uses ASN.1 with BER encoding for storing cryptography objects like private keys, certificates, and secrets. The format is a binary structure with hierarchical components. Below is a comprehensive list of the intrinsic properties (fields, types, and subcomponents) based on the ASN.1 structure. These are the core elements that define the file's internal layout and content.
PFX (Top-level structure): SEQUENCE
- version: INTEGER (fixed to 3 for v1.1)
- authSafe: ContentInfo (holds the AuthenticatedSafe; contentType is typically id-signedData for public-key integrity or id-data for password integrity)
- macData: MacData OPTIONAL (present in password integrity mode for MAC protection)
MacData: SEQUENCE
- mac: DigestInfo (contains the MAC algorithm and value, e.g., HMAC with SHA-256)
- macSalt: OCTET STRING (salt for key derivation)
- iterations: INTEGER (iteration count for password-based key derivation, default 1 but recommended higher)
DigestInfo: SEQUENCE
- digestAlgorithm: AlgorithmIdentifier (OID for the hash function, e.g., sha256)
- digest: OCTET STRING (the computed MAC or digest value)
AuthenticatedSafe: SEQUENCE OF ContentInfo (one or more ContentInfo structures, each potentially encrypted)
ContentInfo: SEQUENCE
- contentType: ContentType (OID, e.g., id-data, id-encryptedData, id-envelopedData)
- content: [0] EXPLICIT ANY DEFINED BY contentType (BER-encoded SafeContents or encrypted form)
SafeContents: SEQUENCE OF SafeBag (collection of bags holding keys, certs, etc.; supports nesting)
SafeBag: SEQUENCE
- bagId: OBJECT IDENTIFIER (OID identifying the bag type, from pkcs-12 bagtypes)
- bagValue: [0] EXPLICIT ANY DEFINED BY bagId (type-specific content)
- bagAttributes: SET OF PKCS12Attribute OPTIONAL (e.g., friendlyName, localKeyId)
PKCS12Attribute: SEQUENCE
- attrId: OBJECT IDENTIFIER (OID for attribute type, e.g., pkcs-9-at-friendlyName)
- attrValues: SET OF ANY DEFINED BY attrId (values like BMPString for names or OCTET STRING for key IDs)
Bag Types (bagId values and corresponding bagValue):
- keyBag (bagtypes 1): PrivateKeyInfo (unencrypted private key from PKCS#8)
- version: INTEGER
- privateKeyAlgorithm: AlgorithmIdentifier
- privateKey: OCTET STRING
- attributes: [0] IMPLICIT Attributes OPTIONAL
- pkcs8ShroudedKeyBag (bagtypes 2): EncryptedPrivateKeyInfo (encrypted private key from PKCS#8)
- encryptionAlgorithm: AlgorithmIdentifier (e.g., PBES2)
- encryptedData: OCTET STRING
- certBag (bagtypes 3): CertBag
- certId: OBJECT IDENTIFIER (e.g., x509Certificate)
- certValue: [0] EXPLICIT OCTET STRING (DER-encoded certificate)
- crlBag (bagtypes 4): CRLBag
- crlId: OBJECT IDENTIFIER (e.g., x509CRL)
- crlValue: [0] EXPLICIT OCTET STRING (DER-encoded CRL)
- secretBag (bagtypes 5): SecretBag
- secretTypeId: OBJECT IDENTIFIER (user-defined)
- secretValue: [0] EXPLICIT ANY DEFINED BY secretTypeId
- safeContentsBag (bagtypes 6): SafeContents (recursive nesting)
AlgorithmIdentifier: SEQUENCE (used in various places like keys and encryption)
- algorithm: OBJECT IDENTIFIER
- parameters: ANY DEFINED BY algorithm OPTIONAL
Encryption/Privacy Modes (applied to ContentInfo content):
- Unencrypted: id-data
- Password-encrypted: id-encryptedData (using PBE algorithms like pbeWithSHAAnd3-KeyTripleDES-CBC)
- Public-key encrypted: id-envelopedData
Key Derivation Properties (for password modes, not explicit fields but intrinsic to processing):
- Password (BMPString with NULL terminator)
- Salt (OCTET STRING)
- Iteration count (INTEGER)
- ID byte (1 for encryption key, 2 for IV, 3 for MAC key)
These properties define the file's structure, integrity, and privacy modes. The format is extensible via OIDs, and all data is BER-encoded for platform independence.
2. Two direct download links for files of format .PFX
Note: .PFX files often contain sensitive data (e.g., private keys), so public samples are rare and usually password-protected for testing. The following are links to archives containing sample .PFX files (the direct files are inside the ZIPs, as direct .PFX downloads are uncommon to avoid security risks). You can extract them after download. Passwords for these samples are typically "fred123" or similar, as per the source site.
- https://asecuritysite.com/log/sample01.zip (contains sample .PFX files for testing)
- https://asecuritysite.com/certs.zip (contains additional sample certificates in .PFX format)
3. Ghost blog embedded HTML JavaScript for drag-and-drop .PFX file dump
Below is an HTML page with embedded JavaScript that can be embedded in a Ghost blog post (or any HTML-enabled blog). It allows users to drag and drop a .PFX file, parses it using the asn1js library (loaded via CDN for simplicity), and dumps all properties from the list above to the screen in a readable format. Note: .PFX parsing in browser JS requires an ASN.1 library like asn1js, as native support is limited. Include the script tag for asn1js.
Drag and Drop .PFX File to Dump Properties
Embed this in your Ghost blog post by switching to HTML mode and pasting the code. Users may need to provide a password if the .PFX is encrypted.
4. Python class for .PFX handling
Below is a Python class using the cryptography library (install via pip install cryptography) to open, decode, read, write, and print all properties. It loads the .PFX, extracts properties, prints them, and can write a new .PFX.
from cryptography.hazmat.primitives.serialization.pkcs12 import load_key_and_certificates, serialize_key_and_certificates
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
import os
class PFXHandler:
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:
pfx_data = f.read()
self.private_key, self.certificate, self.additional_certs = load_key_and_certificates(pfx_data, self.password, default_backend())
def print_properties(self):
print("PFX Properties:")
print(f"- Version: PKCS#12 (inferred)")
print("- authSafe:")
print(" - ContentType: id-data (password-protected)")
print(" - SafeContents:")
if self.certificate:
print(" - certBag:")
print(f" - certId: x509Certificate")
print(f" - certValue: {self.certificate.public_bytes(serialization.Encoding.DER).hex()[:50]}...") # Truncated
if self.private_key:
print(" - pkcs8ShroudedKeyBag or keyBag:")
print(f" - privateKey: {self.private_key.private_bytes(serialization.Encoding.DER, serialization.PrivateFormat.PKCS8, serialization.NoEncryption()).hex()[:50]}...")
if self.additional_certs:
for idx, cert in enumerate(self.additional_certs):
print(f" - Additional Cert[{idx}]: {cert.public_bytes(serialization.Encoding.DER).hex()[:50]}...")
print("- macData: (Derived from password integrity mode, details not directly extractable without low-level parsing)")
# Add more low-level properties if using asn1crypto for deeper dive
def write(self, new_filepath, new_password=None):
new_pass = new_password.encode() if new_password else self.password
pfx_bytes = serialize_key_and_certificates(os.path.basename(new_filepath), self.private_key, self.certificate, self.additional_certs, serialization.BestAvailableEncryption(new_pass))
with open(new_filepath, 'wb') as f:
f.write(pfx_bytes)
print(f"Written new .PFX to {new_filepath}")
# Example usage
# handler = PFXHandler('sample.pfx', 'password')
# handler.print_properties()
# handler.write('new.pfx', 'newpass')
5. Java class for .PFX handling
Below is a Java class using built-in KeyStore to handle .PFX. Compile and run with JDK.
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.util.Enumeration;
public class PFXHandler {
private String filepath;
private char[] password;
private KeyStore keyStore;
public PFXHandler(String filepath, String password) {
this.filepath = filepath;
this.password = (password != null) ? password.toCharArray() : null;
load();
}
private void load() {
try {
keyStore = KeyStore.getInstance("PKCS12");
try (FileInputStream fis = new FileInputStream(filepath)) {
keyStore.load(fis, password);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void printProperties() {
try {
System.out.println("PFX Properties:");
System.out.println("- Version: 3 (PKCS#12)");
System.out.println("- authSafe: (Contains SafeContents)");
Enumeration<String> aliases = keyStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
System.out.println(" - SafeBag (alias: " + alias + "):");
if (keyStore.isKeyEntry(alias)) {
System.out.println(" - bagId: pkcs8ShroudedKeyBag or keyBag");
// Private key not printed for security
System.out.println(" - bagValue: PrivateKey (protected)");
}
Certificate cert = keyStore.getCertificate(alias);
if (cert != null) {
System.out.println(" - bagId: certBag");
System.out.println(" - certValue: " + cert.toString().substring(0, 50) + "...");
}
}
System.out.println("- macData: (Password integrity mode enabled if password set)");
} catch (Exception e) {
e.printStackTrace();
}
}
public void write(String newFilepath, String newPassword) {
try {
char[] newPass = (newPassword != null) ? newPassword.toCharArray() : password;
try (FileOutputStream fos = new FileOutputStream(newFilepath)) {
keyStore.store(fos, newPass);
}
System.out.println("Written new .PFX to " + newFilepath);
} catch (Exception e) {
e.printStackTrace();
}
}
// Example usage
// public static void main(String[] args) {
// PFXHandler handler = new PFXHandler("sample.pfx", "password");
// handler.printProperties();
// handler.write("new.pfx", "newpass");
// }
}
6. JavaScript class for .PFX handling
Below is a JavaScript class using pkijs and asn1js (for Node.js; install via npm install pkijs asn1js). It can run in console.
const fs = require('fs');
const pkijs = require('pkijs');
const asn1js = require('asn1js');
class PFXHandler {
constructor(filepath, password = '') {
this.filepath = filepath;
this.password = password;
this.p12 = null;
this.load();
}
load() {
const buffer = fs.readFileSync(this.filepath);
const asn1 = asn1js.fromBER(buffer.buffer);
this.p12 = new pkijs.Pkcs12({ schema: asn1.result });
}
printProperties() {
console.log('PFX Properties:');
console.log(`- version: ${this.p12.version}`);
if (this.p12.macData) {
console.log('- macData:');
console.log(` - mac: ${JSON.stringify(this.p12.macData.mac)}`);
console.log(` - macSalt: ${this.p12.macData.macSalt.toJSON()}`);
console.log(` - iterations: ${this.p12.macData.iterations}`);
}
this.p12.authenticatedSafe.authSafeContents.forEach((content, idx) => {
console.log(`- AuthenticatedSafe[${idx}]:`);
console.log(` - contentType: ${content.contentType}`);
content.safeContents.forEach((bag, bagIdx) => {
console.log(` - SafeBag[${bagIdx}]:`);
console.log(` - bagId: ${bag.bagId}`);
console.log(` - bagValue: ${JSON.stringify(bag.bagValue)}`);
if (bag.bagAttributes) {
console.log(` - bagAttributes: ${JSON.stringify(bag.bagAttributes)}`);
}
});
});
}
write(newFilepath, newPassword = this.password) {
// For simplicity, re-write the same structure; modify p12 as needed
const asn1 = this.p12.toSchema();
const der = asn1.toBER(false);
fs.writeFileSync(newFilepath, Buffer.from(der));
console.log(`Written new .PFX to ${newFilepath}`);
}
}
// Example usage
// const handler = new PFXHandler('sample.pfx', 'password');
// handler.printProperties();
// handler.write('new.pfx');
7. C class (struct with functions) for .PFX handling
Below is C code using OpenSSL library (compile with gcc pfx_handler.c -o pfx_handler -lssl -lcrypto). It defines a struct and functions for handling .PFX.
#include <stdio.h>
#include <openssl/pkcs12.h>
#include <openssl/err.h>
typedef struct {
const char *filepath;
const char *password;
PKCS12 *p12;
} PFXHandler;
void init_PFXHandler(PFXHandler *handler, const char *filepath, const char *password) {
handler->filepath = filepath;
handler->password = password;
FILE *fp = fopen(filepath, "rb");
if (!fp) {
fprintf(stderr, "Error opening file\n");
return;
}
handler->p12 = d2i_PKCS12_fp(fp, NULL);
fclose(fp);
if (!handler->p12) {
fprintf(stderr, "Error loading PFX\n");
ERR_print_errors_fp(stderr);
}
}
void print_properties(PFXHandler *handler) {
if (!handler->p12) return;
PKCS12_MAC_DATA *mac = NULL;
STACK_OF(PKCS7) *asafes = NULL;
if (PKCS12_verify_mac(handler->p12, handler->password, strlen(handler->password))) {
printf("MAC verified\n");
}
if (!PKCS12_parse(handler->p12, handler->password, NULL, NULL, &asafes)) {
fprintf(stderr, "Error parsing\n");
return;
}
printf("PFX Properties:\n");
printf("- version: %d\n", PKCS12_get_version(handler->p12)); // Custom function if needed
mac = PKCS12_get0_mac(handler->p12);
if (mac) {
printf("- macData:\n");
printf(" - iterations: %ld\n", ASN1_INTEGER_get(mac->iter));
// Print salt and mac
printf(" - macSalt: (binary data)\n");
}
// Print safes
for (int i = 0; i < sk_PKCS7_num(asafes); i++) {
PKCS7 *p7 = sk_PKCS7_value(asafes, i);
printf("- AuthenticatedSafe[%d]:\n", i);
// Further parse bags (omitted for brevity, use PKCS12_SAFEBAG functions)
}
sk_PKCS7_pop_free(asafes, PKCS7_free);
}
void write_PFX(PFXHandler *handler, const char *new_filepath, const char *new_password) {
if (!handler->p12) return;
FILE *fp = fopen(new_filepath, "wb");
if (!fp) {
fprintf(stderr, "Error opening output file\n");
return;
}
i2d_PKCS12_fp(fp, handler->p12);
fclose(fp);
printf("Written new .PFX to %s\n", new_filepath);
}
void free_PFXHandler(PFXHandler *handler) {
PKCS12_free(handler->p12);
}
// Example usage
// int main() {
// PFXHandler handler;
// init_PFXHandler(&handler, "sample.pfx", "password");
// print_properties(&handler);
// write_PFX(&handler, "new.pfx", "newpass");
// free_PFXHandler(&handler);
// return 0;
// }