NIST SP 800-56A级联/单步密钥派生函数的现有实现?

Lai*_*Chu 13 c++ java security cryptography cryptoapi

有谁知道NIST SP 800-56A级联密钥派生函数/ CONCAT KDF(最好用Java)的任何现有实现?

密钥推导函数记录在NIST出版物的第5.8.1节中:使用离散对数密码学的双智能密钥建立方案的建议

链接到这里:http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf

微软的CNG 在这里有一个实现,但是如果你比较微软实现的功能,与NIST SP 800-56A中记录的参数相比,它们不相符,并且微软的实现是无法使用的.我试图用C++实现一个示例程序,但是我无法匹配这些参数.

有人能够尝试实现它或知道任何现有的实现吗?

我正在寻找一种能够证明为什么它对NIST规范是准确的实现.我已经看到了一些实现,我觉得它们对NIST规范不准确(缺少参数,无效的逻辑流程等).

如果您可以自己实现它,我总是很乐意分享我自己的源代码进行辩论.谢谢!这对开源社区是一个很好的贡献!

编辑:

感谢@Rasmus Faber,我终于可以结束这个问题,并希望回答其他人对我的同样问题.

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

ConcatKeyDerivationFunction.java

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * 
 * Implementation of Concatenation Key Derivation Function<br/>
 * http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf
 *
 */

public class ConcatKeyDerivationFunction {

    private static final long MAX_HASH_INPUTLEN = Long.MAX_VALUE;
    private static final long UNSIGNED_INT_MAX_VALUE = 4294967295L;
    private static MessageDigest md;

    public ConcatKeyDerivationFunction(String hashAlg) throws NoSuchAlgorithmException {
        md = MessageDigest.getInstance(hashAlg);
    }

    public byte[] concatKDF(byte[] z, int keyDataLen, byte[] algorithmID, byte[] partyUInfo, byte[] partyVInfo, byte[] suppPubInfo, byte[] suppPrivInfo) {
        int hashLen = md.getDigestLength() * 8;

        if (keyDataLen % 8 != 0) {
            throw new IllegalArgumentException("keydatalen should be a multiple of 8");
        }

        if (keyDataLen > (long) hashLen * UNSIGNED_INT_MAX_VALUE) {
            throw new IllegalArgumentException("keydatalen is too large");
        }

        if (algorithmID == null || partyUInfo == null || partyVInfo == null) {
            throw new NullPointerException("Required parameter is null");
        }

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            baos.write(algorithmID);
            baos.write(partyUInfo);
            baos.write(partyVInfo);
            if (suppPubInfo != null) {
                baos.write(suppPubInfo);
            }
            if (suppPrivInfo != null) {
                baos.write(suppPrivInfo);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

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

    private byte[] concatKDF(byte[] z, int keyDataLen, byte[] otherInfo) {
        keyDataLen = keyDataLen / 8;
        byte[] key = new byte[keyDataLen];

        int hashLen = md.getDigestLength();
        int reps = keyDataLen / hashLen;

        if (reps > UNSIGNED_INT_MAX_VALUE) {
            throw new IllegalArgumentException("Key derivation failed");
        }

        int counter = 1;
        byte[] counterInBytes = intToFourBytes(counter);

        if ((counterInBytes.length + z.length + otherInfo.length) * 8 > MAX_HASH_INPUTLEN) {
            throw new IllegalArgumentException("Key derivation failed");
        }

        for (int i = 0; i <= reps; i++) {
            md.reset();
            md.update(intToFourBytes(i + 1));
            md.update(z);
            md.update(otherInfo);

            byte[] hash = md.digest();
            if (i < reps) {
                System.arraycopy(hash, 0, key, hashLen * i, hashLen);
            } else {
                System.arraycopy(hash, 0, key, hashLen * i, keyDataLen % hashLen);
            }
        }
        return key;
    }

    private 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

@Rasmus Faber:谢谢你的努力.我完全赞同上述代码.我对上面的代码所做的是添加代码以按照NIST规范的要求执行验证.

另外,我修复了一个错误,其中传入的keyDataLen旨在指定以位为单位的长度,但它被视为以字节为单位的长度.因此,生成的密钥最终变大了8倍.

通过keyDataLen = keyDataLen/8;在第二种方法的第一行中添加一行来解决这个问题.

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

Ras*_*ber 8

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

    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;
}
Run Code Online (Sandbox Code Playgroud)