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 中自行实施的任何解决方案都要快得多。
我正在尝试在 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 中自行实施的任何解决方案都要快得多。