JavaCard 上的 HMAC-SHA 实现

HMAC-SHA implementation on 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);
    }
}

}

用法是在主小程序中设置足够大的缓冲区或某处class,然后像下面的例子一样调用:

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 源代码中是否缺少某些内容?

这些行导致了问题:

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
  • 不要在每次调用 init 时创建 MessageDigest 的新实例,您将 运行 永久内存不足。在构造函数中创建您需要的所有实例。
  • ctr 不得存储在持久内存中。它是一个临时变量,应移至 RAM。你经常重写这个变量,它会损坏你的卡。
  • ArrayLogic.arrayCopyRepack 应替换为标准 Java 卡片库中的 Util.arrayCopyNonAtomic - 您的小程序会更快、更便携
  • 只创建一次所需的所有缓冲区,而不是每次调用 init (secretKey,secretIpad,secretOpad)。顺便说一句,你可以只有一个缓冲区并只保留偏移量...
  • 只在必要时才调用md.reset(),不必每次都在md.doFinal(...)
  • 之前调用

最后(显而易见的)注意事项:如果您的卡支持,请始终使用 Signature.ALG_HMAC_SHA_XXX。它比您可以在 Java Card 中自行实施的任何解决方案都要快得多。