amportugal
amportugal

Reputation: 124

Storing secret key on token USB gives different key (few different bytes) when doing getKey()

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

Answers (1)

Maarten Bodewes
Maarten Bodewes

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

Related Questions