java - 如何从密钥库开始生成私钥和公钥 (.p12)
java - How to generate PrivateKey and PublicKey starting from a keystore (.p12)
使用OpenSSL生成一些密钥,然后在Base64中编码并获取它们并尝试生成它们以验证与JWT的身份验证。这是发生在我身上的代码和描述
使用以下命令生成:
openssl req -x509 -newkey rsa:4096 -keyout private_key.pem -out public_key.der
openssl pkcs12 -export -out keyStore.p12 -inkey private_key.pem -in public_key.der
base64 –w 0 private_key.pem > private_key_base64_enc.txt
base64 –w 0 public_key.der > public_key_base64_enc.txt
我从 wildfly 保存在我的 vault.keystore 中:private_key_base64_enc.txt 和 public_key_base64_enc.txt
然后在我的javaclass中写下:
private void jwtSignedAuthentication(String token, PropName vaultBlockName) throws Exception
{
String rsa512Alias = vaultBlockName.getDefaultValue();
String rsa512pvt = VaultReader.getValue(rsa512Alias, "privateKey");
String rsa512pbc = VaultReader.getValue(rsa512Alias, "publicKey");
KeyFactory keyfatc = null;
PrivateKey privateKey = null;
PublicKey publicKey = null;
try {
keyfatc = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
logger.error(e);
}
StringBuilder pkcs8Lines = new StringBuilder();
BufferedReader rdr = new BufferedReader(new StringReader(new String(Base64.getDecoder().decode(rsa512pvt.getBytes()))));
String line;
while ((line = rdr.readLine()) != null) {
pkcs8Lines.append(line);
}
// Remove the "BEGIN" and "END" lines, as well as any whitespace
String pkcs8Pem = pkcs8Lines.toString();
pkcs8Pem = pkcs8Pem.replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replace("-----END ENCRYPTED PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replaceAll("\s+","");
byte[] dataPvt = Base64.getDecoder().decode(pkcs8Pem.getBytes());
PKCS8EncodedKeySpec specPvt = new PKCS8EncodedKeySpec(dataPvt);
byte[] dataPbc = Base64.getDecoder().decode(rsa512pbc.getBytes());
StringBuilder publicLinesBuilder = new StringBuilder();
BufferedReader readerPlubKey = new BufferedReader(new StringReader(new String(dataPbc)));
String lineP;
while ((lineP = readerPlubKey.readLine()) != null) {
publicLinesBuilder.append(lineP);
}
String pubK = publicLinesBuilder.toString();
pubK = pubK.replace("-----BEGIN CERTIFICATE-----", "");
pubK = pubK.replace("-----END CERTIFICATE-----", "");
pubK = pubK.replaceAll("\s+","");
X509EncodedKeySpec specPbc = new X509EncodedKeySpec(Base64.getDecoder().decode(pubK.getBytes()));
try {
privateKey = keyfatc.generatePrivate(specPvt);
publicKey = keyfatc.generatePublic(specPbc);
} catch (InvalidKeySpecException e) {
logger.error(e);
}
Algorithm algorithm = Algorithm.RSA512((RSAPublicKey) publicKey, (RSAPrivateKey) privateKey);
// Creación de un verificador JWT
JWTVerifier verifier = JWT.require(algorithm).withIssuer(JWT_CLAIM_ISSUER).acceptLeeway(2).build();
UserContext userContext = new UserContext();
userContext.setUserName(JWT_CLAIM_ISSUER);
try {
// Decode JWT, verificación del token.
@SuppressWarnings("unused")
DecodedJWT decodeJwt = verifier.verify(token);
} catch (JWTDecodeException e) {
logger.error(e);
}
}
当我尝试生成密钥时,我 return null:
privateKey = keyfatc.generatePrivate(specPvt);
publicKey = keyfatc.generatePublic(specPbc);
任何人都知道这会发生什么。提前致谢
为了生成我的 JWT:
public ResteasyWebTarget getClientWebAgent(String host, String blockName) throws KeyStoreException
{
ResteasyClient clientBuilder = new ResteasyClientBuilder().establishConnectionTimeout(10, TimeUnit.SECONDS).socketTimeout(5, TimeUnit.SECONDS).build();
ResteasyWebTarget target = clientBuilder.target(host);
KeyPair keys = null;
try {
keys = keyStore.getKeys();
/*logger.infov(new String(Base64.getEncoder().encode(keys.getPrivate().getEncoded())));
logger.infov("****PUBLIC KEY ******");
logger.infov(new String(keys.getPublic().getEncoded()));*/
} catch (IOException e) {
logger.error(e);
}
Algorithm algorithm = Algorithm.RSA512((RSAPublicKey) keys.getPublic(), (RSAPrivateKey) keys.getPrivate());
Map<String, Object> headerClaims = new HashMap<>();
headerClaims.put("alg", "RS512");
headerClaims.put("typ", "JWT");
JWTCreator.Builder jwtCreator = JWT.create();
jwtCreator.withHeader(headerClaims);
jwtCreator.withIssuer(JWT_CLAIM_ISSUER);
jwtCreator.withIssuedAt(LocalDate.now().toDate());
jwtCreator.withExpiresAt(LocalDate.now().toDateTimeAtCurrentTime().plusSeconds(30).toDate());
String jwtToken = jwtCreator.sign(algorithm);
target.register(new BearerAuthenticator(jwtToken));
target.register(new LanguageHeaderToken(Locale.getDefault()));
return target;
}
也许您在生成密钥库时没有分配有效的别名,查看您的命令您没有使用 -name
选项。
命令应该是这样的:
openssl pkcs12 -export -out keyStore.p12 -inkey private_key.pem -in public_key.der -name "alias"
使用 java 中的键的更聪明的方法是创建一个 KeyPair
:
KeyPair loadKeyPair() throws Exception {
// Read keystore from resource folder
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL resource = classLoader.getResource("keyStore.p12");
File file = new File(Objects.requireNonNull(resource).toURI());
char[] keyPass = "1234".toCharArray();
String alias = "alias";
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
try (FileInputStream is = new FileInputStream(file)) {
keystore.load(is, keyPass);
}
Key key = keystore.getKey(alias, keyPass);
if (key instanceof PrivateKey) {
// Get certificate of public key
Certificate cert = keystore.getCertificate(alias);
// Get public key
PublicKey publicKey = cert.getPublicKey();
// Return a key pair
return new KeyPair(publicKey, (PrivateKey) key);
}
return null;
}
然后从 KeyPair 中提取 RSAPublicKey
和 RSAPrivateKey
密钥:
void loadKeys() throws Exception{
KeyPair keyPair = loadKeyPair();
if (null != keyPair) {
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
}
}
希望它能对您有所帮助,并祝您 Json Web 令牌好运! :-p
您的 'public key' 实际上是一个证书(具体是 X.509 v1 或 v3 证书,取决于您的 openssl 配置),其中 包含 公钥但不同来自公钥 - 并且采用 PEM 格式,即使您误将其命名为 .der
- 并且您的私钥已加密。
除了使用 PKCS12 的方法之外,正如 Roberto 有效地提出的并且通常是最简单的,因为它只需要管理一个文件并且仍然是加密的,因此更安全:
Java 可以处理 X.509 证书,但是您使用 CertificateFactory.getInstance("X.509")
并给它一个 InputStream
而不是 KeyFactory
并且一个 X509EncodedKeySpec
。 CertificateFactory
可以 处理 PEM 或 DER,不像 KeyFactory
只能处理 DER,所以你不需要 de-PEM (strip BEGIN/END/EOL 并解码 base64) 部分。
标准 Java 无法直接处理加密的 PKCS8 密钥。如果您可以添加第三方库,BouncyCastle 的 bcpkix 可以 这样做;搜索现有的十几个使用 PEMParser
(不是 PEMReader
,那是旧版本)和 JceOpenSSLPKCS8DecryptorBuilder
的问题。否则,您可以将 -nodes
添加到您的 req -newkey -x509
命令以生成一个 未加密的 私钥文件,在您 de-PEM 后它 会 在 KeyFactory
和 PKCS8EncodedKeySpec
工作。 (它仍然拼写为 -nodes
,尽管没有它使用的加密几十年来一直不是简单的单一 DES。)当然,使用未加密的私钥文件意味着系统上任何可以读取该文件的入侵者或恶意软件都可以获取您的私钥,这在很多情况下都是有风险的。
最后,如果您真的只想要密钥对而不想要证书,请不要理会req -newkey -x509
。而是使用 openssl genpkey
生成私钥,或者使用较旧但更简单的 openssl genrsa -nodes
后跟 (或通过管道传输到) openssl pkcs8 -topk8 -nocrypt
将其转换为 PKCS8-未加密格式。然后使用 openssl pkey -pubout
或更早的 openssl rsa -pubout
来制作一个带有公钥的单独文件。这些命令可以写入(并在适用的情况下读回)DER 格式而不是 PEM;如果这样做,您的代码就不需要 de-PEM 步骤,您只需将二进制文件内容传递给 KeyFactory
。未加密文件的风险同上
使用OpenSSL生成一些密钥,然后在Base64中编码并获取它们并尝试生成它们以验证与JWT的身份验证。这是发生在我身上的代码和描述 使用以下命令生成:
openssl req -x509 -newkey rsa:4096 -keyout private_key.pem -out public_key.der
openssl pkcs12 -export -out keyStore.p12 -inkey private_key.pem -in public_key.der
base64 –w 0 private_key.pem > private_key_base64_enc.txt
base64 –w 0 public_key.der > public_key_base64_enc.txt
我从 wildfly 保存在我的 vault.keystore 中:private_key_base64_enc.txt 和 public_key_base64_enc.txt
然后在我的javaclass中写下:
private void jwtSignedAuthentication(String token, PropName vaultBlockName) throws Exception
{
String rsa512Alias = vaultBlockName.getDefaultValue();
String rsa512pvt = VaultReader.getValue(rsa512Alias, "privateKey");
String rsa512pbc = VaultReader.getValue(rsa512Alias, "publicKey");
KeyFactory keyfatc = null;
PrivateKey privateKey = null;
PublicKey publicKey = null;
try {
keyfatc = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
logger.error(e);
}
StringBuilder pkcs8Lines = new StringBuilder();
BufferedReader rdr = new BufferedReader(new StringReader(new String(Base64.getDecoder().decode(rsa512pvt.getBytes()))));
String line;
while ((line = rdr.readLine()) != null) {
pkcs8Lines.append(line);
}
// Remove the "BEGIN" and "END" lines, as well as any whitespace
String pkcs8Pem = pkcs8Lines.toString();
pkcs8Pem = pkcs8Pem.replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replace("-----END ENCRYPTED PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replaceAll("\s+","");
byte[] dataPvt = Base64.getDecoder().decode(pkcs8Pem.getBytes());
PKCS8EncodedKeySpec specPvt = new PKCS8EncodedKeySpec(dataPvt);
byte[] dataPbc = Base64.getDecoder().decode(rsa512pbc.getBytes());
StringBuilder publicLinesBuilder = new StringBuilder();
BufferedReader readerPlubKey = new BufferedReader(new StringReader(new String(dataPbc)));
String lineP;
while ((lineP = readerPlubKey.readLine()) != null) {
publicLinesBuilder.append(lineP);
}
String pubK = publicLinesBuilder.toString();
pubK = pubK.replace("-----BEGIN CERTIFICATE-----", "");
pubK = pubK.replace("-----END CERTIFICATE-----", "");
pubK = pubK.replaceAll("\s+","");
X509EncodedKeySpec specPbc = new X509EncodedKeySpec(Base64.getDecoder().decode(pubK.getBytes()));
try {
privateKey = keyfatc.generatePrivate(specPvt);
publicKey = keyfatc.generatePublic(specPbc);
} catch (InvalidKeySpecException e) {
logger.error(e);
}
Algorithm algorithm = Algorithm.RSA512((RSAPublicKey) publicKey, (RSAPrivateKey) privateKey);
// Creación de un verificador JWT
JWTVerifier verifier = JWT.require(algorithm).withIssuer(JWT_CLAIM_ISSUER).acceptLeeway(2).build();
UserContext userContext = new UserContext();
userContext.setUserName(JWT_CLAIM_ISSUER);
try {
// Decode JWT, verificación del token.
@SuppressWarnings("unused")
DecodedJWT decodeJwt = verifier.verify(token);
} catch (JWTDecodeException e) {
logger.error(e);
}
}
当我尝试生成密钥时,我 return null:
privateKey = keyfatc.generatePrivate(specPvt);
publicKey = keyfatc.generatePublic(specPbc);
任何人都知道这会发生什么。提前致谢
为了生成我的 JWT:
public ResteasyWebTarget getClientWebAgent(String host, String blockName) throws KeyStoreException
{
ResteasyClient clientBuilder = new ResteasyClientBuilder().establishConnectionTimeout(10, TimeUnit.SECONDS).socketTimeout(5, TimeUnit.SECONDS).build();
ResteasyWebTarget target = clientBuilder.target(host);
KeyPair keys = null;
try {
keys = keyStore.getKeys();
/*logger.infov(new String(Base64.getEncoder().encode(keys.getPrivate().getEncoded())));
logger.infov("****PUBLIC KEY ******");
logger.infov(new String(keys.getPublic().getEncoded()));*/
} catch (IOException e) {
logger.error(e);
}
Algorithm algorithm = Algorithm.RSA512((RSAPublicKey) keys.getPublic(), (RSAPrivateKey) keys.getPrivate());
Map<String, Object> headerClaims = new HashMap<>();
headerClaims.put("alg", "RS512");
headerClaims.put("typ", "JWT");
JWTCreator.Builder jwtCreator = JWT.create();
jwtCreator.withHeader(headerClaims);
jwtCreator.withIssuer(JWT_CLAIM_ISSUER);
jwtCreator.withIssuedAt(LocalDate.now().toDate());
jwtCreator.withExpiresAt(LocalDate.now().toDateTimeAtCurrentTime().plusSeconds(30).toDate());
String jwtToken = jwtCreator.sign(algorithm);
target.register(new BearerAuthenticator(jwtToken));
target.register(new LanguageHeaderToken(Locale.getDefault()));
return target;
}
也许您在生成密钥库时没有分配有效的别名,查看您的命令您没有使用 -name
选项。
命令应该是这样的:
openssl pkcs12 -export -out keyStore.p12 -inkey private_key.pem -in public_key.der -name "alias"
使用 java 中的键的更聪明的方法是创建一个 KeyPair
:
KeyPair loadKeyPair() throws Exception {
// Read keystore from resource folder
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL resource = classLoader.getResource("keyStore.p12");
File file = new File(Objects.requireNonNull(resource).toURI());
char[] keyPass = "1234".toCharArray();
String alias = "alias";
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
try (FileInputStream is = new FileInputStream(file)) {
keystore.load(is, keyPass);
}
Key key = keystore.getKey(alias, keyPass);
if (key instanceof PrivateKey) {
// Get certificate of public key
Certificate cert = keystore.getCertificate(alias);
// Get public key
PublicKey publicKey = cert.getPublicKey();
// Return a key pair
return new KeyPair(publicKey, (PrivateKey) key);
}
return null;
}
然后从 KeyPair 中提取 RSAPublicKey
和 RSAPrivateKey
密钥:
void loadKeys() throws Exception{
KeyPair keyPair = loadKeyPair();
if (null != keyPair) {
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
}
}
希望它能对您有所帮助,并祝您 Json Web 令牌好运! :-p
您的 'public key' 实际上是一个证书(具体是 X.509 v1 或 v3 证书,取决于您的 openssl 配置),其中 包含 公钥但不同来自公钥 - 并且采用 PEM 格式,即使您误将其命名为 .der
- 并且您的私钥已加密。
除了使用 PKCS12 的方法之外,正如 Roberto 有效地提出的并且通常是最简单的,因为它只需要管理一个文件并且仍然是加密的,因此更安全:
Java 可以处理 X.509 证书,但是您使用
CertificateFactory.getInstance("X.509")
并给它一个InputStream
而不是KeyFactory
并且一个X509EncodedKeySpec
。CertificateFactory
可以 处理 PEM 或 DER,不像KeyFactory
只能处理 DER,所以你不需要 de-PEM (strip BEGIN/END/EOL 并解码 base64) 部分。标准 Java 无法直接处理加密的 PKCS8 密钥。如果您可以添加第三方库,BouncyCastle 的 bcpkix 可以 这样做;搜索现有的十几个使用
PEMParser
(不是PEMReader
,那是旧版本)和JceOpenSSLPKCS8DecryptorBuilder
的问题。否则,您可以将-nodes
添加到您的req -newkey -x509
命令以生成一个 未加密的 私钥文件,在您 de-PEM 后它 会 在KeyFactory
和PKCS8EncodedKeySpec
工作。 (它仍然拼写为-nodes
,尽管没有它使用的加密几十年来一直不是简单的单一 DES。)当然,使用未加密的私钥文件意味着系统上任何可以读取该文件的入侵者或恶意软件都可以获取您的私钥,这在很多情况下都是有风险的。最后,如果您真的只想要密钥对而不想要证书,请不要理会
req -newkey -x509
。而是使用openssl genpkey
生成私钥,或者使用较旧但更简单的openssl genrsa -nodes
后跟 (或通过管道传输到)openssl pkcs8 -topk8 -nocrypt
将其转换为 PKCS8-未加密格式。然后使用openssl pkey -pubout
或更早的openssl rsa -pubout
来制作一个带有公钥的单独文件。这些命令可以写入(并在适用的情况下读回)DER 格式而不是 PEM;如果这样做,您的代码就不需要 de-PEM 步骤,您只需将二进制文件内容传递给KeyFactory
。未加密文件的风险同上