I am trying to read the RSA public and private keys files in Java.
My RSA public and private key is generated using PuttyGen. (SSH-2 RSA, 1024 bits)
The code I am using for reading file is:
//public key
pubkeyBytes = getBytesFromFile(new File(pubKeyfileName));
KeySpec pubSpec = new X509EncodedKeySpec(pubkeyBytes);
RSAPublicKey pubKey =(RSAPublicKey) rsakeyFactory.generatePublic(pubSpec);
//private key
privkeyBytes = getBytesFromFile(new File(privKeyfileName));
PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(privkeyBytes);
PrivateKey privKey = rsakeyFactory.generatePrivate(privSpec);
It throws:
java.security.InvalidKeyException: invalid key format
at sun.security.x509.X509Key.decode(Unknown Source)
Putty uses its own key format. You need to export the Putty key to the OpenSSH format - see How to convert SSH keypairs generated using PuttyGen(Windows) into key-pairs used by ssh-agent and KeyChain(Linux).
Then, you need to convert the OpenSSH key to pkcs8 format - see How to Load RSA Private Key From File. The Cygwin version of openssh will work just fine for this; there's no need to find a Unix system to run openssh.
Related
I am generating a DSA key with the below command:
ssh-keygen -t dsa
Then I try to sign data using bouncycastle API like that:
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
String privateKeyContent = // the content of the generated file
//init privateKey
byte[] pemContent = null;
PEMParser pemParser = new PEMParser(new StringReader(privateKeyContent));
Object pemObject = pemParser.readObject(); // throws
And getting this exception
java.io.IOException: unrecognised object: OPENSSH PRIVATE KEY
So I have been trying to convert the key file to PEM, using this example, and executing:
ssh-keygen -e -f key -m PEM > key.pem
But I am getting an error:
do_convert_to_pem: unsupported key type DSA
Any ideas on how to solve this?
There are a few things going on here.
You are generating keys using a pretty recent version of OpenSSH (which is good). These are now output in OpenSSH's new key format which the BouncyCastle API does not recognise as its a custom format.
You are generating a DSA key. OpenSSH deprecated use of DSA as it's not considered as secure as the other private key types provided like RSA, ECDSA, ED25519 etc. So whilst its letting you generate the key; its not letting you convert it.
I would recommend that you change the key type to an RSA key with 2048 bits (minimum). That will, however, not stop the BouncyCastle API error because it will still be in the new OpenSSH format.
It really depends on what you are doing with the key. If you not using it within an SSH API to authenticate to remote servers and simply want to sign data with BouncyCastle API then you would be better off generating the key using OpenSSL with the command
openssl genrsa -out private.pem 2048
This key should then be recognised by the BouncyCastle API.
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.
Is it possible decrypt an encrypted RSA (or others, shouldn't matter) private keys using JCE and/or BouncyCastle provider (not using openssl bundle)?
I can read unencrypted keys just fine using PrivateKeyFactory.
Googling this gets me through examples of using PEMReader (from BC openssl bundle) that has a password applied to it, but - don't want to use openssl bundle, don't necessarily want to use PEM format, and I can decode PEM using PemReader (from provider bundle). It's what can I do with it afterwards is the question.
I'm looking for some mega-function, or a series thereof that can do it, i.e. I am not looking into parsing the ASN1 of the encrypted key, figuring out the encryption method, passing the input through the cipher, etc.
If you have an encrypted PKCS#8 key in binary format (i.e. not in PEM format) the following code shows how to retrieve the private key:
public PrivateKey decryptKey(byte[] pkcs8Data, char[] password) throws Exception {
PBEKeySpec pbeSpec = new PBEKeySpec(password);
EncryptedPrivateKeyInfo pkinfo = new EncryptedPrivateKeyInfo(pkcs8Data);
SecretKeyFactory skf = SecretKeyFactory.getInstance(pkinfo.getAlgName());
Key secret = skf.generateSecret(pbeSpec);
PKCS8EncodedKeySpec keySpec = pkinfo.getKeySpec(secret);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(keySpec);
}
If you have a PEM format, remove the header (first line), the footer(last line) et convert the remaining content from base64 to regular byte array.
Goal: Extract an RSA private key from an encrypted PEM file. The private key will be used to programmatically sign certs.
Environment: Java 8 and Bouncy Castle 1.52
Use Bouncy Castle PEMParser (results in PKCSException)
//Register BC as a crypto provider
Security.addProvider(new BouncyCastleProvider());
//Get file handle
String caPrivateKeyFname = "cakey.pem";
FileInputStream fis = null;
try {
fis = new FileInputStream(caPrivateKeyFname);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//Load and parse PEM object
PEMParser pemRd = new PEMParser(new InputStreamReader(fis));
Object objectInPemFile = pemRd.readObject();
//I do not know why BC loads the file as a PKCS8 object. OpenSSL does not recognize it as such.
PKCS8EncryptedPrivateKeyInfo keyInfo = (PKCS8EncryptedPrivateKeyInfo) objectInPemFile;
//Decrypt the private key
String pwd = "secret";
InputDecryptorProvider pkcs8Prov = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(pwd.toCharArray());
//Next statement raises an exception.
PrivateKeyInfo privateKeyInfo = keyInfo.decryptPrivateKeyInfo(pkcs8Prov);
Exception in thread "main" org.bouncycastle.pkcs.PKCSException: unable to read encrypted data: 1.2.840.113549.1.5.13 not available: Illegal key size
at org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo.decryptPrivateKeyInfo(Unknown Source)
at org.codice.ddf.certificate.SignedCertificate.main(SignedCertificate.java:67)
Caused by: org.bouncycastle.operator.OperatorCreationException: 1.2.840.113549.1.5.13 not available: Illegal key size
at org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder$1.get(Unknown Source)
... 2 more
Caused by: java.security.InvalidKeyException: Illegal key size
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1039)
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1060)
at javax.crypto.Cipher.implInit(Cipher.java:809)
at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
at javax.crypto.Cipher.init(Cipher.java:1539)
at javax.crypto.Cipher.init(Cipher.java:1470)
... 3 more
* UPDATE *
openssl asn1parse -in cakey.pem
PBES2
PBKDF2
des-ede3-cbc
If I understand correctly, DES key length is 64 bits, so 3DES is 192 bit keys.
However, Java policy limits the length of DES keys to 56 bits, so maximum key length for 3DES is 168 bits.
Switching out the jurisdiction policy files is not an option for me. I think there is a way to decrypt a PKCS8 encrypted private key with Bouncy Castle, but the solution is more involved than setting the provider to BC.
Can anyone point me to an example or provide some reference code?
(Thanks to dave_thompson_085 for his help).
Yes, a file that begins with the line "-----BEGIN ENCRYPTED PRIVATE KEY-----" is a PEM format PKCS#8 encrypted private key. I don't know why your comment says "OpenSSL does not recognize it"; the fact that openssl rsa reads it successfullly proves OpenSSL does recognize it.
BC PKCSException is just a wrapper; your actual problem is java.security.InvalidKeyException: Illegal key size. This occurs if you try to use better than 128-bit symmetric encryption or (as here) decryption in a JRE that has the default shipped-by-Sun-now-Oracle crypto policy which is limited to 128-bit symmetric. I'll bet you a dollar that openssl asn1parse <cakey.pem shows the file encrypted with PBES2 using PBKD2 (with some parameters) and aes-192-cbc or aes-256-cbc.
Download the "... (JCE) Unlimited Strength Jurisdiction Policy Files for JDK/JRE 8" from the "Additional Resources" section of http://www.oracle.com/technetwork/java/javase/downloads/index.html then unzip and place the two *policy.jar files in the JREHOME/lib/security directory of the JRE you are using. (Assuming you are not under US sanctions because you're in the North Korea, Iran or Syria governments etc.)
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.