Convert RSAPublicKey to PEM [duplicate] - java

How can I convert this RSA public key:
109120132967399429278860960508995541528237502902798129123468757937266291492576446330739696001110 6039072308886100726558188253585034290 57592827629436413108566029093628 2126359538366865626758497206207862794310902180176810615217550567108238764764442605581471797071 19674283982419152118103759076030616683978566631413
to *.pem file?

A "pem" file is not really a format, and different kinds of objects may be be stored in things called "pem" files. Most often these files contain either a base64-encoded X509 certificate or else a base64-encoded private key object.
Assuming you want an X509 certificate, you should next realize that a certificate consists of many fields, only one of which is the public key. So would need to decide on the values of the other fields. Finally, a certificate must be signed, with a private key.
PS. An RSA public key consists of a modulus and a public exponent. What is your public exponent?

If your hex string is just a simple convert from base64 to hex. Then you can reverse it. Here is the script I did (in PHP) to solve my case:
<?php
$list = array_slice($argv, 1);
foreach ($list as $file) {
$hex = str_replace("\n", "", file_get_contents($file));
$pem = str_replace(".hex", ".pem", $file);
$b64 = base64_encode(hex2bin($hex));
$fd = fopen($pem, 'w');
fprintf($fd, "-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY----\n", implode("\n", str_split($b64, 64)));
fclose($fd);
}
Given a list of .hex files, it convert back to ".pem". You can run it like this:
php script.php *.hex

I would use OpenSSL. Assuming that you have the key in DER format, you can use this command to convert from DER to PEM.
openssl x509 -inform der -in input.der -out output.pem
If you are not sure whether you have the correctly formatted DER (ASN.1) encoded key, try this tool to parse your input file.

First of all President James K. Polk is right. A complete RSA Key is formed by a modulus and a public exponent.
In Java, the interface RSAPublicKey from the package java.security.interfaces can hold well-formed RSA Keys. Assuming you have such object, the process of converting to a PEM format would involve encoding it, as follows:
String pemFormatKey = Base64.getEncoder().encodeToString(rsaPublicKey.getEncoded());

use putty programs, PuTTYgen does this conversion

Related

RSA decryption according to the private key reports an error

My code using RSA on the front end:
const rsa = new JSEncrypt();
rsa.setPublicKey(k);
const resultText = rsa.encrypt("violet");
console.log(resultText);
My code using RSA in the backend:
byte[] inputByte = org.apache.commons.codec.binary.Base64.decodeBase64(str.getBytes("UTF-8"));
byte[] decoded = org.apache.commons.codec.binary.Base64.decodeBase64(privateKey);
PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(new
PKCS8EncodedKeySpec(decoded));
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE,priKey);
String outStr=new String(cipher.doFinal(inputByte));
return outStr;
PublicKey like this:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA13gYdCmOjR9yqQD7ldzG
ZXabSon6SiLceCK6vRXf4NMbF+EQke0vRpqU3IZ/S1pFdvoQswQabsA4zf0WACVT
iaGIhWDlPu3mecri8rYtmOSfd8GCE0vEgFNvSD6IXRLPeLCB+i7WENBa4fCEtW8W
Hzdas96CLiESbjSAruRasQXP2OLqEA2GU83/069vh8uRKzui+yw0aAXZFyFyFRFa
lxYltFadVpz3+kBplvpzuj82t4fc3yCRbrpeRyTyX1sz0ULSxx/k3/p1OuJtIq9Y
9uN0G4gxhcDFJ4L41uXOln5CPapk7tlsYobhhvxYHw1rrweY+06hrQ7r0Hblv2nH
GQIDAQAB
-----END PUBLIC KEY-----
PrivateKey like this:
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEA13gYdCmOjR9yqQD7ldzGZXabSon6SiLceCK6vRXf4NMbF+EQ
ke0vRpqU3IZ/S1pFdvoQswQabsA4zf0WACVTiaGIhWDlPu3mecri8rYtmOSfd8GC
E0vEgFNvSD6IXRLPeLCB+i7WENBa4fCEtW8WHzdas96CLiESbjSAruRasQXP2OLq
EA2GU83/069vh8uRKzui+yw0aAXZFyFyFRFalxYltFadVpz3+kBplvpzuj82t4fc
3yCRbrpeRyTyX1sz0ULSxx/k3/p1OuJtIq9Y9uN0G4gxhcDFJ4L41uXOln5CPapk
7tlsYobhhvxYHw1rrweY+06hrQ7r0Hblv2nHGQIDAQABAoIBAAyqFmXde294BblB
QYhRbafRDNeYvIlW+zZkdC1g98OzJMiGhf7NvhWcSFud3CWFrMeNcyXSe+s+iRgy
Y/SmPP6969RLGa5VNVK7RhOV+aAe7/COAyM3NNmGDehlJIaz8FXbqggWcKaUWIMn
K+WuHdK/4ijoTyZ+8yJfG6Er8tisryLQ9io9+op9g/ZWzaUKgu934/cDxUt70bfm
x+ZEPi6YfkJ1uOpXnnadDyw2RUDcvCM3fK3KF5fqM7SJAXY9b1pmLr+Ccn1qkT9G
I+QHidEsGfJciX5AoHnlIMLPMVIPKBbq4GwC/Ngb41LprNJWlPR38N2ySjky/Jyt
159XWHECgYEA9lx2KfFmyLyVjnkIF3JI50mSZAw4YPBBqB27UInacvxXbjfVURht
xK60GB9OkDbFdeNh89x86Gfwvm5bTq4W8YSH4Obd5Fg8XjTuaicTi03CSfF5SdJn
JLLOUmlqP75gkbEPNUoOfqhqq6IbyJVB3egyL90cd2/wCdJOVLEUly8CgYEA3+Y4
lNdl0xedkDNkWsCyyA4iPSUzVxsuch9hW/VGBwzga8rtiNllpKifVvctQOEu/KUe
vVQRF78ojZaMGT33l6TivnUL54z9Lo9uWghoG8TqMfnG34pFPe3R+zvGP87Hrozw
1EUhiMT198SlB/YHrgGGGlJbG+rlm5GIx3lEdDcCgYA4RSw0LlA0v2ZFkX14pYDj
WxmVwnjKI3ZLqObU4XfE1cA+i4AssrC3wNOfwt7V77ywTYxc/9qD1uHVDS3LzdWt
uoCyrOi3tDOtrNdb5asAIXWkIAR9CRLH/hNEHZHIF3rFLDT2DgE7iso6g59m9DiE
L/nulsleunGQPLnpfDzgvwKBgDRV5Q3tl3CTSZJGYQPRnTikDR7LzkdjJCUq7qAH
IhpNyTuJEKL3ZgnqHGzAlERhHpGRqzDIMMKjPUBzW0YfNPuuYA3y4Bh83UV/42SK
KIOtMK0D3JeuA2dparbWDw4lMIm9iiGkEyWcHH6Q6Z6FxN/InWcTrxZEfu0xRI6T
6wtbAoGAfl5dW9LNoaNfQbgIq+PJKZm9f1frza55mFTJgo3ravdb3GmzWVHN8xRf
nLKyKyNLqbkT35IGc39NkALJLxT5RibkAZLiUiwqdMF63sgODbA9AGTmhe+JHS+V
hBmFnCyp6UiN9E4ZAWcZQILa0rRMftMFngAJ3El0ZP+HziRnNzs=
-----END RSA PRIVATE KEY-----
Bu-t, when i do the java code decryption, it reported such an error:
java.security.InvalidKeyException: IOException : DerInputStream.getLength(): lengthTag=111, too big.
How can i solve this problem ?
1. You are decoding wrong. PEM format has a dash-BEGIN line identifying the type of data, a block of base64 encoding the data, and a dash-END line. The BEGIN and END lines are part of the format, but they do not contain base64-encoded data; only the lines in between contain the base64-encoded data. You are apparently passing the whole thing, including the BEGIN and END lines, to commons.codec.Base64, which results in decoding a bunch of garbage before and after the actual data. That garbage isn't valid ASN.1 DER, so when Java tries to parse it as DER it fails.
2. Plus your data is not a PKCS8-clear privatekey. The PEM type 'RSA PRIVATE KEY' is an OpenSSL-defined format that contains a 'traditional' or 'legacy' format, namely the PKCS1 representation of the private key. This is not PKCS8, which is the only key format Java supports natively; that's why the spec class is named PKCS8EncodedKeySpec, because it is a key spec encoded as PKCS8 and more specifically PKCS8-clear. If you fix the above problem by removing the BEGIN and END lines before base64-decoding, Java can parse the result as DER, but not as a PKCS8-clear key; you get a different exception about 'algid parse error, not a sequence'. To fix this there are 5 approaches:
change whatever process you use to initially generate the keypair so it generates PKCS8, not OpenSSL-legacy PKCS1. Especially since you need anyway to replace the keypair you compromised by publishing it, as 207421 said. You give no clue what that process is or was, so I can't give any details.
convert your generated privatekey, or a copy, to PKCS8-clear. This is not programming or development and offtopic, but if you have or get OpenSSL (on the same or any accessible and secure system), you can do
openssl pkey -in oldfile -out newfile # 1.0.0 up only, but older is now rare
# or
openssl pkcs8 -topk8 -nocrypt -in oldfile -out newfile # even ancient versions
Once you have a PKCS8-clear file, just remove the BEGIN and END lines and base64-decode what is left, and pass that to KeyFactory as PKCS8EncodedKeySpec as you already do.
use https://www.bouncycastle.org . The 'bcpkix' jar has (Java) code to read a large range of OpenSSL-supported PEM formats, including the RSA-PKCS1 private key format you have. There are lots of existing Qs about this; just search for PEMParser and JcaPEMKeyConverter.
convert it yourself. Decode the body of the file you have, after removing the BEGIN and END lines, to get the PKCS1 key, then build the PKCS8 format for that key, and then pass it to KeyFactory as PKCS8EncodedKeySpec. See answers by Noa Resare and Jean-Alexis Aufauvre on Getting RSA private key from PEM BASE64 Encoded private key file or mine in Java: Convert DKIM private key from RSA to DER for JavaMail .
do it entirely yourself. Decode the file you have without BEGIN/END to get PCKS1, parse that as DER following e.g. RFC8447, and build RSAPrivateCrtKeySpec. Some other As on the Q I linked just above do this. However, this requires either: using undocumented internal sun.* classes, which used to work in Java (hence the existing answers) but which 'modular' Java versions (9 up) since 2017 have steadily made more difficult or impossible; using BouncyCastle which has documented (and good) support for ASN.1 -- but then it's easier to use bcpkix for the whole job as above; or writing your own ASN.1 parsing, which is a good deal of work.
PS: encrypting text with RSA is usually a bad design; it's not suited for that. But that's not really a programming issue and doesn't belong here.

What is/are the equivelant openssl command(s) to do the following in Java code

Hi I have the following java code.
What is the Openssl commands equivalent. I want to be able to run from OS command line.
This is what i think the openssl command equivalent is... but I am not entirely sure.
openssl dgst -sha256 -sign my_private.key -out /tmp/sign.sha256 codeTosign.txt
When i try both and then compare them
sign.sha256
compare to output from
System.out.println("Signature="+base64Signature);
they are not equal / do not match.
import java.security.KeyFactory;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
public class SHA256RSA {
public static void main(String[] args) throws Exception {
String input = "sample input";
// Not a real private key! Replace with your private key!
String strPk = "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9"
+ "w0BAQEFAASCBKkwggSlAgEAAoIBAQDJUGqaRB11KjxQ\nKHDeG"
+ "........................................................"
+ "Ldt0hAPNl4QKYWCfJm\nNf7Afqaa/RZq0+y/36v83NGENQ==\n"
+ "-----END PRIVATE KEY-----\n";
String base64Signature = signSHA256RSA(input,strPk);
System.out.println("Signature="+base64Signature);
}
// Create base64 encoded signature using SHA256/RSA.
private static String signSHA256RSA(String input, String strPk) throws Exception {
// Remove markers and new line characters in private key
String realPK = strPk.replaceAll("-----END PRIVATE KEY-----", "")
.replaceAll("-----BEGIN PRIVATE KEY-----", "")
.replaceAll("\n", "");
byte[] b1 = Base64.getDecoder().decode(realPK);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(b1);
KeyFactory kf = KeyFactory.getInstance("RSA");
Signature privateSignature = Signature.getInstance("SHA256withRSA");
privateSignature.initSign(kf.generatePrivate(spec));
privateSignature.update(input.getBytes("UTF-8"));
byte[] s = privateSignature.sign();
return Base64.getEncoder().encodeToString(s);
}
}
references : https://www.quickprogrammingtips.com/java/how-to-create-sha256-rsa-signature-using-java.html
Aside from the difference in input (an in-memory variable versus file contents, which I assume you have made exactly the same), your Java code base64 encodes the result (and sticks Signature= on the front) but openssl dgst -sign does not.
The simplest way is to have Java write the binary (without encoding it), but then you have to compare in binary, for example cmp on Unix or fc /b on Windows; a visual or text-mode comparison will not give reliable results.
You can instead base64-encode the OpenSSL output, but be careful: there are many base64 encoding schemes, which often produce similar but not identical output, thus the output may compare unequal even when the signatures are the same (see below). In particular if that Base64 is Java8+ java.util.Base64 then the default 'basic' encoder uses the classic PEM/MIME alphabet and no line terminators. If you pipe the output of openssl dgst -sign into openssl base64 it defaults to the same alphabet but line terminators every 64 characters. If you use openssl base64 -A it should suppress the line terminators. If you have other base64-encoding programs on your system, they may (each) behave differently. If you really want the Signature= on the front, use any appropriate text-manipulation program like sed awk perl or even most shells on Unix.
Aside: you should not assume you can verify a signature by comparing it to a newly-generated one. The scheme used here, RSASSA-PKCS1-v1_5 aka 'classic type 01', happens to be deterministic, and comparing happens to work, but many other digital signature schemes are not deterministic. The correct way to verify a signature is to apply the verification process defined by the signature scheme. For examples, Java Signature.initVerify(pubkeyobj), update(databuf), verify(signaturebuf) or OpenSSL commandline dgst -$hash <datafile -verify pubkeyfile -signature file .
Thanks every one for help.
The ultimate problem i had in the end was when i copied an pasted string
"sample input" it was appending a "\n".
When this was removed. The output of the openssl command matched the output of the java snippet.
Thanks all.

Display Android Certificate

I have a short question: What does this call return exactely?
context.getPackageManager().getPackageInfo(context.getPackageName(), GET_SIGNATURES).signatures[0].toByteArray();
I know it returns the first app certificate for the app which is the CERT.RSA in the META-INF folder, but what exately does it return? Just a byte-array which represents the whole certificate as the file or some other byte-array? I don't really know much about the structure of certificates and the data they contain so I really don't have any clue.
The best answer would be an instruction for openssl with that I get the returned value from the above code line.
I finally tested it myself on an android simulator and got the final answer. It's actually not hard to understand once I realized that PKCS7 is just a storage-form or rather a container for various signature-types.
Within the app
The call returns the first signature within the CERT.RSA file. It's a PKCS7 file which embeds the X.509-certificate and from what I've read it's always just one signature for android apps.
Signature sig = context.getPackageManager().getPackageInfo(context.getPackageName(), GET_SIGNATURES).signatures[0];
This Signature obtained from above can be directly used to generate a working X.509-certificate like this (taken from here):
byte[] rawCert = sig.toByteArray();
InputStream certStream = new ByteArrayInputStream(rawCert);
CertificateFactory certFactory;
X509Certificate x509Cert;
try {
certFactory = CertificateFactory.getInstance("X509");
x509Cert = (X509Certificate) certFactory.generateCertificate(certStream);
//do stuff with your certificate
} catch(Exception ex) {
//handle exception
}
Anywhere else
If you have the certificate outside of your own android app and want the same byte-stream, that is provided by the function above you can do the same with a simple Java-program like this:
FileInputStream is = new FileInputStream("CERT.RSA");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate c = (X509Certificate) cf.generateCertificates(is).toArray()[0];
byte[] rawCert = c.getEncoded();
This code first reads the file, creates the CertificateFactory and then the important step it to isolate the first certificate in the PKCS7-container. And then c.getEncoded() finally gives you the exact same representation as the method above.
openssl
And last but not least the openssl-command for it(taken from here):
openssl pkcs7 -inform DER -in CERT.RSA -print_certs -text
It will give you a pretty overview of the information contained and at the end the
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
block. It contains the same data as above. If you parse the contents of this block and decode it with base64 it will give you the exact same byte array as in the upper two examples.
context.getPackageManager().getPackageInfo(context.getPackageName(), GET_SIGNATURES).signatures[0]
Would return the representation of the signing certificate associated with an application package. It would be an instance of a Signature class as defined here in the documentation. (The name 'Signature' is slightly misleading and even mentioned in the documentation itself).
context.getPackageManager().getPackageInfo(context.getPackageName(), GET_SIGNATURES).signatures[0].toByteArray();
Would return its byte Array representation. ie: The certificate file's byte array representation.
Adding to that, a certificate is nothing new than a text file, but the data is structured into a common format and encoded. X.509 is one of the most widely used formats. You can find it here. The RFC for a X.509 certificate is here. Certificates are structured such that they can be easily checked if altered by a 3rd party.
There isn't any openssl command which directly returns a byte[]. The closest which I could think of is the openssl command which you can use to get the textual representation of a certificate.
$ openssl x509 -in <your-certificate> -noout -text
The signatures member of PackageInfo is simply an array of all signatures read from the package file.
public Signature[] signatures;
The class android.content.pm.Signature stores a signature in the form
private final byte[] mSignature;
This is what toByteArray() does:
/**
* #return the contents of this signature as a byte array.
*/
public byte[] toByteArray() {
byte[] bytes = new byte[mSignature.length];
System.arraycopy(mSignature, 0, bytes, 0, mSignature.length);
return bytes;
}
So essentially this code simply provides you the byte array of a signature used to sign the package.
Assuming CERT.RSA is the RSA file in META-INF folder of the apk, you can get the package MD5, SHA1, and SHA256 signatures through:
keytool -printcert -file CERT.RSA

What is the openssl equivalent of using java's KeyPairGenerator?

Firstly I tried to ask this on security -- I got some upvotes but it seems it has had no answer for a week now. I understand that this is openssl related, however it stems from using a java KeyPairGenerator object so I feel it may be valid for stack overflow. Please see the code below:
I have been using java's KeyPairGenerator in order to generate public/private keys within a program so that I can encrypt and decrypt files(also using java encrypt/decrypt methods). I want to be able to move over to using openssl in order to generate these public private keypairs however I keep getting padding exceptions when I decrypt my files if i used command line generated openssl keys. For example instead of using java's KeyPairGenerator, I try and use openssl to generate keys:
openssl rsa -in keypair.pem -outform DEF -pubout -out public.der
openssl pkcs8 -topk8 -nocrypt -in keypair.pem -outform DER -out private.der
Where I try to use the DER files to encrypt/decrypt my files. Ultimately every key format I have tried seems to give me problems.
I'm assuming that this means that the format of the keys in my openssl commands do not match up how java's KeyPairGenerator works. Here is a snippet of my key generating code:
public void createPublicPrivateKeys() throws IOException, NoSuchAlgorithmException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
logger.info("Password to encrypt the private key: ");
String password = in.readLine();
logger.info("Generating an RSA keypair.");
// Create an RSA key key pair
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(4096);
KeyPair keyPair = keyPairGenerator.genKeyPair();
logger.info("Done generating the keypair.\n");
// Write public key out to a file
String publicKeyFilename = "publicKey.der";
logger.info("Public key filename: " + publicKeyFilename);
// Get encoded form of the public key for future usage -- this is X.509 by default.
byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
// Write the encoded public key
FileOutputStream fos = new FileOutputStream(publicKeyFilename);
fos.write(publicKeyBytes);
fos.close();
String privateKeyFilename = "privateKey.der";
logger.info("Private key filename: " + privateKeyFilename);
// Get the encoded form -- PKCS#8 by default.
byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
// Encrypt the password
byte[] encryptedPrivateKeyBytes = new byte[0];
try {
encryptedPrivateKeyBytes = passwordEncrypt(password.toCharArray(), privateKeyBytes);
} catch (Exception e) {
e.printStackTrace();
}
fos = new FileOutputStream(privateKeyFilename);
fos.write(encryptedPrivateKeyBytes);
fos.close();
}
What is the openssl equivalent command line statement of using java's standard KeyPairGenerator? Also note that using outside packages such as bouncy castle is not an option.
OpenSSL commandline rsa or pkcs8 do not generate a keypair; they only convert from one form to another and/or display. For RSA (only), genrsa generates a keypair, in OpenSSL's 'legacy' privatekey format which is not (easily) compatible with Java. Your rsa and pkcs8 commands do convert the legacy format to the "X.509" (SPKI) and PKCS#8 DER formats Java prefers. OpenSSL since 1.0.0 also has genpkey which generates keys, or where applicable parameters, for all supported asymmetric algorithms including RSA, and defaults to PKCS#8 output.
Your Java code has a passwordEncrypt step that is not standard Java and not explained. OpenSSL library supports password-based encryption of PKCS#8 according to that standard if that is what your passwordEncrypt does, but most OpenSSL commandline functions don't.
If you are trying to use the private.der of your example in Java which is trying to do any kind of password-based (or other) decryption on it, that won't work because it isn't encrypted; it isn't even in the PKCS#8 structure used for an encrypted key.
However that error would occur before you even try to decrypt any data, or more likely a working key.

How can I read a BouncyCastle private key PEM file using JCA? [duplicate]

This question already has an answer here:
convert openSSH rsa key to javax.crypto.Cipher compatible format
(1 answer)
Closed 6 years ago.
In one of our applications private keys are stored using BouncyCastle's PEMWriter. At the moment I am investigating if we can get rid of the BouncyCastle dependency since Java 7 seems to have everything we need. The only issue is that I can not read the private keys stored in the database as PEM-encoded strings (the certificates/public keys are fine).
If I save the PEM-encoded string of the private key from the database to a file I can run OpenSSL to convert the key to PKCS#8 format like this:
openssl pkcs8 -topk8 -inform PEM -outform DER \
-in private_key.pem -out private_key.der -nocrypt
The resulting output I can base64 encode and then read using this bit of Java/JCA code:
byte[] privateKeyBytes =
DatatypeConverter.parseBase64Binary(privateKeyDERcontents);
PrivateKey prKey =
KeyFactory.getInstance("RSA").
generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
This private key matches the public key stored as expected, i.e. I can round-trip from plaintext to ciphertext and back.
The question I have is: can I directly read the original PEM encoding somehow?
EDIT
Here is a bit of code that reads the strings in question using BouncyCastle:
if (Security.getProvider("BC") == null) {
Security.addProvider(new BouncyCastleProvider());
}
PEMReader pemReader = new PEMReader(new StringReader(privateKeyPEM));
KeyPair keyPair = (KeyPair) pemReader.readObject();
PrivateKey key = keyPair.getPrivate();
The "privateKeyPEM" is the PEM encoded string in the database, otherwise this example is self-contained. Interestingly it already uses the JCA KeyPair object as output. To rephrase my original question: can I do the equivalent of the code above without depending on PEMReader (and in turn quite a few other BouncyCastle classes)?
Key inside of PEM file is already stored in PKCS#8 format, so if it is not encrypted with password you can just remove headers (-----BEGIN RSA PRIVATE KEY-----), Base64-decode input, and get the needed bytes.

Categories