You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PEM文件生成PrivateKey时遇版本不匹配异常的Java问询

问题:PEM私钥生成JWT时出现版本不匹配异常

问题详情

使用给定的.pem私钥文件生成JWT时,在执行KeyFactory.generatePrivate(keySpec)步骤抛出InvalidKeySpecException,核心错误为version mismatch: (supported: 00, parsed: 01)。

相关文件与代码

mysecret1.pem文件内容

-----BEGIN PRIVATE KEY----- MIGkAgEBBDAz2He6GHzqEe+a2MAl/3QyuvnCsGQrFYKmP/F9mX7lhjYBqN/wJBQd ppqjhVPFiF+gBwYFK4EEACKhZANiAAQvEqdP8J+IkWrx2WA9EEblRiZPaRY9drWI aNFGI24rMBbx5SPd+OkLEOPx1Z5QOJlhdkKtwGsNZRklXGBwFwy/sYV+bRt/bt5O 6SfitF4XZvewKafZ8YaxPklhdloWe+I= -----END PRIVATE KEY-----

JwtSigner1.java代码

public class JwtSigner1 {
    
    private static final long EXPIRY_DURATION = 1000L * 60 * 60 * 1; // 1 hour
    
    public static void main(String[] args) throws Exception {
        File file = new File("mysecret1.pem");
        System.out.println(JwtSigner1.generateTokenUsingPemFile(file));
    }
    
    public static String generateTokenUsingPemFile(File file) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        String key = new String(Files.readAllBytes(file.toPath()), Charset.defaultCharset());
    
        String privateKeyPEM = key.replace("-----BEGIN PRIVATE KEY-----", "").replaceAll(System.lineSeparator(), "")
                .replace("-----END PRIVATE KEY-----", "");
    
        byte[] encoded = Base64.getDecoder().decode(privateKeyPEM);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
    
        Map<String, Object> claims = new HashMap<>();
    
        claims.put("data", " app");
        claims.put("namespace", "sample");
    
        return Jwts.builder().setClaims(claims).setExpiration(new Date(System.currentTimeMillis() + EXPIRY_DURATION))
                .setIssuedAt(new Date(System.currentTimeMillis())).signWith(privateKey).compact();
    }

}

异常信息

Exception in thread "main" java.security.spec.InvalidKeySpecException: 
java.security.InvalidKeyException: IOException : version mismatch: (supported:     00, parsed:     01   
at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:217)     
at java.security.KeyFactory.generatePrivate(KeyFactory.java:372)    
at c.s.v.d.w.c.util.JwtSigner1.generateTokenUsingPemFile(JwtSigner1.java:41)    
at c.s.v.d.w.c.util.JwtSigner1.main(JwtSigner1.java:28) 
Caused by: java.security.InvalidKeyException: IOException : version mismatch: (supported:     00, parsed:     01    
at sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:351)     
at sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:356)     
at sun.security.rsa.RSAPrivateCrtKeyImpl.<init>(RSAPrivateCrtKeyImpl.java:91)   
at sun.security.rsa.RSAPrivateCrtKeyImpl.newKey(RSAPrivateCrtKeyImpl.java:75)   
at sun.security.rsa.RSAKeyFactory.generatePrivate(RSAKeyFactory.java:316)   
at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:213)     ... 3 more 

解决方案

错误原因分析

报错中的版本不匹配说明:

  • Java默认RSAKeyFactory仅支持PKCS#8格式的私钥(版本号00)
  • 给定的PEM文件虽然头标注为BEGIN PRIVATE KEY(PKCS#8标准头),但实际内容是PKCS#1格式的RSA私钥(版本号01),因此直接用PKCS8EncodedKeySpec解析会失败。

修正后的代码(方案一:手动解析ASN.1结构)

import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.pkcs.RSAPrivateKeyStructure;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Security;
import java.security.spec.RSAPrivateKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Base64;

import io.jsonwebtoken.Jwts;

public class JwtSigner1 {
    
    private static final long EXPIRY_DURATION = 1000L * 60 * 60 * 1; // 1 hour
    
    public static void main(String[] args) throws Exception {
        File file = new File("mysecret1.pem");
        System.out.println(JwtSigner1.generateTokenUsingPemFile(file));
    }
    
    public static String generateTokenUsingPemFile(File file) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        String key = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8);

        // 清理PEM内容:移除头尾标记及所有空白字符
        String privateKeyPEM = key.replaceAll("-----BEGIN PRIVATE KEY-----", "")
                .replaceAll("-----END PRIVATE KEY-----", "")
                .replaceAll("\\s+", "");

        byte[] encoded = Base64.getDecoder().decode(privateKeyPEM);

        // 解析PKCS#1格式的RSA私钥
        try (ASN1InputStream asn1InputStream = new ASN1InputStream(encoded)) {
            ASN1Primitive primitive = asn1InputStream.readObject();
            RSAPrivateKeyStructure rsaPrivateKey = new RSAPrivateKeyStructure((ASN1Sequence) primitive);

            RSAPrivateKeySpec rsaPrivateKeySpec = new RSAPrivateKeySpec(rsaPrivateKey.getModulus(), rsaPrivateKey.getPrivateExponent());
            KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");
            PrivateKey privateKey = keyFactory.generatePrivate(rsaPrivateKeySpec);

            Map<String, Object> claims = new HashMap<>();
            claims.put("data", " app");
            claims.put("namespace", "sample");

            return Jwts.builder()
                    .setClaims(claims)
                    .setExpiration(new Date(System.currentTimeMillis() + EXPIRY_DURATION))
                    .setIssuedAt(new Date(System.currentTimeMillis()))
                    .signWith(privateKey)
                    .compact();
        }
    }
}

简化方案(方案二:使用BouncyCastle的PEMParser)

借助BouncyCastle的PEMParser可以自动处理PEM格式解析,无需手动清理Base64内容:

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;

import java.io.File;
import java.io.FileReader;
import java.security.PrivateKey;
import java.security.Security;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import io.jsonwebtoken.Jwts;

public class JwtSigner1 {
    
    private static final long EXPIRY_DURATION = 1000L * 60 * 60 * 1; // 1 hour
    
    public static void main(String[] args) throws Exception {
        File file = new File("mysecret1.pem");
        System.out.println(JwtSigner1.generateTokenUsingPemFile(file));
    }
    
    public static String generateTokenUsingPemFile(File file) throws Exception {
        Security.addProvider(new BouncyCastleProvider());

        // 使用PEMParser读取并解析私钥
        try (PEMParser pemParser = new PEMParser(new FileReader(file))) {
            Object object = pemParser.readObject();
            JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
            PrivateKey privateKey = converter.getPrivateKey((org.bouncycastle.asn1.pkcs.PrivateKeyInfo) object);

            Map<String, Object> claims = new HashMap<>();
            claims.put("data", " app");
            claims.put("namespace", "sample");

            return Jwts.builder()
                    .setClaims(claims)
                    .setExpiration(new Date(System.currentTimeMillis() + EXPIRY_DURATION))
                    .setIssuedAt(new Date(System.currentTimeMillis()))
                    .signWith(privateKey)
                    .compact();
        }
    }
}

依赖注意事项

确保项目中引入BouncyCastle的相关依赖(以Maven为例):

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.70</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk15on</artifactId>
    <version>1.70</version>
</dependency>

内容的提问来源于stack exchange,提问作者Varun Ramachandran

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 07:05:56