.InvalidKeyException:IOException:使用 PEM 文件生成私钥时版本不匹配

问题描述 投票:0回答:1

`嗨,

我有一个 .pem 文件,我正在尝试从中生成一个私钥,以便我可以创建一个由该私钥签名的 JWT(Json Web 令牌)。但是,当执行 KeyFactory.generatePrivate(keySpec) 时,代码会中断。以下是密钥所在的 .pem 文件和 java 文件的内容

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 

我无法使用 OpenSSL 命令行,因为我必须以编程方式使用此 pem 文件并使用 Java 直接将其转换为 PrivateKey。

我希望创建一个私钥并使用它生成 JWT 令牌,并由该密钥签名

java jwt cryptography java-security pkcs#8
1个回答
0
投票

这不是 PKCS8 格式的密钥。标记为

BEGIN/END PRIVATE KEY
的 PEM 文件中正文的唯一正确格式是
RFC5208 第 5 节
中定义的 PKCS8 未加密结构 PrivateKeyInfo 或可能(但很少)其在
RFC5958 第 2 节
中的 OneAsymmetricKey 增强,如下RFC7468第10节中指定,因此该密钥文件违反标准。由于 Java
PKCS8EncodedKeySpec
的唯一正确格式也是 PKCS8 未加密格式(二进制,即不是 PEM),因此该文件在 Java 加密中无法使用。

而且它不是 RSA 密钥。它是 SEC1=RFC5915 结构中的(X9 式)EC 私钥,因此即使转换为 PKCS8,也不能由 RSA 的

KeyFactory
处理。

但是,由于 X9EC 私有标量的长度仅取决于曲线组顺序,而公共点的长度仅取决于基础字段顺序和点格式(压缩与否),因此 SEC1 结构的长度仅取决于曲线,选择哪些选项,以及如果选择公共值选项,则使用哪种点格式。因此,DER 中相应的 PKCS8 结构可以包含仅取决于曲线、SEC1 选项和条件点格式的前缀,以及您拥有的 SEC1 结构。你可以用 Java 做到这一点:

        String key = //new String(Files.readAllBytes(file.toPath()), Charset.defaultCharset());
        // hardcoded for test/demo
        "-----BEGIN PRIVATE KEY-----\n"+ // should be EC PRIVATE KEY instead for SEC1
        "MIGkAgEBBDAz2He6GHzqEe+a2MAl/3QyuvnCsGQrFYKmP/F9mX7lhjYBqN/wJBQd\n"+
        "ppqjhVPFiF+gBwYFK4EEACKhZANiAAQvEqdP8J+IkWrx2WA9EEblRiZPaRY9drWI\n"+
        "aNFGI24rMBbx5SPd+OkLEOPx1Z5QOJlhdkKtwGsNZRklXGBwFwy/sYV+bRt/bt5O\n"+
        "6SfitF4XZvewKafZ8YaxPklhdloWe+I=\n"+
        "-----END PRIVATE KEY-----\n"; // ditto

        String privateKeyB64 = key.replaceAll("-----(BEGIN|END) PRIVATE KEY-----", "")
            .replaceAll("\r?\n", ""); // more robust to be lax about CR
        byte[] sec1 = Base64.getDecoder().decode(privateKeyB64);
 
        // correct only for secp256k1 with SEC1 [0] and [1] present and point uncompressed
        // and thus sec1.length = 167
        byte[] prefix = Base64.getDecoder().decode("MIG/AgEAMBAGByqGSM49AgEGBSuBBAAiBIGn");
        byte[] encoded = new byte[prefix.length+167];
        System.arraycopy(prefix,0, encoded,0, prefix.length);
        System.arraycopy(sec1,0, encoded,prefix.length, 167);

        PrivateKey privateKey = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(encoded));

        // use privateKey -- dummy for demo
        System.out.println(privateKey); 

© www.soinside.com 2019 - 2024. All rights reserved.