This package allows you to decrypt a token received from Apple Pay.
This works in node and not on a browser, as it requires the built-in crypto package and secret keys (.pem files), which should never exist on the client anyway.
The decryption methodology of this package is largely taken from the Gala Ruby Gem.
Version 1.1.0+:
- ✅ Removed x509 dependency: The package no longer depends on the
x509library - ✅ Using node-forge: Certificate parsing is now handled by
node-forgewhich was already included - ✅ Reduced dependencies: Fewer external dependencies for better security and maintenance
- ✅ Same functionality: All existing functionality remains unchanged
- ✅ Added comprehensive tests: New test suite to ensure reliability
This package now uses only:
crypto(built-in Node.js module)node-forge(for certificate parsing and cryptographic operations)ec-key(for elliptic curve key operations)
npm i --save apple-pay-decryptIn order to decrypt the token, you will need two .pem files. One is a certificate and one is a key. The process for generating these is complicated.
If you get stuck, this document might be helpful.
Run the following commands (largely taken from the article written by @amast09) to generate your keys:
openssl ecparam -out private.key -name prime256v1 -genkey
openssl req -new -sha256 -key private.key -nodes -out request.csrThen go to the Apple Developer Certificate Manager.
Make sure you have a Merchant Id. Navigate to Identifiers => Merchant IDs to make sure you have one, if not, create one.
Go to Certificates => All, then + in the top right. Select Apple Pay Payment Processing Certificate, go through to Generate and upload the .csr file you created (request.csr). Note that .csr is the same as .certSigningRequest.
Download the file, which will download as apple_pay.cer. You need that file to create the key.
openssl x509 -inform DER -outform PEM -in apple_pay.cer -out temp.pem
openssl pkcs12 -export -out key.p12 -inkey private.key -in temp.pemYou will need to password protect your .p12 file. Keep that password somewhere secure.
You now have the two files you need to decrypt Apple Pay tokens, but before you can do that, you need to convert them into .pem files.
Run the following commands to convert them to .pem files:
openssl x509 -inform DER -outform PEM -in apple_pay.cer -out certPem.pem
openssl pkcs12 -in key.p12 -out privatePem.pem -nocerts -nodesAfter all that, you should have a certificate (certPem.pem) file that looks something like this:
-----BEGIN CERTIFICATE-----
MIIEfzCCBCagAwIBAgIIcDQ4Fbx2jWYwCgYIKoZIzj0EAwIwgYAxNDAyBgNVBAMM
K0FwcGxlIFdvcmxkd2lkZSBEZXZlbG9wZXIgUmVsYXRpb25zIENBIC0gRzIxJjAk
BgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRMwEQYDVQQKDApB
cHBsZSBJbmMuMQswCQYDVQQGEwJVUzAeFw0xOTAyMjMxNDU2NDFaFw0yMTAzMjQx
NDU2NDFaMIGxMTAwLgYKCZImiZPyLGQBAQwgbWVyY2hhbnQuY29tLmdyYXRpdHVk
ZS5ncmF0aXR1ZGUxRjBEBgNVBAMMPUFwcGxlIFBheSBQYXltZW50IFByb2Nlc3Np
iWlORp7+MRSeIt3sEdnWIhY29xvHSdXgMT6kpaUupattcKtlHnLiYlTJHRRCO20x
2thoxaQriM+gFSnAyzrdaOnVTJHRRCO20xxcarcjHFr9GHRVsoysRC/ThwAqMaTs
XEV5VwHqpLuvzOca/+A5Q1MEkhH4lgNrqs5AhKkI1WZv2AWErjxkXBehvZy5C51n
RNcJ4KOAHTePfdrkQ3YVcyMnTlz2QBT8K/uLkoG/H1U8nNfaxwA5m6FDLoVXatC2
oGI+ctCv5Ge2SsEPaUqJ7zE3BU4UsbRvwiXwbWW42YZ2V2wvASdTiXw3/nv7apD4
H+PXFQuC86CSKNKV58jFZZNQoTlU0K+0rBR63ps4bBonVg4Bp2EBntFu5Du/rXMo
U5qxOgbh3/ZNtUT52AQicdJ0c+IgVYP6sGhVGorxMS0lFQ67qaj6luRaqzVovcGl
wa7DzQxcl0HZh2M/Wj9v2d+oGjlINlD9SAlWA/dWXrQF6kzEMoOJKBakO1SRVwD2
9UMDoM5JUK+iBteSFp6iHB7wyfb8VMwzzU3aSWDC+zrsbGXgQsFJ9ZClMyu/aiWs
rbugF9EtKocCWbODlxbRBp310XkPVcOKamZ0UI8P3+AvuMeXdnrFzUUBZnXU8bWM
RuIiK0QZobngHsRO3J/oT1h9URFflg7MrvbAyHTBPv5bSztOPcxOEIfwd+opq6Bc
MXZ+0fErpK5YW7jcahrPRp63e3FZjiKrHWZPFXXOH3N30VKRMDsKbZepNWu4glVb
YwKcj8BAm4LvxkCLODZVIsqYZbNTzyTWbKiz7G53Rt6XqFaQVlqlSxvA97SUfq62
RNcJ4KOAHTePfdrkQ3YVcyMnTlz2QBT8K/uLkoG/H1U8nNfaxwA5m6FDLoVXatC2
8nG5lEs5hYJ2WG9Yo39m1gyCHeNse5sOrph9Dq7tro5mO+nX3XaVaIi3MHFl9Hq6
uMetisso8rg633J/YpJipiz6MOdpf7Q7LqX6M0i3x4BJZfIa3xZPsUoEYObyGTJI
OtAJHpvnTIoDhBApBiH/sDq97pzcsl4VkngxxEiTEjXYQEIhcVQpG6lU6rX9+ekQ
qDRXQRMETBev1j7Y1w/v2K0CIAlnnXPVX52g5FTadoFyVq2a91sA4ao44VabMaz8
W5k1
-----END CERTIFICATE-----
And a key (privatePem.pem) that looks something like this:
Bag Attributes
localKeyID: 90 C8 20 E7 8A 2A E5 7E 33 06 FD C5 43 47 9F 15 2F DE 73 90
Key Attributes: <No Attributes>
-----BEGIN PRIVATE KEY-----
8nG5lEs5hYJ2WG9Yo39m1gyCHeNse5sOrph9Dq7tro5mO+nX3XaVaIi3MHFl9Hq6
YwKcj8BAm4LvxkCLODZVIsqYZbNTzyTWbKiz7G53Rt6XqFaQVlqlSxvA97SUfq62
qDRXQRMETBev1j7Y1w/v2K0CIAlnnXPVX52g5FTadoFyVq2a91sA4ao4
-----END PRIVATE KEY-----
(And no, those are not my real keys)
The tokenFromApplePay you get from Apple Pay will look something like this:
{
"version": "EC_v1",
"data": "vxae4VFHqdtWakaJ1wqQHyel...<a lot more data>...ggVQsfUxBXR8=",
"signature": "MIAGCSqGSIb3DQEHAqCA...<a lot more data>...MAAAAAAAA=",
"header": {
"ephemeralPublicKey": "MFkwEwYHKoZIzj0CAQYIKoZICZImiZPyLGQBAQwgbWVyY2hhbnQuY29tLmdy332d55suNAl1RIZi3KIT5hwmiSKSch9+6OOGlRZw0xOTAy4jejmO0A==",
"publicKeyHash": "0aB0KxDCKoZICZImiZPyLGQBAQwoIwz3m6bKxuqPe+F6yQco=",
"transactionId": "54829332dd6db37d06KoZICZImiZPyLGQBAQw5e6f35059acad43133d792fc139"
}
}To decrypt the token, import the .pem files and create a new PaymentToken with the token from Apple Pay. Then decrypt using the keys.
const PaymentToken = require('apple-pay-decrypt')
const certPem = fs.readFileSync(path.join(__dirname, '../path/to/certPem.pem'), 'utf8')
const privatePem = fs.readFileSync(path.join(__dirname, '../path/to/privatePem.pem'), 'utf8')
const tokenFromApplePay = {...} // from Apple Pay
const token = new PaymentToken(tokenFromApplePay)
const decrypted = token.decrypt(certPem, privatePem)The decrypted value at this point should look something like this:
{
applicationPrimaryAccountNumber: '17029283048730',
applicationExpirationDate: '231231',
currencyCode: '840',
transactionAmount: 500,
deviceManufacturerIdentifier: '544555544456',
paymentDataType: '3DSecure',
paymentData: {
onlinePaymentCryptogram: 'IE0QTuXZlbG9wZXIgUmiQAQojEBhgA='
}
}You can then use those decrypted values with your payment processor of choice (Stripe, Braintree, et al) to process payments from Apple Pay.
This package includes comprehensive tests to ensure reliability. Run the tests with:
npm testThe test suite covers:
- ✅ Constructor validation and initialization
- ✅ Symmetric key generation (KDF implementation)
- ✅ Certificate parsing and merchant ID extraction
- ✅ Error handling for invalid inputs
- ✅ Method existence and functionality
- ✅ Integration with node-forge instead of x509
- Constructor tests: Validates proper initialization with token attributes
- SymmetricKey tests: Tests the NIST SP 800-56A key derivation function
- MerchantId tests: Tests certificate parsing and merchant ID extraction
- Error handling: Ensures graceful handling of invalid certificates and keys
- Integration tests: Validates that all methods work together correctly
If you're upgrading from a version that used the x509 library, no code changes are required. The API remains exactly the same, but you'll benefit from:
- Fewer dependencies
- Better security posture
- More reliable certificate parsing
- Improved error handling
npm test- crypto: Node.js built-in module for cryptographic operations
- node-forge: Certificate parsing and additional crypto operations
- ec-key: Elliptic curve key operations for ECDH
The decryption process follows Apple Pay's specification:
- Parse Certificate: Extract merchant ID from the Apple Pay certificate extension (OID: 1.2.840.113635.100.6.32)
- Generate Shared Secret: Use ECDH with the merchant private key and ephemeral public key
- Derive Symmetric Key: Apply NIST SP 800-56A KDF with merchant ID and shared secret
- Decrypt Data: Use AES-GCM with the derived key to decrypt the payment data
Contributions are welcome! Please ensure that:
- All tests pass:
npm test - Code follows the existing style
- New features include appropriate tests
- Security considerations are addressed
MIT
This package handles sensitive cryptographic operations and private keys. Always:
- Keep private keys secure and never commit them to version control
- Use proper key management in production environments
- Validate all inputs and handle errors appropriately
- Keep dependencies up to date for security patches
For security issues, please report them privately rather than creating public issues.