跳到主要内容
ecdsa.com
Java (JDK)Java

错误信息

algid parse error, not a sequence

也可能输出为

  • java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
  • java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: Unable to decode key

在 JDK 8 到 11 上,异常文本本身就是“InvalidKeyException: IOException : algid parse error, not a sequence”。较新的 JDK(已在 21 和 25 上验证)显示“Unable to decode key”,而“algid parse error, not a sequence”保留在堆栈跟踪的 `Caused by:` 行中。

Java (JDK) — KeyFactory.generatePrivate / generatePublic with PKCS8EncodedKeySpec or X509EncodedKeySpec

含义

你封装进 `PKCS8EncodedKeySpec` 或 `X509EncodedKeySpec` 的字节,开头并不是这个 KeySpec 所约定的结构:这两种格式都以一个包含 AlgorithmIdentifier(“algid”)的 SEQUENCE 开头,而 JDK 在那个位置发现的是别的东西。典型情况是 PKCS#1 RSA 密钥(`-----BEGIN RSA PRIVATE KEY-----`),它的第一个元素是 INTEGER。JDK 读取私钥只支持 PKCS#8,读取公钥只支持 X.509 SubjectPublicKeyInfo。

原因

修复方法

  1. 1.

    一次性把密钥转换为 PKCS#8 / SPKI

    一条 OpenSSL 命令就能把 PKCS#1 密钥转换成 JDK 原生支持读取的格式。

    bash
    # "-----BEGIN RSA PRIVATE KEY-----" (PKCS#1) -> "-----BEGIN PRIVATE KEY-----" (PKCS#8)
    openssl pkcs8 -topk8 -nocrypt -in rsa-pkcs1.pem -out private-pkcs8.pem
    # "-----BEGIN RSA PUBLIC KEY-----" -> "-----BEGIN PUBLIC KEY-----" (X.509 SubjectPublicKeyInfo)
    openssl rsa -RSAPublicKey_in -in rsa-public-pkcs1.pem -pubout -out public.pem
  2. 2.

    去掉封装行并检查标签

    只解码 base64 正文;遇到 JDK 无法读取的 PEM 标签,用一条清晰的消息拒绝,而不是抛出解析错误。

    java
    import java.nio.file.*;
    import java.security.*;
    import java.security.spec.*;
    import java.util.Base64;
    
    static PrivateKey loadPkcs8(Path path, String algorithm) throws Exception {
      String pem = Files.readString(path);
      if (!pem.contains("-----BEGIN PRIVATE KEY-----")) {
        throw new IllegalArgumentException("expected a PKCS#8 'BEGIN PRIVATE KEY' file");
      }
      String body = pem.replaceAll("-----[A-Z ]+-----", "").replaceAll("\\s", "");
      return KeyFactory.getInstance(algorithm).generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(body)));
    }
    
    static PublicKey loadSpki(Path path, String algorithm) throws Exception {
      String body = Files.readString(path).replaceAll("-----[A-Z ]+-----", "").replaceAll("\\s", "");
      return KeyFactory.getInstance(algorithm).generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(body)));
    }
  3. 3.

    或者用 Bouncy Castle 读取各种 PEM 格式

    `PEMParser` 能解析 PKCS#1、PKCS#8、SEC 1 和加密的 PEM,`JcaPEMKeyConverter` 再把结果转换成 JCA 密钥。

    java
    // With Bouncy Castle (bcpkix): reads PKCS#1, PKCS#8, SEC 1 and encrypted keys
    try (var parser = new org.bouncycastle.openssl.PEMParser(Files.newBufferedReader(path))) {
      Object object = parser.readObject();
      var converter = new org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter();
      PrivateKey key = object instanceof org.bouncycastle.openssl.PEMKeyPair pair
          ? converter.getKeyPair(pair).getPrivate()
          : converter.getPrivateKey((org.bouncycastle.asn1.pkcs.PrivateKeyInfo) object);
    }

相关错误

浏览完整的签名错误库