Hi, Do you know someone about java example how to decrypt encryptedData in VTS API?
Thanks!
Hey @MichalVas,
I'll take a look and get back to you soon.
Hey @MichalVas - can you confirm you have access to the VTS API - what is your project name?
To decrypt the encryptedData you need to have the apikey and shared secret for the application(appID) you are using.
JWE/JWS specification requires BASE64URL encoding with NO padding.
General approach for JSON Web Encryption/Decryption is using API key/Shared Secret
(Refer to complete specification for deeper overview of JWE – https://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-40)
For more information on how to decrypt data, please visit this link - https://developer.visa.com/capabilities/vts/docs#security_and_authentication_requirements
If you need more insights please share the details with account manager from Visa associated with your organisation, they could be able to help you in details.
One way since Java 1.8, is to use java.util.Base64.Decoder
public byte[] decode(String src)
Decodes a Base64 encoded String into a newly-allocated byte array using the Base64 encoding scheme.
An invocation of this method has exactly the same effect as invoking decode(src.getBytes(StandardCharsets.ISO_8859_1))
Parameters:
src - the string to decode
Returns:
A newly-allocated byte array containing the decoded bytes.
Throws:
IllegalArgumentException - if src is not in valid Base64 scheme
Another way is to use org.apache.commons.codec.binary.Base64.
decode (source): Decodes an "encoded" Object and returns a "decoded" Object.
decode
Object decode(Object source)
throws DecoderException
Decodes an "encoded" Object and returns a "decoded" Object. Note that the implementation of this interface will try to cast the Object parameter to the specific type expected by a particular Decoder implementation. If a ClassCastException occurs this decode method will throw a DecoderException.
Parameters:
source - the object to decode
Returns:
a 'decoded" object
Throws:
DecoderException - a decoder exception can be thrown for any number of reasons. Some good candidates are that the parameter passed to this method is null, a param cannot be cast to the appropriate type for a specific encoder.
Also, Visa Checkout provides code in several languages that you can use to decrypt the payload
using the prerequisite fields. An example in Java is below:
public static byte[] decryptPayload(byte[] key, byte[] wrappedKey, byte[] payload) throws GeneralSecurityException { byte[] unwrappedKey = decrypt(key, wrappedKey); return decrypt(unwrappedKey, payload); } String sharedSecret = "YOUR_SHARED_SECRET"; String encKey = "YOUR_ENC_KEY"; String encPaymentData = "YOUR_ENC_PAYMENT"; decryptPayload(sharedSecret.getBytes(), encKey.getBytes(), encPaymentData.getBytes());
Java Decryption Example
The following Java code, using the Bouncy Castle API and bcprov-jdk15on-149.jar provides an example of decrypting the payload.
Note: Encryption in Java requires Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction
Policy files.
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
private static final String HASH_ALGORITHM = "SHA-256";
private static final String HMAC_ALGORITHM = "HmacSHA256";
private static final int IV_LENGTH = 16, HMAC_LENGTH = 32;
private static final Charset utf8 = Charset.forName("UTF-8");
private static final Provider bcProvider;
static {
bcProvider = new BouncyCastleProvider();
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(bcProvider);
}
}
private static byte[] decrypt(byte[] key,byte[] data) throws GeneralSecurityException{
byte[] decodedData = Base64.decode(data);
if (decodedData == null || decodedData.length <= IV_LENGTH) {
throw new RuntimeException("Bad input data.");
}
byte[] hmac = new byte[HMAC_LENGTH];
System.arraycopy(decodedData, 0, hmac, 0, HMAC_LENGTH);
if (!Arrays.equals(hmac,
hmac(key, decodedData, HMAC_LENGTH, decodedData.length - HMAC_LENGTH))) {
throw new RuntimeException("HMAC validation failed.");
}
byte[] iv = new byte[IV_LENGTH];
System.arraycopy(decodedData, HMAC_LENGTH, iv, 0, IV_LENGTH);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM, bcProvider);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(hash(key), "AES"),
new IvParameterSpec(iv));
return cipher.doFinal(decodedData, HMAC_LENGTH + IV_LENGTH,
decodedData.length - HMAC_LENGTH - IV_LENGTH);
}
private static byte[] hash(byte[] key) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance(HASH_ALGORITHM);
md.update(key);
return md.digest();
}
private static byte[] hmac(byte[] key, byte[] data, int offset, int length)
throws GeneralSecurityException {
Mac mac = Mac.getInstance(HMAC_ALGORITHM, bcProvider);
mac.init(new SecretKeySpec(key, HMAC_ALGORITHM));
mac.update(data, offset, length);
return mac.doFinal();
}
Hey @MichalVas,
Do you have access to the Visa Token Service API? If not, we cannot provide you with any documentation for the product. Typically, the merchants handle decryption, but if you do, in fact, have access to the Visa Token Service API then I can escalate your inquiry to confirm if we have documentation on decryption. Can you please confirm so we can proceed accordingly?