エラーメッセージ
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` で包んだバイト列が、そのクラスが前提とする構造で始まっていません。どちらの形式も AlgorithmIdentifier(「algid」)を含む SEQUENCE で始まりますが、JDK はその位置で別のものを見つけました。典型的なのは PKCS#1 の RSA 鍵(`-----BEGIN RSA PRIVATE KEY-----`)で、その最初の要素は INTEGER です。JDK が読み込めるのは、秘密鍵では PKCS#8、公開鍵では X.509 の SubjectPublicKeyInfo だけです。
発生する理由
PKCS#1 形式の鍵(「BEGIN RSA PRIVATE KEY」)
よくあるOpenSSL 1.x の `genrsa`、多くのホスティングの管理画面、古いツールは PKCS#1 で書き出します。その DER は RSAPrivateKey の SEQUENCE で、PKCS#8 が付け加えるアルゴリズム識別子を含まないため、JDK は algid を見つけられません。
公開鍵用と秘密鍵用の KeySpec を取り違えている
よくある秘密鍵を `X509EncodedKeySpec` に渡している、または公開鍵を `PKCS8EncodedKeySpec` に渡しているケースです。両者の構造は、外側の SEQUENCE の直後から異なります。
鍵の DER バイト列ではない
ときどきPEM のテキストをそのままバイト列として渡した場合、base64 を二重にデコードした場合、キーストアから別の形式でエクスポートした鍵を使った場合です。これらは通常、別の `Caused by`(「extra data at the end」「DerInputStream.getLength()」)で失敗しますが、先頭のバイト列がたまたま解析できてしまうと algid のメッセージが表示されます。
鍵ではなく証明書を渡している
ときどき`-----BEGIN CERTIFICATE-----` の中身は X.509 証明書であり、SubjectPublicKeyInfo ではありません。`CertificateFactory` で読み込み、`getPublicKey()` を呼び出してください。
修正方法
- 1.
鍵を一度だけ PKCS#8 / SPKI に変換する
OpenSSL のコマンド 1 つで、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.
アーマーを取り除き、ラベルを確認する
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.
または 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); }
関連するエラー
x509: failed to parse private key (use ParsePKCS1PrivateKey instead for this ke…Go crypto/x509java.lang.IllegalArgumentException: Illegal base64 character 2dJava (JDK)System.Security.Cryptography.CryptographicException: ASN1 corrupted data..NET System.Security.CryptographyCould not deserialize key data. The data may be in an incorrect format, it may…Python cryptography