Reputation: 124
I'm trying to store a symmetric key (SecretKey
which is Triple-DES key, ECB mode) on a cryptographic USB token. I use the following code to do it:
private void storeSecretKey( SecretKey secretKey, String alias ) throws StoreException {
try {
log.info("Format: " + secretKey.getFormat());
log.info("Alg: " + secretKey.getAlgorithm());
log.info("STORE KEY (bytes): " + Arrays.toString( secretKey.getEncoded()));
log.info("STORE KEY: " + ArrayUtils.convertToHexString( secretKey.getEncoded(), false, false));
myKeyStore.setKeyEntry(alias, secretKey, tokenPIN.toCharArray(), null);
myKeyStore.store(null);
Key key = myKeyStore.getKey(alias, tokenPIN.toCharArray());
log.info("Format: " + key.getFormat());
log.info("Alg: " + key.getAlgorithm());
log.info("FINAL KEY (bytes): " + Arrays.toString( key.getEncoded()));
log.info("FINAL KEY: " + ArrayUtils.convertToHexString( key.getEncoded(), false, false));
}
catch ( KeyStoreException e ) {
throw new StoreException( "Unable to store encryption key", e );
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnrecoverableEntryException e) {
e.printStackTrace();
}
}
And I get the following logging:
INFO: Format: RAW
INFO: Alg: DESede
INFO: STORE KEY (bytes): [87, -81, -89, -62, 5, -116, -46, 111, -85, -52, -28, -85, -26, -57, -26, -58, -66, -52, -16, 30, 89, -45, 61, -86]
INFO: STORE KEY: 57afa7c2058cd26fabcce4abe6c7e6c6beccf01e59d33daa
INFO: Format: RAW
INFO: Alg: DESede
INFO: FINAL KEY (bytes): [87, -82, -89, -62, 4, -116, -45, 110, -85, -51, -27, -85, -26, -57, -26, -57, -65, -51, -15, 31, 88, -45, 61, -85]
INFO: FINAL KEY: 57aea7c2048cd36eabcde5abe6c7e6c7bfcdf11f58d33dab
The key that is stored on the token (STORE KEY) and the key that I get using getKey()
(FINAL KEY) are different. How can it be?
Please inform me if you need any other information it might be missing.
Thank you.
Upvotes: 4
Views: 189
Reputation: 93978
Your USB token simply adjusts the parity of the 3DES key. Each lower end bit of each byte is a parity bit for (triple) DES.
It needs to be set if the addition of the 7 highest bits is an even number, and unset if the number is odd. In the end each byte should have odd parity if you add all the bits together. So the first byte with value 0x57
or 0b0101011_1
and has 4 bits in the highest positions so the final bit has to be 1
- but it already is set, so no adjustment is necessary. The second byte, 0xAF
or 0b1010111_1
has 5 bits in the highest position, so the last bit needs to be 0
. This is not the case, so it is adjusted to 0b1010111_0
or 0xAE
.
If you want to have the same value then you can construct your (triple) DES keys using a SecretKeyFactory
instead of using a random number generator directly. The SecretKeyFactory
will also adjust the parity for you - no need to program it yourself. I'd recommend this as other implementations could reject bytes where the number of bits is even (although generally the bits are adjusted or ignored). As indicated by James, the lowest bits are not used by (triple) DES during encryption / decryption.
This will create correctly coded triple DES keys (168 bit effective, 192 bits encoded). These are also called DES ABC keys as all three DES keys are different (with a very high probability).
public static SecretKey generate192Bit3DESKey() {
KeyGenerator keyGen;
try {
keyGen = KeyGenerator.getInstance("DESede");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("DESede functionality is required for Java, but it is missing");
}
// NOTE: this is the effective key size excluding parity bits
// use 112 for two key (ABA) triple DES keys (not recommended)
keyGen.init(168);
// this does adjust parity
SecretKey desABCKey = keyGen.generateKey();
return desABCKey;
}
If your data needs adjusting afterwards, you can use this (with only a for loop, no additional branching):
public static byte[] adjustDESParity(final byte[] keyData) {
for (int i = 0; i < keyData.length; i++) {
// count the bits, and XOR with 1 if even or 0 if already odd
keyData[i] ^= (Integer.bitCount(keyData[i]) % 2) ^ 1;
}
return keyData;
}
Upvotes: 4