首页 > 解决方案 > 是否可以从 PEM 文件中读取私钥而无需遍历所有可能的算法或编写 ASN.1 解析器?

问题描述

我正在考虑从 PEM 编码的 DER 文件中读取一个或多个私钥,我想知道它是否可以在不尝试所有可能的算法(丑陋且蛮力)或编写 ASN.1 解析器的情况下稳健地完成?

我试图在没有标准 Java 库以外的任何依赖项的情况下做到这一点。我确信 BouncyCastle 可以做到这一点,但我对开箱即用的 Java 的可能性感兴趣。

PEM 编码的 DER 文件是有点熟悉的文件,如下所示:

-----BEGIN EC PRIVATE KEY-----
[base64-encoded DER data]
-----END EC PRIVATE KEY-----

在上面的示例中,文本指示密钥的类型:它是椭圆曲线密钥,因此我们可以向 Java 请求 EC 密钥工厂:

byte[] keyBytes = // get DER bytes
KeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("EC");
PrivateKey key = kf.generateKey(spec);

但也有可能该文件没有宣传其内容,如下所示:

-----BEGIN PRIVATE KEY-----
[base64 encoded DER data]
-----END PRIVATE KEY-----

在这种情况下,给定 API,我只看到两种明显的可能性:

  1. 遍历所有支持的算法,尝试所有算法并忽略某些错误

  2. 实现一个 ASN.1 解析器来确定密钥的实际类型,然后使用正确的KeyFactory

循环似乎很简单,但 IMO 令人反感:

byte[] keyBytes = // get DER bytes
KeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
PrivateKey key = null;
for(String algorithm : new String[] { "EC", "RSA", "DSA" }) {
  try {
    KeyFactory kf = KeyFactory.getInstance(algorithm);
    key = kf.generatePrivateKey(keyBytes);
  } catch (....) {
    // Ignore "wrong algorithm" exceptions
  }
}
// "key" is either null (couldn't read/interpret) or non-null and OK

这也可能很耗时,尽管通常密钥设置是“罕见”的操作,因此性能并不是非常重要。

我能想到的唯一其他选择是检查数据以准确找出数据代表的内容,然后使用正确的类型:

byte[] keyBytes = // get DER bytes
KeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
ASN1Parser a1p = new ASN1Parser();
String algorithm  = a1p.parse(keyBytes).getAlgorithm();
KeyFactory kf = KeyFactory.getInstance(algorithm);
PrivateKey key = fk.generatePrivateKey(spec);

将此与读取 X.509 证书进行对比,您无需了解任何有关加载证书的算法的信息:

CertificateFactory cf = CertificateFactory.getInstance("X.509");
cf.generateCertificate(new ByteArrayInputStream(certBytes));

我是否在 API 中遗漏了一些允许这种“只为我做”机制的东西,还是在加载密钥材料之前绝对有必要知道算法?

编写 ASN.1 解析器并非易事,尽管它们存在于第三方库中,但我想尽可能地限制依赖关系。

标签: javaprivate-keypki

解决方案


推荐阅读