passkit-generator
Version:
The easiest way to generate custom Apple Wallet passes in Node.js
96 lines • 6.44 kB
JavaScript
import forge from "node-forge";
import { Buffer } from "node:buffer";
/**
* Creates an hash for a buffer. Used by manifest
*
* @param buffer
* @returns
*/
export function createHash(buffer) {
const hashFlow = forge.md.sha1.create();
hashFlow.update(buffer.toString("binary"));
return hashFlow.digest().toHex();
}
/**
* Generates the PKCS #7 cryptografic signature for the manifest file.
*
* @method create
* @params manifest
* @params certificates
* @returns
*/
export function create(manifestBuffer, certificates) {
const signature = forge.pkcs7.createSignedData();
signature.content = new forge.util.ByteStringBuffer(manifestBuffer);
const { wwdr, signerCert, signerKey } = parseCertificates(getStringCertificates(certificates));
signature.addCertificate(wwdr);
signature.addCertificate(signerCert);
/**
* authenticatedAttributes belong to PKCS#9 standard.
* It requires at least 2 values:
* • content-type (which is a PKCS#7 oid) and
* • message-digest oid.
*
* Wallet requires a signingTime.
*/
signature.addSigner({
key: signerKey,
certificate: signerCert,
digestAlgorithm: forge.pki.oids.sha1,
authenticatedAttributes: [
{
type: forge.pki.oids.contentType,
value: forge.pki.oids.data,
},
{
type: forge.pki.oids.messageDigest,
},
{
type: forge.pki.oids.signingTime,
},
],
});
/**
* We are creating a detached signature because we don't need the signed content.
* Detached signature is a property of PKCS#7 cryptography standard.
*/
signature.sign({ detached: true });
/**
* Signature here is an ASN.1 valid structure (DER-compliant).
* Generating a non-detached signature, would have pushed inside signature.contentInfo
* (which has type 16, or "SEQUENCE", and is an array) a Context-Specific element, with the
* signed content as value.
*
* In fact the previous approach was to generating a detached signature and the pull away the generated
* content.
*
* That's what happens when you copy a fu****g line without understanding what it does.
* Well, nevermind, it was funny to study BER, DER, CER, ASN.1 and PKCS#7. You can learn a lot
* of beautiful things. ¯\_(ツ)_/¯
*/
return Buffer.from(forge.asn1.toDer(signature.toAsn1()).getBytes(), "binary");
}
/**
* Parses the PEM-formatted passed text (certificates)
*
* @param element - Text content of .pem files
* @param passphrase - passphrase for the key
* @returns The parsed certificate or key in node forge format
*/
function parseCertificates(certificates) {
const { signerCert, signerKey, wwdr, signerKeyPassphrase } = certificates;
return {
signerCert: forge.pki.certificateFromPem(signerCert.toString("utf-8")),
wwdr: forge.pki.certificateFromPem(wwdr.toString("utf-8")),
signerKey: forge.pki.decryptRsaPrivateKey(signerKey.toString("utf-8"), signerKeyPassphrase),
};
}
function getStringCertificates(certificates) {
return {
signerKeyPassphrase: certificates.signerKeyPassphrase,
wwdr: Buffer.from(certificates.wwdr).toString("utf-8"),
signerCert: Buffer.from(certificates.signerCert).toString("utf-8"),
signerKey: Buffer.from(certificates.signerKey).toString("utf-8"),
};
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2lnbmF0dXJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL1NpZ25hdHVyZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssTUFBTSxZQUFZLENBQUM7QUFFL0IsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUVyQzs7Ozs7R0FLRztBQUVILE1BQU0sVUFBVSxVQUFVLENBQUMsTUFBYztJQUN4QyxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztJQUN4QyxRQUFRLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztJQUUzQyxPQUFPLFFBQVEsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxLQUFLLEVBQUUsQ0FBQztBQUNsQyxDQUFDO0FBRUQ7Ozs7Ozs7R0FPRztBQUVILE1BQU0sVUFBVSxNQUFNLENBQ3JCLGNBQXNCLEVBQ3RCLFlBQXdDO0lBRXhDLE1BQU0sU0FBUyxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztJQUVqRCxTQUFTLENBQUMsT0FBTyxHQUFHLElBQUksS0FBSyxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxjQUFjLENBQUMsQ0FBQztJQUVwRSxNQUFNLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxTQUFTLEVBQUUsR0FBRyxpQkFBaUIsQ0FDeEQscUJBQXFCLENBQUMsWUFBWSxDQUFDLENBQ25DLENBQUM7SUFFRixTQUFTLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQy9CLFNBQVMsQ0FBQyxjQUFjLENBQUMsVUFBVSxDQUFDLENBQUM7SUFFckM7Ozs7Ozs7T0FPRztJQUVILFNBQVMsQ0FBQyxTQUFTLENBQUM7UUFDbkIsR0FBRyxFQUFFLFNBQVM7UUFDZCxXQUFXLEVBQUUsVUFBVTtRQUN2QixlQUFlLEVBQUUsS0FBSyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSTtRQUNwQyx1QkFBdUIsRUFBRTtZQUN4QjtnQkFDQyxJQUFJLEVBQUUsS0FBSyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsV0FBVztnQkFDaEMsS0FBSyxFQUFFLEtBQUssQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUk7YUFDMUI7WUFDRDtnQkFDQyxJQUFJLEVBQUUsS0FBSyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsYUFBYTthQUNsQztZQUNEO2dCQUNDLElBQUksRUFBRSxLQUFLLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxXQUFXO2FBQ2hDO1NBQ0Q7S0FDRCxDQUFDLENBQUM7SUFFSDs7O09BR0c7SUFFSCxTQUFTLENBQUMsSUFBSSxDQUFDLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7SUFFbkM7Ozs7Ozs7Ozs7OztPQVlHO0lBRUgsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUNqQixLQUFLLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQyxRQUFRLEVBQUUsRUFDL0MsUUFBUSxDQUNSLENBQUM7QUFDSCxDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBRUgsU0FBUyxpQkFBaUIsQ0FBQyxZQUF3QztJQUNsRSxNQUFNLEVBQUUsVUFBVSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsbUJBQW1CLEVBQUUsR0FBRyxZQUFZLENBQUM7SUFFMUUsT0FBTztRQUNOLFVBQVUsRUFBRSxLQUFLLENBQUMsR0FBRyxDQUFDLGtCQUFrQixDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDdEUsSUFBSSxFQUFFLEtBQUssQ0FBQyxHQUFHLENBQUMsa0JBQWtCLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUMxRCxTQUFTLEVBQUUsS0FBSyxDQUFDLEdBQUcsQ0FBQyxvQkFBb0IsQ0FDeEMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsRUFDM0IsbUJBQW1CLENBQ25CO0tBQ0QsQ0FBQztBQUNILENBQUM7QUFFRCxTQUFTLHFCQUFxQixDQUM3QixZQUF3QztJQUt4QyxPQUFPO1FBQ04sbUJBQW1CLEVBQUUsWUFBWSxDQUFDLG1CQUFtQjtRQUNyRCxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQztRQUN0RCxVQUFVLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsVUFBVSxDQUFDLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQztRQUNsRSxTQUFTLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsU0FBUyxDQUFDLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQztLQUNoRSxDQUFDO0FBQ0gsQ0FBQyJ9