Is it possible to read the RSA private key of format PKCS1 in JAVA without converting to PKCS8? if yes, sample code is appreciated.
-----BEGIN RSA PRIVATE KEY-----
BASE64 ENCODED DATA
-----END RSA PRIVATE KEY-----
Java does not come with out-of-the-box support for PKCS1 keys. You can however use Bouncycastle
PEMParser pemParser = new PEMParser(new FileReader(privateKeyFile));
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
Object object = pemParser.readObject();
KeyPair kp = converter.getKeyPair((PEMKeyPair) object);
PrivateKey privateKey = kp.getPrivate();
Related
Is it possible to read the RSA private key of format PKCS1 in JAVA without converting to PKCS8? if yes, sample code is appreciated.
-----BEGIN RSA PRIVATE KEY-----
BASE64 ENCODED DATA
-----END RSA PRIVATE KEY-----
Java does not come with out-of-the-box support for PKCS1 keys. You can however use Bouncycastle
PEMParser pemParser = new PEMParser(new FileReader(privateKeyFile));
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
Object object = pemParser.readObject();
KeyPair kp = converter.getKeyPair((PEMKeyPair) object);
PrivateKey privateKey = kp.getPrivate();
I am trying to convert EC private key
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIE2tzb8O0gBVw2IFOB/B8l1Ztjax3ut4DeNtuC3UMmZ6oAoGCCqGSM49
AwEHoUQDQgAEayT6Tv8zZlpIUOKHEYnmsKZyTaqOHajL0InS4c5tK4fhkHZDSWUa
3tPl1ibIXt0LvaxHk47h0Tc4SGr3Ex8Bhg==
-----END EC PRIVATE KEY-----
to Private Key
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgTa3Nvw7SAFXDYgU4
H8HyXVm2NrHe63gN4224LdQyZnqhRANCAARrJPpO/zNmWkhQ4ocRieawpnJNqo4d
qMvQidLhzm0rh+GQdkNJZRre0+XWJshe3Qu9rEeTjuHRNzhIavcTHwGG
-----END PRIVATE KEY-----
It's very easy when you execute openSsl command like this:
openssl pkcs8 -topk8 -nocrypt -in ec1.pem -out ec2.pem
But i want to do this in Java way and didn't find any solution (tried so many from stackoverflow).
So i have for now following class:
ECNamedCurveParameterSpec ecNamedCurveParameterSpec = ECNamedCurveTable.getParameterSpec("prime256v1");
KeyPairGenerator keyPair = KeyPairGenerator.getInstance("ECDSA", "BC");
// Create a secure random number generator using the SHA1PRNG algorithm
SecureRandom secureRandomGenerator = SecureRandom.getInstance("SHA1PRNG");
keyPair.initialize(ecNamedCurveParameterSpec, secureRandomGenerator);
Then i generate KeyPair and get PrivateKey in ECPrivateKey Object:
KeyPair pair =keyPair.generateKeyPair();
ECPrivateKey privateKey = (ECPrivateKey) pair.getPrivate();
StringWriter sw = new StringWriter();
try (JcaPEMWriter jcaPEMWriter = new JcaPEMWriter(sw);) {
jcaPEMWriter.writeObject(privateKey);
}
String pemFormat = sw.toString();
This string pemFormat is actually PEM format that starts with BEGIN EC PRIVATE KEY
How can i convert it just to BEGIN PRIVATE KEY?
I assume that should be a way if openSsl can do it.
A conversion from the SEC1/PEM (-----BEGIN EC PRIVATE KEY-----...) to the PKCS#8/PEM format (-----BEGIN PRIVATE KEY-----...) is not needed at all, because privateKey.getEncoded() returns the key already in PKCS#8 format. So it only needs to be exported as PEM e.g. with a PemWriter:
import org.bouncycastle.util.io.pem.PemWriter;
...
// Your code
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
...
ECPrivateKey privateKey = (ECPrivateKey)pair.getPrivate();
System.out.println(privateKey.getFormat()); // PKCS#8
// Export as PKCS#8 PEM encoded key via PemWriter
StringWriter stringWriter = new StringWriter();
try (PemWriter pemWriter = new PemWriter(stringWriter)){
pemWriter.writeObject((PemObjectGenerator)new PemObject("PRIVATE KEY", privateKey.getEncoded()));
}
String pkcs8PEM = stringWriter.toString();
System.out.println(pkcs8PEM); // -----BEGIN PRIVATE KEY-----MIGTAg...-----END PRIVATE KEY-----
You can check the format in an ASN.1 parser, e.g. https://lapo.it/asn1js.
However, if you are really looking for an explicit conversion of a SEC1/PEM key into a PKCS#8/PEM key, then the import of a SEC1/PEM key is described e.g. here. The imported key can then be exported as a PKCS#8/PEM key using a PemWriter as in the example above.
I'm trying to generate JWT but I'm receiving this error:
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout private.key -out certificate_pub.crt
I'm using the io.jsonwebtoken.Jwts library, and a private key in string form but I'm getting errors.
Map<String, Object> payload = new HashMap<>();
payload.put("iss", orgId);
payload.put("sub", accountId);
payload.put("exp", expirationTime);
payload.put("aud", new
StringBuilder("Url").append("/c/").append(apiKey).toString());
payload.put(new StringBuilder("Url").append("/s/entt_sdk").toString(), Boolean.TRUE);
return Jwts.builder().setClaims(payload).**signWith**(SignatureAlgorithm.RS256, privateKeyStr).compact();
java.lang.IllegalArgumentException: Base64-encoded key bytes may only be specified for HMAC signatures. If using RSA or Elliptic Curve, use the signWith(SignatureAlgorithm, Key) method instead.
My private key looks like this:
-----BEGIN PRIVATE KEY-----
sajdkjsadkjsahdkjsadksadkjsadkjs
-----END PRIVATE KEY-----
The error is quite straightforward - this method can be used only for Hmac algorihtms. For RSA based algorithms you will have to use signWith(SignatureAlgorithm, Key).
The key that I will use will not be in PEM format - it will be base64 DER encoded String - to do it I had to get rid of the beginning and ending of PEM format for this key - -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY----- :
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC9/xBGBRhpSBa/
Xc/5CeAQjjVMcXfIFOqeIc/kq7dnsLz+ntTrvTE3fyz0E8J8MLMTNypK7irt8MB0
0Gpm/NJHnLSGqgSqIXAvUbUsxCO8ULSTKLTQs4RiDbUtOWZgacvRN0IYRnN2tDWf
DWTYAElBSValTt/jvHw72BMcLXd8plGQYYD52l6+w/7ENgLG+hNbVewdM/saJKyL
Y/jhRl758XWGw5bCmIYk9BCbUVDLc5PN+iiYoBFTQYwP0Y1ln58vNZ/CDBw2mW3q
TyploxoAdmG86km4EK2mtlhWBaUshfHORxGkWlCXcwazWDoc5QJ24McaYbcQOcPW
lLDgP8MvAgMBAAECggEAHDX6MZtiE4fbsNB6J+06ctrauR4D/hZ0+8PjfX2tvty0
Q05MKTCvVSEyCI/CifQlMs43HmccwrXDrdSgZ+hURMPU3kXyaVyLrssADsSU1cpZ
9ZvOtUpidri4VR23wMsUs1z0GGylilOZvqMbfSMVvXbpQaRjhAohnDUqKT3rBvvd
fqO8KFl8FCgMEbbPvym5tJvrYfe9WalisnrGrrCZoaBmR5dEbjUfWrMg6bMcfPlz
rVaney8+UdFu15RUXTno3mu+glIYz1MyYk6LdlgBrb19gBscykqi0wYhZ312Yk0B
SXx+RIi49oZy7IU4jybzOSqWL79L4rQdvtBrp/dx6QKBgQDn/XeuCATwLdFyeo4/
ksVlhXIp9ykAZSFk/wnapcsvLSV59edI8mkslAwTTbgqPn0hvxVdwf0k23wipkGl
FiujHMCGoeT9ZwYs9uDEkGABATXomr7eC64AEfuUnUZMj3s0BgG5S/mGonlkIlTt
RpvxzMeYnRvLjDXZMP7FKCz0DQKBgQDRqPq+w7MQFBaBMTE7+685QeR+xxGnMipW
Qf579E++ihslGx6LztQnFhbah2VEVCPBq7R/BiEHiW4bA+DiTC8HnMsZi3jhlO9q
yw0DSZUSX8vsgNW2ghJOF9JnZEbptN3RlD11koSvkFZiUuxHYa0n6ti38CwfLxgV
MCuL5XOZKwKBgGi4CqD9L7V3CTdiyPk7eG1mOm1lCxYJkHR1h24yLrCB8YvHC3rr
Kbycq4K/L2WqRXPJPIzQ90L+7F77q2AozNPZM7LSO3qDWc9MNZOlFCD/+eSgjY3P
ueCAPY8NG2GN1vBZ0cdh2yYCC0e/E5TzrYsNg/+I07Yi+V+r9STsCLa1AoGAJnJo
WOcmRQKKBfLxZmCHB2bv8dergw+N9/duJWjt3rEQvUM13Ml22hwQ4M4HYfpT/EXy
eYC0Od+X01houtbhoPG9xNdwuV1Icjr+DeZGcfIjQSF3D1rW5H811EPtRRonuzEF
/DN8JX3AeZNfRM/CoxlL2J8wWB+YuPn2YlcXVbUCgYEAmVETM7+OBW9YKtv6zvKe
OZeZUIDIUZDqZgLd3IT7rikVCedIljWNhroXU1wNMssJPkfiQToGaykUMbBcgZKI
neU2IuYWaLXBN9oAj1u7/YQ0DpPqk/Sb2FpVX5eKfp4cu8XdyezxNuFFsPVdGBhB
xhqJOJuUc/ZKbo5Stc3NXEE=
Here is example of how to read this key and sign JWT with it :
//create payload
Map<String, Object> payload = new HashMap<>();
payload.put("iss", "orgId");
payload.put("sub", "orgId");
payload.put("exp", "orgId");
payload.put("aud", new
StringBuilder("Url").append("/c/").append("key").toString());
payload.put(new StringBuilder("Url").append("/s/entt_sdk").toString(), Boolean.TRUE);
// read key
String privateKeyB64 = Files.lines(Paths.get("src/main/resources/private.key")).collect(Collectors.joining());
byte[] privateKeyDecoded = Base64.getDecoder()
.decode(privateKeyB64);
//create key spec
PKCS8EncodedKeySpec spec =
new PKCS8EncodedKeySpec(privateKeyDecoded);
// create key form spec
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(spec);
//create signed JWT - JWS
String jws = Jwts.builder().setClaims(payload).signWith(SignatureAlgorithm.RS256, privateKey).compact();
System.out.println(jws);
Notice I used PKCS8EncodedKeySpec because your key seems to be in PKCS8 format. The output is :
eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJvcmdJZCIsImF1ZCI6IlVybC9jL2tleSIsImlzcyI6Im9yZ0lkIiwiZXhwIjoib3JnSWQiLCJVcmwvcy9lbnR0X3NkayI6dHJ1ZX0.m8ASk4kUNx41csikpd0zALLQTjwG2pc0Ba0D9PGLVbI2NaY0IIXgaVVVJcIERz4ejj_jfq436v6v0_QnxdmvjMAnx88UmHGdrCT0V5MZl008LP4g4LrV-WczNltCUpoJQ-4CW6xkpXD03JIDQAYwaKb-PIOtm-pfLJhPPmxykc8QioueijhI5M__Pq5Nq0JCbkQxfGzxE5m_gJwwq7n290RBGRYH6AHeClaEJhDzLNitIejNvvua4zNNC6S1CHsa4ChaEFfRb9bi-jNEQW27IGhrKRCtuwleFwigl7oTIsyaRWlzuVNYcZHS707Z2o6Mkf9hDo8AGKURUVsJgA8WIg
I tested it on Java 8. For Java 11 I received an error with missing module regarding XML processing.
I can't generate private key with bouncycastle due to Unknown KeySpec type: java.security.spec.X509EncodedKeySpec. (However doing same for public key doesn't throw exception and works - why?)
java.security.spec.InvalidKeySpecException: Unknown KeySpec type:
java.security.spec.X509EncodedKeySpec at
org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyFactorySpi.engineGeneratePrivate(Unknown
Source) at
java.security.KeyFactory.generatePrivate(KeyFactory.java:366)
PemReader pemReader = new PemReader(new InputStreamReader(new FileInputStream("private_unencrypted.pem")));
PemObject pemObject = pemReader.readPemObject();
pemReader.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");
byte[] privateKeyBytes = pemObject.getContent();
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(privateKeyBytes);
PrivateKey privateKey = keyFactory.generatePrivate(x509KeySpec);
For RSA private keys you should be using PKCS8EncodedKeySpec if your key is encoded in PKCS8 format. PKCS8 format usually looks like :
-----BEGIN PRIVATE KEY-----
base64 encoded der key
-----END PRIVATE KEY-----
If your key is in PKCS1 format and looks like :
-----BEGIN RSA RIVATE KEY-----
base64 der encoded key
-----END RSA PRIVATE KEY-----
you should first convert it to PKCS8 format and then use the class mentioned above.
However doing same for public key doesn't throw exception and works - why?
Because public keys, which are usually part of Certificates, are encoded in X509 format, however private keys are usually encoded in PKCS format.
I used java to generate public key out of private key as follows,
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC");
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();
PrivateKey priv = kp.getPrivate();
RSAPrivateCrtKey rsaCrtKey = (RSAPrivateCrtKey) priv;
RSAPublicKeySpec keyspecPublic = new RSAPublicKeySpec(rsaCrtKey.getModulus(), rsaCrtKey.getPublicExponent());
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey publicKey2 = kf.generatePublic(keyspecPublic);
String publicString = Base64.getMimeEncoder().encodeToString(publicKey2.getEncoded());
StringBuilder sb = new StringBuilder("");
sb.append("\"-----BEGIN PUBLIC KEY-----\\n")
.append(publicString)
.append("\\n-----END PUBLIC KEY-----");
And I generate public key using openssl as follows for same private key,
openssl rsa -in private.key -pubout -out public.key
But above two methods generate different lengths of strings as public key. Am I doing something wrong here?
public key from java code:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6UkqbaGp7wOx2vrtqxmX/22ixKZcZPfZ
xXAawIst+AsWyNS+99MxnilstLIBKd6BCzJJsAa2I0ks43mnNZbkx1f1um+tQpXuTMbTh5MRprPn
jmX6aF+JXJbXATKhabTIQcCdpnrMi6scp9nWYkFdxVMfvo1gyThzfPwPgCO4eRFo1IkwZuergkl7
e0+U7WonqzFEb0joy5P78U+K8HebDW7nbS8zliq3DH2FI9yvEK3LeEN+Sa5icMWlERGv+7FCJVIH
CjBYfzaRZD9qqld/AnAEkCVt38SfSqWJECsnJYUW90WuFd8IxUVPS0TMfFMQhJFMT3eGweBOUb/b
pDm/hQIDAQAB
-----END PUBLIC KEY-----
Public key from openssl :
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6UkqbaGp7wOx2vrtqxmX
/22ixKZcZPfZxXAawIst+AsWyNS+99MxnilstLIBKd6BCzJJsAa2I0ks43mnNZbk
x1f1um+tQpXuTMbTh5MRprPnjmX6aF+JXJbXATKhabTIQcCdpnrMi6scp9nWYkFd
xVMfvo1gyThzfPwPgCO4eRFo1IkwZuergkl7e0+U7WonqzFEb0joy5P78U+K8Heb
DW7nbS8zliq3DH2FI9yvEK3LeEN+Sa5icMWlERGv+7FCJVIHCjBYfzaRZD9qqld/
AnAEkCVt38SfSqWJECsnJYUW90WuFd8IxUVPS0TMfFMQhJFMT3eGweBOUb/bpDm/
hQIDAQAB
-----END PUBLIC KEY-----
The keys are identical, and will decode to the same byte content.
Formatting-wise, the Java one is wrapped at 76 columns and the OpenSSL one is wrapped at 64 columns.
If you want to make the wrapping consistent, use the Base64.getMimeEncoder(int lineLength, byte[] lineSeparator) overloaded method that lets you specify the line length and pass 64 as the lineLength parameter.
Base64.getMimeEncoder(64, new byte[] {'\r', '\n'});