Convert an RSA PKCS1 private key string to a Java PrivateKey object - java

I have a RSA private key stored as a String that I need to convert into a PrivateKey object for use with an API. I can find examples of people converting from a private-key file to a string but not the other way around.
I managed to convert it to a PrivateKey object but it was in PKCS8, when I need it to be PKCS1, and I know Java doesn't have PKCS1EncodedKeySpec
byte[] key64 = Base64.decodeBase64(privateKeyString.getBytes());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
KeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);

you can convert pkcs#1 to pkcs#8
openssl pkcs8 -topk8 -in server.key -nocrypt -out server_pkcs8.key
cat server_pkcs8.key
-----BEGIN PRIVATE KEY-----
base64_encode xxx
-----END PRIVATE KEY-----

Related

PKCS8EncodedKeySpec able to decode RSA PrivateKey in PKCS#1 and PKCS#8 format. But, not for Ec algorithm

I have a scenario where I put RSA/EC private key (created by openssl) in JSON payload. I have my customized parser where I look for banners and fetch the information between them.
Now for RSA key irrespective whether it is PKCS#1 (default openssl) or PKCS#8 following code is working:
KeyFactory factory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = factory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
where privateKeyBytes is decoded base64 byte of private key. Private key is in the PEM format I put in JSON payload.
However when I create private key for EC algorithm via openssl, same piece of code is giving following error:
java.security.spec.InvalidKeySpecException:
java.security.InvalidKeyException: IOException : version mismatch:
(supported: 00, parsed: 01)
When I convert created pem to PKCS#8 format via (openssl pkcs8 command) then it works.
Note: My code uses "SUNRSA" provider for RSA algorithm and "SUNEC" provider for EC algorithm.
The RSA KEYS for example are generated in following manner:
Note: KeySize is the size of key you want to generate i.e 1024/2048 etc.
public static KeyPair generateRsaKeyPair(int keySizeInBits) throws NoSuchAlgorithmException {
KeyPairGenerator r = KeyPairGenerator.getInstance("RSA");
r.initialize(keySizeInBits, RandomUtil.getSecureRandom());
KeyPair keypair = r.generateKeyPair();
return keypair;
}
And EC key pair is generated as follows:
Note: CurveType is the type of curve used for EC algorithm key pair generation eg: prime256v1
public static KeyPair generateEcKeyPair(String curveType) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC","BC");
ECGenParameterSpec ecsp = new ECGenParameterSpec(curveType);
kpg.initialize(ecsp, new SecureRandom());
KeyPair kpU = kpg.generateKeyPair();
return kpU;
}

InvalidKeySpecException: How do I extract a private key from a .der file?

I have a private key file in the .der format. I'm trying to save this private key as a PrivateKey object (with Java) like this:
PrivateKey clientPrivKey = getPrivateKeyFromKeyFile("C:\\Users\\Bob\\Desktop\\Assignments\\Project\\VPN Project\\src\\client-private.der");
This is what the getPrivateKeyFromKeyFile method looks like:
private static PrivateKey getPrivateKeyFromKeyFile(String keyfile) throws Exception
{
Path path = Paths.get(keyfile);
byte[] privKeyByteArray = Files.readAllBytes(path);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privKeyByteArray);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey myPrivKey = keyFactory.generatePrivate(keySpec);
return myPrivKey;
}
But when I try this, I keep getting InvalidKeySpecException because of this line of code:
PrivateKey myPrivKey = keyFactory.generatePrivate(keySpec);
I'm not sure what's the issue here. I opened up the private key file and everything looks fine. It starts with -----BEGIN RSA PRIVATE KEY----- and ends with -----END RSA PRIVATE KEY-----.
And in case it's relevant, I created this private key using this OpenSSL command:
genrsa -out client-private.der 2048
A file generated with
openssl genrsa -out <path to output-file> 2048
is actually not a .der-file, but a .pem-file (see e.g. What are the differences between .pem, .cer and .der?) and the data are not stored in the PKCS8-format, but in the PKCS1-format (see e.g. PKCS#1 and PKCS#8 format for RSA private key).
Keys in the PKCS1-format can not be processed directly using standard Java tools. For this, third-party libraries like BouncyCastle are necessary (see e.g. Read RSA private key of format PKCS1 in JAVA).
Another possibility is to convert the PKCS1-formatted key into a PKCS8-formatted key with OpenSSL first (see e.g. Load a RSA private key in Java (algid parse error, not a sequence)):
openssl pkcs8 -topk8 -inform PEM -outform PEM -in <path to the input-pkcs1-pem-file> -out <path to the output-pkcs8-pem-file> -nocrypt
And then, after (programmatic) deletion of the Beginn-/End-line and after base64-decoding the private key can be generated (see e.g. How to read .pem file to get private and public key) e.g. with
private static PrivateKey getPrivateKeyFromKeyFile(String keyfile) throws Exception
{
Path path = Paths.get(keyfile);
byte[] privKeyByteArray = Files.readAllBytes(path);
// added ----------------------------------------------------------------
String privKeyString = new String(privKeyByteArray);
privKeyString = privKeyString.replace("-----BEGIN PRIVATE KEY-----", "");
privKeyString = privKeyString.replace("-----END PRIVATE KEY-----", "");
privKeyString = privKeyString.replace("\r\n", "");
privKeyByteArray = Base64.getDecoder().decode(privKeyString);
// ----------------------------------------------------------------------
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privKeyByteArray);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey myPrivKey = keyFactory.generatePrivate(keySpec);
return myPrivKey;
}

convert a public key from BigInteger into a pem file in Java

I can generate a pair of ecc private and public key pem files:
KeyPair keyPair = generateECCKeyPair();
ECPrivateKey priv = (ECPrivateKey) keyPair.getPrivate();
ECPublicKey pub = (ECPublicKey) keyPair.getPublic();
writePemFile(priv, "ECC PRIVATE KEY", "private.key");
writePemFile(pub, "ECC PUBLIC KEY", "public.pem");
but now I hava a public ecc key in a BigInteger variable,how can I covert it or wrap it into the pem form, which contains header and footer, and bewtween them is the base64 encoded der form of the public key? Here If I'm wrong about the pem file, please correct me.

OpenSSL and Java yield different SHA256 RSA signatures

I created a RSA keypair with OpenSSL:
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:4096
openssl rsa -pubout -in private_key.pem -out public_key.pem
These are the keys:
-----BEGIN PRIVATE KEY-----
MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQC7ZRktot/dOrNX
nyc8u1qtQM7Pb+OOLupSkqsVKeBaT1J/Yru9XF2EqlUo9aBSsjZLw1Q7TP8Ocj2s
Boj09otqgb9fjo/OPybhvr+z1FdmwRSShnZ5aGvNdM3eVklr98DrY+anYLPGE8GM
H3WL4tFKFtu2HK5E8NLLAuZx3SoNRnFJ2MZV4irf/vkdSOVRo9+u6f2Hj/0Swftq
SRfkF2gXJUUG0cesrFSPqYyFH5qbH6dDkwTAH27WcFt+X/YNkkzyC/wVtI/b0Ng1
Adjiqa84kxHADoeyPBmRNlCio3T4bx3hDYkSEhpRJQGMqEQdPTdU3lQBixCked3m
aOWW1+SGcqCvNnDAQsgdPa8PaQimYbefwQPDDT3fdKev/Vsip5/9B605QPONaRfO
IUg/Kfh/TL722t4ccFHZfXBKBF/JapMy0aY7e8U5CtkIu0Uh9Y2ZEHTaOMMxXCOK
UuO2rL4ki2MlnwC+zaduolKPPm8pI2tbVv2I9vtT3hSQOMrDFBeCtCVuv0lbu8Em
t+VhtPYvqbUvKlsoC0IzRhdnPhUjkJhZh6pnwxFBgkzpry+3mNQgTLAmHx86xROE
MqI7haMTXyakEKDEjM87Wl0fM8BqxxOILJ/1dp5iNAj7sn3F1EPhjwZHhswzlQQI
r68NPNvuGhYr0ch7ufezybKOlbKGJwIDAQABAoICAHKHiWng350gITfuJ1bw0qyM
rVkOcy8U8VViGnmmzF2lmTANsdw7tVQ9niqYZFHmZVTMnpBXsJKts4+NtuqwO+ju
P34xbSaKZkMWB6fqoThFzUL4NzRgY3KAbsnyRpFcLGYw+mlVqsnmjSYMnBTQnUUA
z2tv//pWjR5SOXFh0C+o29hcSmt2/OlFtg713GGXHECU9cSCdhOexv/giF95Br8O
oi9Wywh3kn7zCtTZye4A3INqOT8Kdv1MUj7ce080GR7dwEWrwzh/XYSYCmwNdO/I
wtJ0ju6Z2K5tWb7s07J6hvJjCWEn+zmYumey914/aa/O6dy/l35OlRYbCia08A9H
6udkJIOJrB/LX+GRFibHOEROLFGc58IDA0Pfty6BnQ19oPy2yFOVcN9IgnAzlYCk
8B/uwT234E3Eyp0sJ64okuo4w12G3myRzz9Lnt0KruC9pYCO0tFW1kgxO9OVdYbM
Tc1v8uU9Ho6/ajVDjXJGftXb1o2nHWKzS4rew2nOxdIOn83p8TO2Paheasf2x9D/
wP/rOua46TRuwIaIp3RS9E0PGACoRCBKUgydtf8TJL9ns92AYK4Yk2wsyGou5hmj
ImNv9C0KLHDmJSIu7EFhGh16teLA2885HpU1ayPeIRYkyl5Q8I4Js3zhYx36EYdb
BZEQR+NZliLMZ9DjcYDxAoIBAQDhm8qyQnLEjHCMGAOazBbRvfLMdBOLJ0mmueRp
sb5RGB5k+84wROjg8Dmhv5BkG13EXwbzaL2vwfCLl3ABOt4daPETQjaQBxwBDjR0
IZTGH5iqkn9s1giz9Yr+0iAl39+1PfcsiAupax2o88KerJxC8BkMlXasiNCaef4N
GEA4+JxKGO1TJV5xkHXXzSvl1HxBZbZn9Bgq/UnLNzyTpC8bkxEPXecc8orBr4kp
MOgHhU731CYMtMs1OegnBxjr40pYfBEKkXL7iXtnfhoNDetYdmYrfd5vCzHh1FzS
BXubutHwJL341i98oQyVCuMLKCfu8+h7IJ09WmYMRRNjmqGFAoIBAQDUo34YJEh3
4dqJLE3fmOq+6CdLz/IfvefJul1VCEOYqJ/x0K7AUr7GvGruwiL3yEShnaJE13mD
GkKE6PvRVlKLx+a7jjnaS2RTfGmtUrcxOEQ+1NRIHV/REvfT+QOroxiO4/Choz71
h11ZFNWCdRaFls2x5+Bm4d1J4OnR7BtGs+CsBX9Gy4j4bEr/FdsRNQEQvGH96e6a
kLsNp11eDTe65l/T7nlpgxFQtmMLDAkynUzcbM7rtV196z60VZ3OF7ojl+70eSdE
YzhOqfGuxEoYA5HswvOQxL82pa6PrkNjAaBIoe+kVohBINfYu8QNbY/qPiRDvnVj
8nJy0tpXpYK7AoIBAHiW41NA/UNdpdLcCyE0sYXOt/bcnI2mpFF/zz1HimiiMazc
gyt9ydRh0wxH7o/zqL0L5hU5CRrm2QJkIbQmmQ6zRxh6LdvyUN+njhX81R7JSLrz
rzSneuNl/bOS8SgNb+/afUHKPiGCZcom8QILVULaPylgwpwaTRYk2sx5B+rrx+47
QK9OrBqbksGUo0AnwXrbORJ7gGeKF2WN0s0/1wYwJ9BE6wGoCs7YWsIvp/ZNJRaG
yQfFo1CB8TrbKlD7noOJXTp+4CzfIZ0B5D7SD9erAZWBo+FK5JDHCA4ibB2jUA/C
M+DL63n8Q1NvDEisYBvzN3KIzkM7DIzGZ0S4JFkCggEAZd289ovLO8MzEivrUobL
TpGg4azI7XPoqrdhJu9CksuVdB7o2LHLOyHgiGjwn4uNdW8y4Y/oYmoeG9YRbQPR
n60wWAGWrYEYmvYrGiFaKXYvG3qSB8oqsNpJZ38PxxXnGe0Qxh6SdsVWbuqQLmMx
WuYXvHa9BgyGGnJpEWvu1TwvEbnAc4XmJDyc4P7zZ5bg38efmiiLafiT48Kd9ASZ
AiyDE+BWwblMfkIhyJUHgvvgamil6D2GgGBGNdYeNzEetxZ+Hxg7O4Pc4bKAWZ0n
ccGkEqyCSoQ2plNRAPwWxENvKdyKFUxvEeROA0JQda5bF3iVnu6LBkqLkX55oytj
KQKCAQEA2mm6ZBf63is/N0Z/fVYkeUE4K2MD1AVOUisNzXvkxDiw7RxrsQO9KA/9
AudcftN1STNoCSmDilm/4L4YTbKUrl0wj2kFZGOm8bH/RSWBHeI9ZrYpXBMBvh90
ZxhuRzYp7dynppH1QFijDt3kPMJq2Dw0ErCPds90JfztFLfZK2BikTcFkbUQia+z
hwhHs75o7RnLOse+kxHP0nci2lhxJD7MwZR8HKsLqZuv7VNPIigWSC9pFYWr/TM1
eYTR9iGpRLGN+MBwpZIr94z02nrl0GhNNoyq+skDjtJ2Bi66YIe/IJu86mIZu5Ma
FxHiQ4CS5eVAn5hotPLcR/0B56rCZw==
-----END PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAu2UZLaLf3TqzV58nPLta
rUDOz2/jji7qUpKrFSngWk9Sf2K7vVxdhKpVKPWgUrI2S8NUO0z/DnI9rAaI9PaL
aoG/X46Pzj8m4b6/s9RXZsEUkoZ2eWhrzXTN3lZJa/fA62Pmp2CzxhPBjB91i+LR
ShbbthyuRPDSywLmcd0qDUZxSdjGVeIq3/75HUjlUaPfrun9h4/9EsH7akkX5Bdo
FyVFBtHHrKxUj6mMhR+amx+nQ5MEwB9u1nBbfl/2DZJM8gv8FbSP29DYNQHY4qmv
OJMRwA6HsjwZkTZQoqN0+G8d4Q2JEhIaUSUBjKhEHT03VN5UAYsQpHnd5mjlltfk
hnKgrzZwwELIHT2vD2kIpmG3n8EDww0933Snr/1bIqef/QetOUDzjWkXziFIPyn4
f0y+9treHHBR2X1wSgRfyWqTMtGmO3vFOQrZCLtFIfWNmRB02jjDMVwjilLjtqy+
JItjJZ8Avs2nbqJSjz5vKSNrW1b9iPb7U94UkDjKwxQXgrQlbr9JW7vBJrflYbT2
L6m1LypbKAtCM0YXZz4VI5CYWYeqZ8MRQYJM6a8vt5jUIEywJh8fOsUThDKiO4Wj
E18mpBCgxIzPO1pdHzPAascTiCyf9XaeYjQI+7J9xdRD4Y8GR4bMM5UECK+vDTzb
7hoWK9HIe7n3s8myjpWyhicCAwEAAQ==
-----END PUBLIC KEY-----
Next, I created file containing my message:
echo "Hello, World" > message.txt
Then I signed the message with my private key and stored the signature to a file in base64:
openssl dgst -sha256 -sign private_key.pem -out signature message.txt
base64 signature > signature.txt
This is the signature of the message:
huMEGAIfj+RQ4XHLVYEkGlgbcz5Wv62QBWk6MsxbiDBIX1ZgjhjKY3zr8livAs3x4QjfOmxqs+hOGDo9einXlNWWinslD9q+0ScoJPauK2UuIbAzLsOFoUGJBlFvCALClDlP+oIkT8qLzVPL2gONln3G68p/yZdKIQldaVMDA3mQEBJgHLKxShW1iveqhXF9P6SijE6PstTZ21KEOgMKXVjqbA8EdYucWZv1IwllFXfWJuk36MrgcfGTDesjK95LgCaZ0MjQqsiQQFA3tmZSntE8oRs7UK2fBkx/RCoFIHut4szdaUUvvxLNXVq5hgEjxysvw8Pls86iC/kfu84fJL6Xc3z9vcw4j4H2u5iJS+XGCcW4Jk+2UNprkyLtTu3uWvz21ruIZTlPhDNXdMexr9DNf7WyIsIZ3zeM1/O/Jgx6j890jN7Tth+wIIqawWuSBjd5PT3WyAqOJNtc5u0EhKlNWkoMorHuf4hWjf07XMJuS3F7PMsOGYaEwHXpc1JNN2U94TKyyPBfEpecvlo84FjKW6cpb+PINQcOVh3UeXCInk32/T4/rv4Ro6DCFlNKGobOozRSTt9a8vnQ2zOgc8omZiQzpvU8o8i92tgYtY9bIrOmIBgD+Lm/eJctjcm/qFOcNwVqj6s9Zth/pMuW+/tLxV/bsdK0XNCjGV+zAqM=
This is how the same signature is created in Java:
Signature signature = Signature.getInstance("SHA256withRSA");
String privateKeyString = "-----BEGIN PRIVATE KEY-----" + paste the key from above +"-----END PRIVATE KEY-----";
privateKeyString = privateKeyString.replaceAll("\\n", "").replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "");
PKCS8EncodedKeySpec encodedPrivateKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyString));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(encodedPrivateKeySpec);
signature.initSign(privateKey);
signature.update("Hello, World".getBytes("UTF-8"));
byte[] signatureValue = signature.sign();
System.out.println(Base64.encodeBase64String(signatureValue));
And finally the Java output:
DYGB6T1QAKMjgyU++rh5Ee41ED0PtvOcU1Bz+WWUM82ONC42y0VSR3a2dyxsay0YL0U8UXHF7l5F51vxxayif1F8h3tsMsyV4PKVCgOYaJooTloOJT+R5oh7mywg1lCd/u9KYHYIc7x+MopDEfiCGWETMFHt7GUqRViV3PdTtYWzKy87GN74bnzQQMqmY+BH+IZhdpdt4IC7RFh9FCwLf7RBhfX3t/dhfFhdee34r/N8aQfxos9d23IDsvudtc0zlP2uluejDqUei4FQGdKuiLCBNQJGwK6yUDXIQ0N3sm19hmysqevsj/hmLPu1BC0Uj9ryxpdL8ViYE4Sj7fLZqUD/LyMbdO4a6WbLSlLbWnd4BYfzFND6919QcUGrcqb1W1Vz5Tso3mceNT8LUb0A69F6yVnbJX1zoQaUDbHnlStsKOopfVlmLgniOXOLlzoqTUzxwRUi8+Iv4EHTLZKP//CO4iAxXR+w/LihFuUTXtTZ+Fcxj1eRbeifyqmOqsGCvPHxjqvr5FvmadusyvgkM18UQvN8f/ohReokQqlr7iW0uUPtefSuffYgNxBjm1bE6kl+/xYE21A/annfdi3dDIeif9+/NM95NQOOD3/YEFaw+H2sbBnskdMmpu5P3QKms4ppJTkZjSPn+Meg5ZAW68Pu74WNFed6qs9SjvW0nQU=
My question: Why are the two signature different? I tried verifying both with OpenSSL and Java respectively and it succeeds in both cases. But of course, verifying the OpenSSL signature in Java fails.
If I add a \n:
signature.update("Hello, World\n".getBytes("UTF-8"));
I get:
huMEGAIfj+RQ4XHLVYEkGlgbcz5Wv62QBWk6MsxbiDBIX1ZgjhjKY3zr8livAs3x4QjfOmxqs+hOGDo9einXlNWWinslD9q+0ScoJPauK2UuIbAzLsOFoUGJBlFvCALClDlP+oIkT8qLzVPL2gONln3G68p/yZdKIQldaVMDA3mQEBJgHLKxShW1iveqhXF9P6SijE6PstTZ21KEOgMKXVjqbA8EdYucWZv1IwllFXfWJuk36MrgcfGTDesjK95LgCaZ0MjQqsiQQFA3tmZSntE8oRs7UK2fBkx/RCoFIHut4szdaUUvvxLNXVq5hgEjxysvw8Pls86iC/kfu84fJL6Xc3z9vcw4j4H2u5iJS+XGCcW4Jk+2UNprkyLtTu3uWvz21ruIZTlPhDNXdMexr9DNf7WyIsIZ3zeM1/O/Jgx6j890jN7Tth+wIIqawWuSBjd5PT3WyAqOJNtc5u0EhKlNWkoMorHuf4hWjf07XMJuS3F7PMsOGYaEwHXpc1JNN2U94TKyyPBfEpecvlo84FjKW6cpb+PINQcOVh3UeXCInk32/T4/rv4Ro6DCFlNKGobOozRSTt9a8vnQ2zOgc8omZiQzpvU8o8i92tgYtY9bIrOmIBgD+Lm/eJctjcm/qFOcNwVqj6s9Zth/pMuW+/tLxV/bsdK0XNCjGV+zAqM=

How to read EC Private key in java which is in .pem file format

How to read EC private key which is in. Pem file using JAVA. While reading I am getting the following exception.
Caused by: java.security.InvalidKeyException: IOException : version mismatch: (supported: 00, parsed: 01
Actually my. Pem file contains private key in the following structure.
----BEGIN EC PRIVATE KEY ------
====+====+===
====+====+===
-----END EC PRIVATE KEY-----
From an EC PRIVATE KEY as requested (ex key.pem), I succeeded to import it in a java.security.KeyStore
transform private key from PEM => PKCS#8 DER
openssl pkcs8 -in key.pem -inform PEM -topk8 -nocrypt -out key-pkcs8.der -outform DER
load it (jvm version java-1.8.0-openjdk-1.8.0.201.b09-2.fc28.x86_64)
void loadPrivateKey(KeyStore ks, X509Certificate cert){
File privKeyFile = new File("key-pkcs8.der");
// read private key DER file
DataInputStream dis = new DataInputStream(new FileInputStream(privKeyFile));
byte[] privKeyBytes = new byte[(int)privKeyFile.length()];
dis.read(privKeyBytes);
dis.close();
KeyFactory kf = KeyFactory.getInstance("EC");
// decode private key
PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(privKeyBytes);
PrivateKey privKey = kf.generatePrivate(privSpec);
ks.setKeyEntry("key-alias", privKey, "password".toCharArray(), new Certificate[] {cert});
}

Categories