Good day,
I’m trying to integrate with ApplePay, but i got in trouble. I can’t decrypt data that i got from ApplePay.
Here is my Code:
Here is my Concatenation Key Derivation Function:
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Concat {
public byte[] concatKDF(String hashAlg, byte[] z, int keyDataLen, byte[] algorithmID, byte[] partyUInfo, byte[] partyVInfo) throws NoSuchAlgorithmException
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
baos.write(algorithmID);
baos.write(partyUInfo);
baos.write(partyVInfo);
} catch (IOException e) {
throw new RuntimeException(e);
}
byte[] otherInfo = baos.toByteArray();
return concatKDF(hashAlg, z, keyDataLen, otherInfo);
}
public byte[] concatKDF(String hashAlg, byte[] z, int keyDataLen, byte[] otherInfo) throws NoSuchAlgorithmException
{
byte[] key = new byte[keyDataLen];
MessageDigest md = MessageDigest.getInstance(hashAlg);
int hashLen = md.getDigestLength();
int reps = keyDataLen / hashLen;
for(int i=1;i<=reps;i++){
md.reset();
md.update(intToFourBytes(i));
md.update(z);
md.update(otherInfo);
byte[] hash = md.digest();
if(i<reps){
System.arraycopy(hash, 0, key, hashLen*(i-1), hashLen);
}else{
if(keyDataLen % hashLen == 0){
System.arraycopy(hash, 0, key, hashLen*(i-1), hashLen);
}else{
System.arraycopy(hash, 0, key, hashLen*(i-1), keyDataLen % hashLen);
}
}
}
return key;
}
public byte[] intToFourBytes(int i){
byte[] res = new byte[4];
res[0] = (byte) (i >>> 24);
res[1] = (byte) ((i >>> 16) & 0xFF);
res[2] = (byte) ((i >>> 8) & 0xFF);
res[3] = (byte) (i & 0xFF);
return res;
}
}
here i try to restore symmetric key and decrypt data:
import org.bouncycastle.util.encoders.Hex;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class AppleDecryption {
private final static byte[] Z = "SHA-256".getBytes(StandardCharsets.UTF_8);
private final static byte[] partyUInfo = "Apple".getBytes(StandardCharsets.US_ASCII);
public static void main(String[] args) throws Exception {
String data = "data i need to decrypt";
PrivateKey merchantPrivateKey = loadPrivateKeyFromPKCS12("path_to_my_p12_key","password_of_key!","alias");
PublicKey publicKey = loadPublicKey();
X509Certificate certificate = loadCertificate();
//here i restore symmetric key according first step (1) this doc: https://developer.apple.com/documentation/passkit_apple_pay_and_wallet/apple_pay/payment_token_format_reference/restoring_the_symmetric_key?language=objc
KeyAgreement agreement = KeyAgreement.getInstance("ECDH");
agreement.init(merchantPrivateKey);
agreement.doPhase(publicKey, true);
byte[] sharedSecret = agreement.generateSecret();
//here i restore symmetric key according second step (2) this doc: https://developer.apple.com/documentation/passkit_apple_pay_and_wallet/apple_pay/payment_token_format_reference/restoring_the_symmetric_key?language=objc
byte[] merchantIdentifierTlv = certificate.getExtensionValue("1.2.840.113635.100.6.32");
byte[] merchantIdentifier = new byte[32];
System.arraycopy(merchantIdentifierTlv, 4, merchantIdentifier, 0, 32);
byte[] partyVInfo = Hex.decode(merchantIdentifier);
Concat concat = new Concat();
byte[] symmetricKey = concat.concatKDF("SHA-256", Z, 32, sharedSecret, partyUInfo, partyVInfo, null, null);
//here i try to decrypt data with symmetricKey that i got before according step 4 this doc: https://developer.apple.com/documentation/passkit_apple_pay_and_wallet/apple_pay/payment_token_format_reference
SecretKeySpec key = new SecretKeySpec(symmetricKey, "AES");
byte[] ivBytes = new byte[16];
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
Cipher aesCipher = Cipher.getInstance("AES/GCM/NoPadding");
aesCipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
aesCipher.doFinal(Base64.getDecoder().decode(data));
}
public static PrivateKey loadPrivateKeyFromPKCS12(String keyStorePath, String keyStorePassword, String alias) throws Exception {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
InputStream keyStoreData = new FileInputStream(keyStorePath);
keyStore.load(keyStoreData, keyStorePassword.toCharArray());
KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(keyStorePassword.toCharArray());
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, entryPassword);
PrivateKey privateKey = privateKeyEntry.getPrivateKey();
keyStoreData.close();
return privateKey;
}
public static X509Certificate loadCertificate() throws Exception {
String filename = "path_to_apple_pay.cer";
FileInputStream fis = new FileInputStream(filename);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) cf.generateCertificate(fis);
fis.close();
return certificate;
}
private static PublicKey loadPublicKey() throws Exception {
String publicKeyBase64 = "my_public_key";
byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyBase64);
KeyFactory keyFactory = KeyFactory.getInstance("EC");
return keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes));
}
}
I always get javax.crypto.AEADBadTagException: Tag mismatch! error.
Will be glad for any help.
What do i do wrong?
Maybe someone has this problem also or can help me?
I tried a few ways to load private and public keys, tried a few ways to generate symmetric keys and decrypt data, but result is always the same: javax.crypto.AEADBadTagException: Tag mismatch! error
Ismail Tahirli is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.