Java NIST SP 800-56A串联/单步密钥派生功能的现有实现?

Java NIST SP 800-56A串联/单步密钥派生功能的现有实现?,java,c++,security,cryptography,cryptoapi,Java,C++,Security,Cryptography,Cryptoapi,有人知道NIST SP 800-56A串联密钥派生函数/CONCAT KDF(最好是Java)的任何现有实现吗 NIST出版物第5.8.1节:使用离散对数加密的成对密钥建立方案建议中记录了密钥推导函数 链接此处: 微软的CNG有一个实现方案,但如果将微软实现的功能与NIST SP 800-56A中记录的参数进行比较,则两者不符,微软的实现方案不可用。我尝试在C++中实现一个示例程序,但是我不能匹配参数。 是否有人能够尝试实现它或知道任何现有的实现 我正在寻找一种能够证明其符合NIST规范的实施方

有人知道NIST SP 800-56A串联密钥派生函数/CONCAT KDF(最好是Java)的任何现有实现吗

NIST出版物第5.8.1节:使用离散对数加密的成对密钥建立方案建议中记录了密钥推导函数

链接此处:

微软的CNG有一个实现方案,但如果将微软实现的功能与NIST SP 800-56A中记录的参数进行比较,则两者不符,微软的实现方案不可用。我尝试在C++中实现一个示例程序,但是我不能匹配参数。 是否有人能够尝试实现它或知道任何现有的实现

我正在寻找一种能够证明其符合NIST规范的实施方案。我已经看到了一些实现,我觉得它们不符合NIST规范(缺少参数、无效逻辑流等)

如果您可以自己实现它,我总是很高兴分享我自己的源代码以供讨论。谢谢这将是对开源社区的一个很好的贡献

编辑:

多亏了@Rasmus Faber,我终于可以结束这个问题了,希望能回答和我一样的问题

以下是我根据@Rasmus Faber和我的原始代码编辑的代码:

concatkeydrivationfunction.java

import java.io.ByteArrayOutputStream;
导入java.io.IOException;
导入java.security.MessageDigest;
导入java.security.NoSuchAlgorithmException;
/**
* 
*级联密钥派生函数的实现
* http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf * */ 公共类ConcatKeyDerivationFunction{ 私有静态最终long MAX_HASH_INPUTLEN=long.MAX_值; 私有静态最终长无符号_INT_MAX_值=4294967295L; 私有静态消息摘要md; public ConcatKeyDerivationFunction(字符串hashAlg)抛出NoSuchAlgorithmException{ md=MessageDigest.getInstance(hashAlg); } 公共字节[]concatKDF(字节[]z,int keyDataLen,字节[]algorithmID,字节[]partyUInfo,字节[]partyVInfo,字节[]suppPubInfo,字节[]suppPrivInfo){ int hashLen=md.getDigestLength()*8; 如果(keyDataLen%8!=0){ 抛出新的IllegalArgumentException(“keydatalen应该是8的倍数”); } if(keyDataLen>(长)hashLen*无符号整数最大值){ 抛出新的IllegalArgumentException(“keydatalen太大”); } if(algorithmID==null | | partyUInfo==null | | partyVInfo==null){ 抛出新的NullPointerException(“必需参数为null”); } ByteArrayOutputStream bas=新的ByteArrayOutputStream(); 试一试{ 写入(算法ID); 编写(partyUInfo); 编写(PartyInfo); 如果(suppPubInfo!=null){ 编写(suppPubInfo); } if(supplivinfo!=null){ 编写(提供信息); } }捕获(IOE异常){ 抛出新的运行时异常(e); } 字节[]otherInfo=baos.toByteArray(); 返回concatKDF(z、keyDataLen、otherInfo); } 专用字节[]concatKDF(字节[]z,int-keyDataLen,字节[]otherInfo){ keyDataLen=keyDataLen/8; 字节[]键=新字节[keyDataLen]; int hashLen=md.getDigestLength(); int reps=keyDataLen/hashLen; 如果(重复>无符号输入最大值){ 抛出新的IllegalArgumentException(“密钥派生失败”); } int计数器=1; 字节[]计数器InBytes=intToFourBytes(计数器); if((counterInBytes.length+z.length+otherInfo.length)*8>MAX\u HASH\u INPUTLEN){ 抛出新的IllegalArgumentException(“密钥派生失败”); } 对于(int i=0;i>>24); res[1]=(字节)((i>>>16)和0xFF); res[2]=(字节)((i>>>8)和0xFF); res[3]=(字节)(i&0xFF); 返回res; } }
@拉斯姆斯·费伯:谢谢你的努力。我完全相信你的上述代码。我对上述代码所做的是添加代码,按照NIST规范的要求执行验证

此外,我还修复了一个bug,其中传入的keyDataLen用于指定以位为单位的长度,但它被视为以字节为单位的长度。因此,生成的密钥最终要大8倍

这是通过添加一行
keyDataLen=keyDataLen/8修复的


我感谢大家的支持,并希望这段代码能对开源社区有很大帮助

不要认为您可以找到其他组件,然后从中测试
验证组件实现列表中的所有组件

只有一家供应商用Java编写它们——委托


所有这些都在没有KDF的情况下进行验证:)。其余的工作由您决定。

这里是一个快速而肮脏的实现:

    public byte[] concatKDF(String hashAlg, byte[] z, int keyDataLen, byte[] algorithmID, byte[] partyUInfo, byte[] partyVInfo, byte[] suppPubInfo, byte[] suppPrivInfo) throws NoSuchAlgorithmException
{
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try {
        baos.write(algorithmID);
        baos.write(partyUInfo);
        baos.write(partyVInfo);
        baos.write(suppPubInfo);
        baos.write(suppPrivInfo);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }

    byte[] otherInfo = baos.toByteArray();
    return concatKDF(hashAlg, z, keyDataLen, otherInfo);
}

public byte[] concatKDF(String hashAlg, byte[] z, int keyDataLen, byte[] otherInfo) throws NoSuchAlgorithmException
{
    byte[] key = new byte[keyDataLen];
    MessageDigest md = MessageDigest.getInstance(hashAlg);
    int hashLen = md.getDigestLength(); 
    int reps = keyDataLen / hashLen;
    for(int i=1;i<=reps;i++){
        md.reset();
        md.update(intToFourBytes(i));
        md.update(z);
        md.update(otherInfo);
        byte[] hash = md.digest();
        if(i<reps){
            System.arraycopy(hash, 0, key, hashLen*(i-1), hashLen);
        }else{
            if(keyDataLen % hashLen == 0){
                System.arraycopy(hash, 0, key, hashLen*(i-1), hashLen);
            }else{
                System.arraycopy(hash, 0, key, hashLen*(i-1), keyDataLen % hashLen);
            }
        }
    }
    return key;
}

public byte[] intToFourBytes(int i){
    byte[] res = new byte[4];
    res[0] = (byte) (i >>> 24);
    res[1] = (byte) ((i >>> 16) & 0xFF);
    res[2] = (byte) ((i >>> 8) & 0xFF);
    res[3] = (byte) (i & 0xFF);
    return res;
}
public byte[]concatKDF(字符串hashAlg,字节[]z,int-keyDataLen,字节[]algorithmID,字节[]partyUInfo,字节[]partyVInfo,字节[]suppPubInfo,字节[]supppprivinfo)抛出NoSuchAlgorithmException
{
ByteArrayOutputStream bas=新的ByteArrayOutputStream();
试一试{
写入(算法ID);
编写(partyUInfo);
编写(PartyInfo);
编写(suppPubInfo);
编写(提供信息);
}捕获(IOE异常){
抛出新的运行时异常(e);
}
字节[]otherInfo=baos.toByteArray();
返回concatKDF(hashAlg、z、keyDataLen、otherInfo);
}
公共字节[]concatKDF(字符串hashAlg,字节[]z,int-keyDataLen,字节[]otherInf
    public byte[] concatKDF(String hashAlg, byte[] z, int keyDataLen, byte[] algorithmID, byte[] partyUInfo, byte[] partyVInfo, byte[] suppPubInfo, byte[] suppPrivInfo) throws NoSuchAlgorithmException
{
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try {
        baos.write(algorithmID);
        baos.write(partyUInfo);
        baos.write(partyVInfo);
        baos.write(suppPubInfo);
        baos.write(suppPrivInfo);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }

    byte[] otherInfo = baos.toByteArray();
    return concatKDF(hashAlg, z, keyDataLen, otherInfo);
}

public byte[] concatKDF(String hashAlg, byte[] z, int keyDataLen, byte[] otherInfo) throws NoSuchAlgorithmException
{
    byte[] key = new byte[keyDataLen];
    MessageDigest md = MessageDigest.getInstance(hashAlg);
    int hashLen = md.getDigestLength(); 
    int reps = keyDataLen / hashLen;
    for(int i=1;i<=reps;i++){
        md.reset();
        md.update(intToFourBytes(i));
        md.update(z);
        md.update(otherInfo);
        byte[] hash = md.digest();
        if(i<reps){
            System.arraycopy(hash, 0, key, hashLen*(i-1), hashLen);
        }else{
            if(keyDataLen % hashLen == 0){
                System.arraycopy(hash, 0, key, hashLen*(i-1), hashLen);
            }else{
                System.arraycopy(hash, 0, key, hashLen*(i-1), keyDataLen % hashLen);
            }
        }
    }
    return key;
}

public byte[] intToFourBytes(int i){
    byte[] res = new byte[4];
    res[0] = (byte) (i >>> 24);
    res[1] = (byte) ((i >>> 16) & 0xFF);
    res[2] = (byte) ((i >>> 8) & 0xFF);
    res[3] = (byte) (i & 0xFF);
    return res;
}
// a shared secret provided by your protocol
byte[] sharedSecret = ...
// a salt; if you don't have access to a salt, use salt-less SingleStepKdf.fromSha256() or similar
byte[] salt = ...
// other info to bind the key to the context, see the NIST spec for more detail
byte[] otherInfo = "macKey".getBytes();
byte[] keyMaterial = SingleStepKdf.fromHmacSha256().derive(sharedSecret, 32, salt, otherInfo);
SecretKey secretKey = new SecretKeySpec(keyMaterial, "AES");
(z: afc4e154498d4770aa8365f6903dc83b, L: 16, fixedInfo: 662af20379b29d5ef813e655) = f0b80d6ae4c1e19e2105a37024e35dc6
(z: a3ce8d61d699ad150e196a7ab6736a63, L: 16, fixedInfo: ce5cd95a44ee83a8fb83f34c) = 5db3455a22b65edfcfde3da3e8d724cd
(z: a9723e56045f0847fdd9c1c78781c8b7, L: 16, fixedInfo: e69b6005b78f7d42d0a8ed2a) = ac3878b8cf357976f7fd8266923e1882
(z: a07a5e8df7ee1b2ce2a3d1348edfa8ab, L: 16, fixedInfo: e22a8ee34296dd39b56b31fb) = 70927d218b6d119268381e9930a4f256

(z: 3f892bd8b84dae64a782a35f6eaa8f00, L: 02, fixedInfo: ec3f1cd873d28858a58cc39e) = a7c0
(z: 3f892bd8b84dae64a782a35f6eaa8f00, L: 36, fixedInfo: ec3f1cd873d28858a58cc39e) = a7c0665298252531e0db37737a374651b368275f2048284d16a166c6d8a90a91a491c16f
(z: 3f892bd8b84dae64a782a35f6eaa8f00, L: 68, fixedInfo: ec3f1cd873d28858a58cc39e) = a7c0665298252531e0db37737a374651b368275f2048284d16a166c6d8a90a91a491c16f49641b9f516a03d9d6d0f4fe7b81ffdf1c816f40ecd74aed8eda2b8a3c714fa0

(z: 9ce5457e4a0eecc1c8709f7ef37a32e9, L: 16, fixedInfo: ) = 7d81e7d61acc06b90984ec4145469608
(z: 6ee6c00d70a6cd14bd5a4e8fcfec8386, L: 16, salt: 532f5131e0a2fecc722f87e5aa2062cb, fixedInfo: 861aa2886798231259bd0314) = 13479e9a91dd20fdd757d68ffe8869fb
(z: cb09b565de1ac27a50289b3704b93afd, L: 16, salt: d504c1c41a499481ce88695d18ae2e8f, fixedInfo: 5ed3768c2c7835943a789324) = f081c0255b0cae16edc6ce1d6c9d12bc
(z: 98f50345fd970639a1b7935f501e1d7c, L: 16, salt: 3691939461247e9f74382ae4ef629b17, fixedInfo: 6ddbdb1314663152c3ccc192) = 56f42183ed3e287298dbbecf143f51ac

(z: 02b40d33e3f685aeae677ac344eeaf77, L: 02, salt: 0ad52c9357c85e4781296a36ca72039c, fixedInfo: c67c389580128f18f6cf8592) = be32
(z: 02b40d33e3f685aeae677ac344eeaf77, L: 36, salt: 0ad52c9357c85e4781296a36ca72039c, fixedInfo: c67c389580128f18f6cf8592) = be32e7d306d891028be088f213f9f947c50420d9b5a12ca69818dd9995dedd8e6137c710
(z: 02b40d33e3f685aeae677ac344eeaf77, L: 68, salt: 0ad52c9357c85e4781296a36ca72039c, fixedInfo: c67c389580128f18f6cf8592) = be32e7d306d891028be088f213f9f947c50420d9b5a12ca69818dd9995dedd8e6137c7104d67f2ca90915dda0ab68af2f355b904f9eb0388b5b7fe193c9546d45849133d

(z: 2c2438b6321fed7a9eac200b91b3ac30, L: 56, salt: 6199187690823def2037e0632577c6b1, fixedInfo: ) = b402fda16e1c2719263be82158972c9080a7bafcbe0a3a6ede3504a3d5c8c0c0e00fe7e5f6bb3afdfa4d661b8fbe4bd7b950cfe0b2443bbd
(z: 0ffa4c40a822f6e3d86053aefe738eac, L: 64, salt: 6199187690823def2037e0632577c6b1, fixedInfo: ) = 0486d589aa71a603c09120fb76eeab3293eee2dc36a91b23eb954d6703ade8a7b660d920c5a6f7bf3898d0e81fbad3a680b74b33680e0cc6a16aa616d078b256
(z: a801d997ed539ae9aa05d17871eb7fab, L: 08, fixedInfo: 03697296e42a6fdbdb24b3ec) = 1a5efa3aca87c1f4
(z: e9624e112f9e90e7bf8a749cf37d920c, L: 16, fixedInfo: 03697296e42a6fdbdb24b3ec) = ee93ca3986cc43516ae4e29fd7a90ef1