Java 从已编码私钥的字节中提取算法ID

Java 从已编码私钥的字节中提取算法ID,java,cryptography,asn.1,pkcs#8,Java,Cryptography,Asn.1,Pkcs#8,是否可以仅从编码字节数组创建私钥,而不事先知道算法? 所以在某种程度上,这是一种扭曲,答案中没有提到 假设我以这种方式生成了一对密钥: KeyPair KeyPair=KeyPairGenerator.getInstance(“RSA”).generateKeyPair();//可以是“EC”而不是“RSA” 字符串privateKeyB64=Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()); 将Priva

是否可以仅从编码字节数组创建
私钥
,而不事先知道算法?

所以在某种程度上,这是一种扭曲,答案中没有提到

假设我以这种方式生成了一对密钥:

KeyPair KeyPair=KeyPairGenerator.getInstance(“RSA”).generateKeyPair();//可以是“EC”而不是“RSA”
字符串privateKeyB64=Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
将PrivateKeyToSafeLocation写入privateKeyB64;
要从base64编码字节中获取
PrivateKey
,我可以这样做,但我必须事先了解算法系列:

String privateKeyB64=readPrivateKeyFromSafeLocation();
EncodedKeySpec EncodedKeySpec=新的PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyB64));
byte[]encodedKeyBytes=encodedKeySpec.getEncoded();
字符串algorithmFamily=“RSA”//这可以从encodedKeyBytes推断出来吗?
PrivateKey=KeyFactory.getInstance(algorithmFamily).generatePrivate(EncodeKeySpec);
不幸的是
encodedKeySpec.getAlgorithm()
返回
null

我非常确定算法ID实际上是以PKCS#8格式在这些字节中指定的,但我不确定如何读取ASN.1编码

我可以从这些字节中可靠地“嗅探”算法ID吗?

只支持RSA和EC(JRE支持的算法,不需要额外的提供者)是可以的

为了了解我想要的是什么,这里有一个尝试,似乎是经验性的:

private static final byte[]EC_ASN1_ID={42,-122,72,-50,61,2,1};
私有静态最终字节[]RSA_ASN1_ID={42,-122,72,-122,-9,13,1,1,1};
专用静态最终int EC_ID_偏移=9;
私有静态最终整数RSA_ID_OFFSET=11;
私有静态字符串sniffAlgorithmFamily(字节[]键字节){
if(Arrays.equals(Arrays.copyOfRange(keyBytes,EC_ID_OFFSET,EC_ID_OFFSET+EC_ASN1_ID.length),EC_ASN1_ID)){
返回“EC”;
}
if(Arrays.equals(Arrays.copyOfRange(keyBytes,RSA_ID_OFFSET,RSA_ID_OFFSET+RSA_ASN1_ID.length),RSA_ASN1_ID)){
返回“RSA”;
}
抛出新的RuntimeException(“非法密钥,这东西需要RSA或EC私钥”);
}

但我不知道这样使用是否安全。也许ID并不总是在这些偏移处。也许它们可以用其他方式编码…

正如James在评论中所建议的那样,尝试每一种受支持的算法都会更安全地工作

可以动态获取此类算法的列表:

Set<String> supportedKeyPairAlgorithms() {
    Set<String> algos = new HashSet<>();
    for (Provider provider : Security.getProviders()) {
        for (Provider.Service service : provider.getServices()) {
            if ("KeyPairGenerator".equals(service.getType())) {
                algos.add(service.getAlgorithm());
            }
        }
    }
    return algos;
}

好消息是:所有信息都以元数据的形式存在于其中的某个地方。坏消息是:对于椭圆曲线,至少相同的曲线可以用不同的方式指定,因此对于给定的椭圆曲线,没有一个规范的表示,也只有一个表示。至于解析元数据,如果您最好使用合适的ASN.1解析库,例如Bouncycastle,但如果您非常小心,也可以“手动”完成。最后,在
sun.*
名称空间中有不受支持的
derthis和
类,如果你是一个可恨的人,你可以使用它。我想知道,对于一种密钥类型,仅仅使用
KeyFactory.generatePrivate()
推测性地创建密钥是否更容易,然后捕获适当的异常并尝试下一个键类型,直到
generatePrivate()
成功。然后可以探测实际的
PrivateKey
实例,看看它是什么。谢谢,我已经考虑过解析ASN.1,但我还需要在上面实现一些PKCS#8逻辑,以免出错。尝试所有的标准算法实际上是个好主意!有没有办法动态获取支持的密钥对算法列表,或者我必须将其硬编码到
[“EC”、“RSA”、“DSA”、“DiffieHellman”]
--嗯,也许这应该是一个独特的(更简单的)问题…所以,是的,它可以满足我的需要,但遗憾的是JCA不能做得更好。这是一个很好的答案,允许一个人在不需要Bouncy Castle的情况下解码公钥。虽然我会抛出InvalidKeySpecificationException,而不是RuntimeException。那样更整洁。
PrivateKey generatePrivateKey(String b64) {
    byte[] bytes = Base64.getDecoder().decode(b64);
    for (String algorithm : supportedKeyPairAlgorithms()) {
        try {
            LOGGER.debug("Attempting to decode key as " + algorithm);
            return KeyFactory.getInstance(algorithm).generatePrivate(new PKCS8EncodedKeySpec(bytes));
        } catch (NoSuchAlgorithmException e) {
            LOGGER.warn("Standard algorithm " + algorithm + " not known by this Java runtime from outer space", e);
        } catch (InvalidKeySpecException e) {
            LOGGER.debug("So that key is not " + algorithm + ", nevermind", e);
        }
    }
    throw new RuntimeException("No standard KeyFactory algorithm could decode your key");
}