JavaCard上的HMAC-SHA实现

时间:2015-10-20 09:32:20

标签: hmac javacard

我正在尝试在JavaCard智能卡上实施RFC-2104 HMAC。我似乎得到了错误的结果。我是否遗漏或误解了RFC-2104中的某些内容?

代码:

public class HMACSHA {

private MessageDigest md = null;
private static final byte IPAD = (byte) 0x36;
private static final byte OPAD = (byte) 0x5c;
private byte[] secretIpad;
private byte[] secretOpad;
private byte[] secretKey;
private short outSize = 20;
private short blockSize = 64;
private short ctr = 0;

/**
 * Init HMAC algo from RFC-2104. Setup the blocksize of the algo. Default SHA-1.
 *
 * @param hashAlgo
 * @param hmacKey
 */
public void init(byte hashAlgo, byte[] hmacKey) {
    md = MessageDigest.getInstance(hashAlgo, false);

    if (hashAlgo == 4) {
        outSize = (short) 32; // SHA-256
    } else if (hashAlgo == 5) {
        outSize = (short) 48; // SHA-384            
        blockSize = (short) 128;
    } else if (hashAlgo == 6) {
        outSize = (short) 64; // SHA-512            
        blockSize = (short) 128;
    }

    secretIpad = JCSystem.makeTransientByteArray((short) blockSize, JCSystem.CLEAR_ON_RESET);
    secretOpad = JCSystem.makeTransientByteArray((short) blockSize, JCSystem.CLEAR_ON_RESET);
    secretKey = JCSystem.makeTransientByteArray((short) blockSize, JCSystem.CLEAR_ON_RESET);

    // Block size == key size. Adjust key.
    if ((short) hmacKey.length > blockSize) {
        md.reset();
        md.doFinal(hmacKey, (short) 0, (short) hmacKey.length, secretKey, (short) 0);
    } else {
        ArrayLogic.arrayCopyRepack(hmacKey, (short) 0, (short) hmacKey.length, secretKey, (short) 0);
    }

    // Setup IPAD & OPAD secrets
    for (ctr = (short) 0; ctr < blockSize; ctr++) {
        secretIpad[ctr] = (byte) (secretKey[ctr] ^ IPAD);
        secretOpad[ctr] = (byte) (secretKey[ctr] ^ OPAD);
    }
    ctr = (short) 0;
}

public void doFinal(byte[] msg, short offset, short length, byte[] workBuff, short workOffset, byte[] outMsg, short outOffset) {
    if (md != null) {
        // hash(i_key_pad ∥ message)
        md.reset();
        ArrayLogic.arrayCopyRepack(secretIpad, (short) 0, (short) secretIpad.length, workBuff, workOffset);
        ArrayLogic.arrayCopyRepack(msg, offset, length, workBuff, (short) (workOffset + secretIpad.length));
        md.doFinal(workBuff, workOffset, (short) (secretIpad.length + length), outMsg, outOffset);

        //hash(o_key_pad ∥ i_pad-hashed)
        md.reset();
        ArrayLogic.arrayCopyRepack(secretOpad, (short) 0, (short) secretOpad.length, workBuff, workOffset);
        ArrayLogic.arrayCopyRepack(outMsg, outOffset, (short) outSize, workBuff, (short) (workOffset + secretOpad.length));
        md.doFinal(workBuff, workOffset, (short) (secretOpad.length + outSize), outMsg, outOffset);
    }
}

}

用法是在主applet类中设置足够大小的缓冲区,然后调用如下例子:

byte[] hmacBuff = JCSystem.makeTransientByteArray((short) 128, JCSystem.CLEAR_ON_RESET);
hmac.init(MessageDigest.ALG_SHA_256, hmacKey);
hmac.doFinal(incomingMsg, (short) 0, (short) incomingMsg.length, hmacBuff, (short) 0, outgoingMsg, (short) 0);

我使用以下参数对标准Java实现进行了双重检查:

HMAC Key Bytes(16):8A560AB02C32377FE3D1BEABE666A19B
HMAC Challenge Bytes(16):8B4F35ADB59D27ABFE95A3CAAB0B613B
HMAC Result Bytes(32):646B96BA38B73847D080E25F843C1E1DE3E8D973DBE6AFC6D402604554E7A7F6

卡的结果是5CB05D1B2CD3F711A853F7166366246743C58509E84D6B8B6C37FF00D6F07619。假设HMAC密钥在JavaCard和示例桌面应用程序中正确同步。我在HMAC源代码中遗漏了什么吗?

1 个答案:

答案 0 :(得分:4)

这些行导致了这个问题:

if (hashAlgo == 4) {
        outSize = (short) 32; // SHA-256
        //!!! missing: blockSize = (short) 64;
} else if (hashAlgo == 5) {
        outSize = (short) 48; // SHA-384            
        blockSize = (short) 128;
} else if (hashAlgo == 6) {
        outSize = (short) 64; // SHA-512            
        blockSize = (short) 128;
}

对于SHA-256,blockSize保持不变,因此SHA-384或SHA-512之后的SHA-256继承了错误的值(128而不是64)。

还有一些事情需要改进:

  • outSize不是必需的,请改用md.getLength()outSize不能存储在持久性内存中,而是经常重写。这也适用于blockSize
  • 每次调用MessageDigest时都不会创建init的新实例,您将耗尽持久性内存。在构造函数中创建所需的所有实例。
  • ctr不得存储在持久性内存中。它是一个临时变量,应该移动到RAM。你经常重写这个变量,它可能会损坏你的卡。
  • ArrayLogic.arrayCopyRepack应替换为标准Java Card库中的Util.arrayCopyNonAtomic - 您的applet将更快,更便携
  • 只需创建一次所需的所有缓冲区,而不是每次调用initsecretKeysecretIpadsecretOpad)。顺便说一下,你可以只有一个缓冲区,只保留偏移量......
  • 仅在必要时致电md.reset(),您不必每次都在md.doFinal(...)
  • 之前致电

最后(明显)注意:如果您的卡支持,请始终使用Signature.ALG_HMAC_SHA_XXX。它将比你自己在Java Card中实现的任何解决方案快得多。