Search code examples
javaencryptioncryptographyaeshmacsha1

Wrong AES encryption


I'm trying to do a simple AES encryption tool that convert a plaintext secret to a PSKC5 XML as illustrated in RFC6030 chap. 6.1. Using the example values illustrated in it, namely:

  • secret: 3132333435363738393031323334353637383930
  • encryption key: 12345678901234567890123456789012
  • MAC key: 1122334455667788990011223344556677889900
  • IV for HOTP: 000102030405060708090a0b0c0d0e0f
  • IV for MAC: 11223344556677889900112233445566

But the IV for the AES digest is too long and MAC class does not accept an IV for making the signature. I tried to ignore the MAC IV and convert the HOTP IV as

{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}

but I'm getting different results from the ones illustrated in the example. This is the class I'm using:

import java.math.BigInteger;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import com.sun.org.apache.xml.internal.security.utils.Base64;

public class Encrypt {
    private byte[] ivMac, ivHotp, sharedKey, macKey;
    private SecretKey secretSharedKey, secretMacKey;
    private Cipher cipher;
    private AlgorithmParameterSpec cipherParamSpec;
    private Mac mac;

    public Encrypt(String encryptionKey, String macKey, String ivMac, String ivHotp, String csvFilePath) throws FileNotFoundException {
        try {
            this.ivMac = hexStr2Bytes(ivMac);
            this.ivHotp = hexStr2Bytes(ivHotp);
            this.sharedKey = hexStr2Bytes(encryptionKey);
            this.macKey = hexStr2Bytes(macKey);         
            this.cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            this.mac = Mac.getInstance("HmacSHA1");
            this.mac.init(new SecretKeySpec(this.macKey, "HmacSHA1"));          
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public void encryptSingleSecret(String serialNo, String secret) {
        try {
            byte[] secretBytes = hexStr2Bytes(secret);
            String macKeyString = Base64.encode(macKey);
            cipherParamSpec = new IvParameterSpec(this.ivHotp);
            cipher.init(Cipher.ENCRYPT_MODE, new     SecretKeySpec(this.sharedKey, "AES"), cipherParamSpec);
            byte[] secretDigested = cipher.doFinal(secretBytes);
            cipherParamSpec = new IvParameterSpec(this.ivMac);
            cipher.init(Cipher.ENCRYPT_MODE, new     SecretKeySpec(this.sharedKey, "AES"), cipherParamSpec);
            byte[] macDigested = cipher.doFinal(macKey);
            String MacEncrypted = Base64.encode(macDigested);
            String SecretEncrypted = Base64.encode(secretDigested);
            String MacValue =     Base64.encode(mac.doFinal(secretDigested));
            System.out.println("MAC Key: " + MacEncrypted);
            System.out.println("Secret: " + SecretEncrypted);
            System.out.println("MAC ValueMac: " + MacValue);
            return;
        } catch (Exception ex) {
            ex.printStackTrace();
            return;
        }
    }

    /**
     * From RFC 6238 App. A
     * @param hex
     * @return
     */
    public byte[] hexStr2Bytes(String hex) {
        // Adding one byte to get the right conversion
        // Values starting with "0" can be converted
        byte[] bArray = new BigInteger("10" + hex,16).toByteArray();

        // Copy all the REAL bytes, not the "first"
        byte[] ret = new byte[bArray.length - 1];
        for (int i = 0; i < ret.length; i++)
            ret[i] = bArray[i+1];
        return ret;
    }
}

What am I getting wrong?

UPDATE: The two IVs are the IVs I have to use to encrypt the secret and the MAC key respectively and they are hex arrays, but still my results are different from what the example shows. Any clue?


Solution

  • I think you are little confused about what gets encrypted, encoded, etc. I have added a simple method concatByteArrays, and then I've rewritten your encryptSingleSecret method (see below). One thing you missed is that you have to prepend the IV that was used to encrypt something to the resulting cipher, and then base64 encode the whole shebang. Also your understanding of the MAC portion was a little off.

    private byte [] concatByteArrays(byte []a, byte [] b ) {
        byte [] result = new byte[a.length + b.length];
        System.arraycopy(a, 0, result, 0, a.length);
        System.arraycopy(b, 0, result, a.length, b.length);
        return result;
    }
    
    public void encryptSingleSecret(String serialNo, String secret) {
        try {
            byte[] secretBytes = hexStr2Bytes(secret);
    
            // First, encrypt the MAC key
    
            cipherParamSpec = new IvParameterSpec(this.ivMac);
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(this.sharedKey, "AES"),
                    cipherParamSpec);
            byte[] encryptedMacKey = cipher.doFinal(this.macKey);
    
            // Now, prepend the IV used to encrypt the mac key to the cipher
    
            byte [] toBeBase64Encoded = concatByteArrays(this.ivMac, encryptedMacKey);
    
            // Now base64-encode the result and print it out. This is for the
            // <MACKey> element
    
            System.out.println("<MACKey> <CipherValue>: " + Base64.encode(toBeBase64Encoded));
    
            // Next, encrypt the secret
    
            cipherParamSpec = new IvParameterSpec(this.ivHotp);
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(this.sharedKey, "AES"),
                    cipherParamSpec);
            byte[] encryptedSecret = cipher.doFinal(secretBytes);
    
            // Now, prepend the IV used to encrypt the secret to the cipher
    
            toBeBase64Encoded = concatByteArrays(this.ivHotp, encryptedSecret); 
    
            // Now base64-encode the result and print it out. This is for the
            // <Data> element
    
            System.out.println("<Data><Secret><CipherValue>: " + Base64.encode(toBeBase64Encoded));
    
            // Finally, compute the MAC over the encrypted value
    
            byte [] macValue = this.mac.doFinal(toBeBase64Encoded);
    
            // Base64-encode the result and print it out. This is for the
            // ValueMAC element
    
            System.out.println("<Data><Secret><ValueMAC>: " + Base64.encode(macValue));
    
            return;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }