`嗨,
我有一个 .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 令牌,并由该密钥签名
这不是 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);