SoftwareVerde/java-dukpt

INFO: Decrypting according to X9.24-2009

Closed this issue · 3 comments

Really good library - thought I'd post some info on using it, in case it is useful to others. I used the library with Castle Saturn 1000 devices which support the above standard.

There were two extra things I had to do to get decryption working. Not sure if your library supports a slightly different variation?

  1. Change the data variant bitmask to 0000000000FF00000000000000FF0000
  2. Derive a session key after calling Dukpt.computeKey

    try {

        byte[] encryptedBytes = Hex.decode(encryptedHexText);

        // Get the IPEK from the KSN and then use it to get the encryption key, and a data variant bitmask is applied
        byte[] derivedMaskedKey = Dukpt.computeKey(this.baseDerivationKeyBytes, Hex.decode(ksnHexText));

        // Get the encryption key, which is sometimes called a session key
        byte[] encryptionKey = derivedMaskedKey;
        encryptionKey = this.getSessionKey(derivedMaskedKey);

        // Do the decryption
        byte[] decryptedBytes = Dukpt.decryptTripleDes(encryptionKey, encryptedBytes);
        return new String(decryptedBytes);
    }
    catch (Exception ex) {
        throw new RuntimeException("Problem encountered decrypting data", ex);
    }
}

private byte[] getSessionKey(byte[] derivedMaskedKey) throws Exception {

    String derivedKeyHex = Hex.toHexString(derivedMaskedKey);
    String left = derivedKeyHex.substring(0, 16);
    String right = derivedKeyHex.substring(16);

    byte[] leftEncrypted = Dukpt.encryptTripleDes(derivedMaskedKey, Hex.decode(left));
    byte[] rightEncrypted = Dukpt.encryptTripleDes(derivedMaskedKey, Hex.decode(right));
    return Hex.decode(Hex.toHexString(leftEncrypted) + Hex.toHexString(rightEncrypted));
}

There is a really good IDTech write up on deriving a key here:
https://idtechproducts.com/support/technical-blog/id/how-to-decrypt-credit-card-data-part-ii/

And an accompanying tool (you can do view source to get the Javascript encryption + decryption source code and see the createDataKeyHex function):
https://idtechproducts.com/hosted-files/tools/encryptiondecryptiontool.html

Not sure if you'd like to add support for this type of device?. If it helps I could add a minor pull request? It might be useful to link to the above article + tool from your README file also?

Hi Gary,

Firstly, thanks for taking the time to let us know about what you had to change to get this working for your purposes. It appears that when we built this we only had a use for the PIN key variant so that was all we really coded for.

After reviewing the code and documentation you included, I think it makes a lot of sense to allow for basically all of the options that were laid out. I made some changes in the branch tech/key-variants that I think should get you what you're looking for while also keeping things about as generic as possible. In particular, I think the new class DukptVariant should allow your code-block above to become:

try {
    byte[] encryptedBytes = Hex.decode(encryptedHexText);

    DukptVariant dukptVariant = new DukptVariant(Dukpt.KEY_REGISTER_BITMASK, Dukpt.DATA_VARIANT_BITMASK);

    // Get the IPEK from the KSN and then use it to get the encryption key, and a data variant bitmask is applied
    byte[] derivedMaskedKey = dukpt.computeKey(this.baseDerivationKeyBytes, Hex.decode(ksnHexText));

    // Get the encryption key, which is sometimes called a session key
    byte[] encryptionKey = dukpt.toDataKey(derivedMaskedKey);

    // Do the decryption
    byte[] decryptedBytes = Dukpt.decryptTripleDes(encryptionKey, encryptedBytes);
    return new String(decryptedBytes);
}
catch (Exception ex) {
    throw new RuntimeException("Problem encountered decrypting data", ex);
}

If you can, please take a look and let me know if this accomplishes everything you need. Assuming you agree that it does I'll merge it in and make a release but I wanted to check with you before doing so just in case I missed or misunderstood something.

Thanks Andrew - I integrated the feature branch and tested it end to end - it works fine and our server side code no longer needs to change the java-dukpt code. Feel free to merge it.

Currently I'm having some discussions with Castle on client side settings for AES-DUKPT, which feels like the most cutting edge option and the one we'll choose for our company's devices.

It looks like your library will work fine for that option also - I will post back once I've verified this.

Good to hear! Thanks for re-testing so quickly.

The AES option definitely sounds ideal to me, though I'm curious to see whether the CBC mode that we currently have the library specifying is sufficient. Regardless, since you were able to get things working with the changes in the branch I'm planning to make a new release and close this issue.

If you do run into any issues with rolling out AES-DUKPT feel free to open another issue and we'll take a look (though if it works, please let us know in the comments here). In the meantime, I think we're somewhat debating whether the encryption/decryption code actually belongs in this library aside from where it's used internally. That said, it also wouldn't be difficult to allow for other encryptions modes to be passed in, so for now we're leaving it as-is.