I have a RSA public key file like this:
-----BEGIN RSA PUBLIC KEY-----
this is content
-----END RSA PUBLIC KEY-----
and i use java to read it:
KeyFactory factory = KeyFactory.getInstance("RSA");
KeySpec spec = new X509EncodedKeySpec(bytesFromThisFile); // bytesFromThisFile is created and filled correctly
PublicKey publicKey = factory.generatePublic(spec);
then i get an exception:
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format
How to read the file properly? Is there a way to convert this rsa public key file to a java-readable format?
Try this method:
/**
* reads a public key from a file
* #param filename name of the file to read
* #param algorithm is usually RSA
* #return the read public key
* #throws Exception
*/
public PublicKey getPemPublicKey(String filename, String algorithm) throws Exception {
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
String temp = new String(keyBytes);
String publicKeyPEM = temp.replace("-----BEGIN PUBLIC KEY-----\n", "");
publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");
BASE64Decoder b64 = new BASE64Decoder();
byte[] decoded = b64.decodeBuffer(publicKeyPEM);
X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance(algorithm);
return kf.generatePublic(spec);
}
source: Load RSA public key from file
This is my implementation; can read PEM with key or certificate.
Tested only in java11.
/**
* reads a public key from a file
* #param f file to read
* #return the read public key
* #throws Exception
*/
public static PublicKey getPublicKeyFromPem(File f)
throws Exception
{
byte[] keyBytes = Files.readAllBytes(f.toPath());
String temp = new String(keyBytes);
String publicKeyPEM = temp;
if(temp.contains("-----BEGIN PUBLIC KEY-----"))
{
publicKeyPEM = temp
.replace("-----BEGIN PUBLIC KEY-----\n", "")
.replace("-----END PUBLIC KEY-----", "")
.trim();
}
else if(temp.contains("-----BEGIN RSA PUBLIC KEY-----"))
{
publicKeyPEM = temp
.replace("-----BEGIN RSA PUBLIC KEY-----\n", "")
.replace("-----END RSA PUBLIC KEY-----", "")
.trim();
}
else if(temp.contains("-----BEGIN CERTIFICATE-----"))
{
CertificateFactory fact = CertificateFactory.getInstance("X.509");
try (FileInputStream is = new FileInputStream(f))
{
X509Certificate cer = (X509Certificate) fact.generateCertificate(is);
return cer.getPublicKey();
}
}
Base64.Decoder b64 = Base64.getDecoder();
byte[] decoded = b64.decode(publicKeyPEM);
X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(spec);
}
Related
public static RSAPrivateKey readPrivateKey(File file) throws Exception {
String key = new String(Files.readAllBytes(file.toPath()), Charset.defaultCharset());
String privateKeyPEM = key.replace("-----BEGIN PRIVATE KEY-----", "")
.replaceAll(System.lineSeparator(), "").replace("-----END PRIVATE KEY-----", "");
byte[] encoded = Base64.decodeBase64(privateKeyPEM);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
}
// to call above code
// private.key in PEM format
File file = new File("private.key");
PrivateKey key = readPrivateKey(file);
Above is my sample code, i am getting below error. I have a pem file and want to extract RsaKrivateKey from it in java.
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : DerInputStream.getLength(): lengthTag=40, too big
I have a String stored in a variable:
-----BEGIN RSA PUBLIC KEY-----
MIGHAoGBANAahj75ZIz9nXqW2H83nGcUao4wNyYZ9Z1kiNTUYQl7ob/RBmDzs5rY
mUahXAg0qyS7+a55eU/csShf5ATGzAXv+DDPcz8HrSTcHMEFpuyYooX6PrIZ07Ma
XtsJ2J4mhlySI5uOZVRDoaFY53MPQx5gud2quDz759IN/0gnDEEVAgED
-----END RSA PUBLIC KEY-----
I generate public key as:
public static PublicKey getFromString(String keystr) throws Exception
{
//String S1= asciiToHex(keystr);
byte[] keyBytes = new sun.misc.BASE64Decoder().decodeBuffer(keystr);
ASN1InputStream in = new ASN1InputStream(keyBytes);
DERObject obj = in.readObject();
RSAPublicKeyStructure pStruct = RSAPublicKeyStructure.getInstance(obj);
RSAPublicKeySpec spec = new RSAPublicKeySpec(pStrcut.getModulus(), pStruct.getPublicExponent());
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(spec);
}
How to generate the PrivateKey using bouncy castle in android
{Edit}
Without using bouncy castle i am generating private key like this:
public static PrivateKey getKey(String mKey){
try{
// Read in the key into a String
StringBuilder pkcs8Lines = new StringBuilder();
BufferedReader rdr = new BufferedReader(new StringReader(mKey));
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 RSA PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replace("-----END RSA PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replaceAll("\\s+","");
// Base64 decode the result
byte [] pkcs8EncodedBytes = Base64.decode(pkcs8Pem, Base64.DEFAULT);
// extract the private key
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8EncodedBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privKey = kf.generatePrivate(keySpec);
System.out.println(privKey);
return privKey;
}catch (Exception ex){
ex.printStackTrace();
}
return null;
}
I want to achieve the same using Bouncy Castle
I'm a little confused on why you insist on using bouncycastle, but if you really want to use bouncycastle then the CMS/PKIX library has a nice helper class called PEMParser that will shorten the code needed, e.g:
public static PrivateKey getPemPrivateKey(String mKey) throws Exception {
PEMParser pemParser = new PEMParser(new StringReader(mKey));
final PEMKeyPair pemKeyPair = (PEMKeyPair) pemParser.readObject();
final byte[] encoded = pemKeyPair.getPrivateKeyInfo().getEncoded();
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(new PKCS8EncodedKeySpec(encoded));
}
In command line I can encrypt and decrypt my file with openssl as following:
Create Keys:
openssl req -x509 -newkey rsa:2048 -keyout myKey.key -out myKey.crt -pubkey
Encrypt:
cat myText.txt | openssl rsautl -encrypt -inkey myKey.crt -pubin >> encryptedtxt.enc
Decrypt:
openssl rsautl -decrypt -inkey myKey.key -in encryptedtxt.enc > decryptedtxt.txt
I followed the this tutorial
public static void main(String[] args) {
String encryptedData = "..\\encryptedtxt.enc";
File encryptedFIle = new File(encryptedData);
try {
byte[] data = Files.readAllBytes(encryptedFIle.toPath());
PrivateKey privateKey = getPrivateKey();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedData = cipher.doFinal(data);
System.out.println(">" + new String(decryptedData));
} catch (Exception e) {
e.printStackTrace();
}
}
private static PrivateKey getPrivateKey() {
String privateKeyFilename = "\\myKey.key";
FileInputStream fis = null;
ObjectInputStream ois = null;
File privateKeyFile = new File(privateKeyFilename);
try {
fis = new FileInputStream(privateKeyFile);
ois = new ObjectInputStream(fis);
BigInteger modulus = (BigInteger) ois.readObject();
BigInteger exponent = (BigInteger) ois.readObject();
RSAPrivateKeySpec rsaPrivateKeySpec = new RSAPrivateKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PrivateKey privateKey = fact.generatePrivate(rsaPrivateKeySpec);
return privateKey;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
When this is run, following issue occurs:
java.io.StreamCorruptedException: invalid stream header: 2D2D2D2D
at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:857)
at java.io.ObjectInputStream.<init>(ObjectInputStream.java:349)
at Decryptor.getPrivateKey(TestClass.java:38)
at Decryptor.main(TestClass.java:20)
java.security.InvalidKeyException: No installed provider supports this key: (null)
at javax.crypto.Cipher.chooseProvider(Cipher.java:893)
at javax.crypto.Cipher.init(Cipher.java:1249)
at javax.crypto.Cipher.init(Cipher.java:1186)
at Decryptor.main(TestClass.java:22)
Any suggestion please how I can I solve this?
UPDATE:
I modified my getting private key method as following:
private static PrivateKey getPrivateKey() {
String privateKeyFilename = "myKey.key";
FileInputStream fis = null;
ObjectInputStream ois = null;
File privateKeyFile = new File(privateKeyFilename);
try {
String key = readFileAsString(privateKeyFilename);
BASE64Decoder b64 = new BASE64Decoder();
byte[] pkcs8EncodedBytes = b64.decodeBuffer(key);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8EncodedBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privKey = kf.generatePrivate(keySpec);
System.out.println(privKey);
return privKey;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
But then the following error is thrown:
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : Short read of DER length
at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:217)
at java.security.KeyFactory.generatePrivate(KeyFactory.java:372)
at Decryptor.getPrivateKey(TestClass.java:61)
at Decryptor.main(TestClass.java:19)
Caused by: java.security.InvalidKeyException: IOException : Short read of DER length
at sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:351)
at sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:356)
at sun.security.rsa.RSAPrivateCrtKeyImpl.<init>(RSAPrivateCrtKeyImpl.java:91)
at sun.security.rsa.RSAPrivateCrtKeyImpl.newKey(RSAPrivateCrtKeyImpl.java:75)
at sun.security.rsa.RSAKeyFactory.generatePrivate(RSAKeyFactory.java:316)
at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:213)
... 3 more
java.security.InvalidKeyException: No installed provider supports this key: (null)
at javax.crypto.Cipher.chooseProvider(Cipher.java:893)
at javax.crypto.Cipher.init(Cipher.java:1249)
at javax.crypto.Cipher.init(Cipher.java:1186)
at Decryptor.main(TestClass.java:21)
You are trying to read the key using an ObjectInputStream. This class is not meant for general purpose decoding; it only decodes a Java-specific serialization format. The error you are seeing is the ObjectInputStream informing you that the data that you are reading is not a serialized Java object.
The key file generated by OpenSSL is not a Java-serialized object. Instead, it uses PEM encoding. For more information on reading keys from a PEM file, have a look at Decrypting an OpenSSL PEM Encoded RSA private key with Java?
I am a newbie to site. I have folowed this post How to Encrypt or Decrypt a File in Java?
and have everything functional (also following: http://www-users.york.ac.uk/~mal503/lore/pkencryption.htm and the previous stackoverflow post).
This may be a stupid question but where is the encrypted file after compiling? I cannot find it. How can I output it to either my desktop or src folder in java eclipse? I am trying to eventually turn this into an application where you can input a file at command line for encryption and then whenever you like decryption (strictly educational, for a class). Thank you for your time and help.
My Code:
/**
* Utility class for encrypting/decrypting files.
*
*/
public class CryptoFunction {
public static final int AES_Key_Size = 256;
Cipher pkCipher, aesCipher;
byte[] aesKey;
SecretKeySpec aeskeySpec;
/**
* Constructor: creates ciphers
*/
public CryptoFunction() throws GeneralSecurityException {
// create RSA public key cipher
pkCipher = Cipher.getInstance("RSA");
// create AES shared key cipher
aesCipher = Cipher.getInstance("AES");
}
/**
* Creates a new AES key
* A random AES key is generated to encrypt files.
* A key size (AES_Key_Size) of 256 bits is standard for AES
*/
public void makeKey() throws NoSuchAlgorithmException {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(AES_Key_Size);
SecretKey key = kgen.generateKey();
aesKey = key.getEncoded();
aeskeySpec = new SecretKeySpec(aesKey, "AES");
}
/**
* Decrypts an AES key from a file using an RSA private key
*/
#SuppressWarnings("resource")
public void loadKey(File in, File privateKeyFile) throws GeneralSecurityException, IOException {
// read private key to be used to decrypt the AES key
byte[] encodedKey = new byte[(int)privateKeyFile.length()];
new FileInputStream(privateKeyFile).read(encodedKey);
// create private key
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey pk = kf.generatePrivate(privateKeySpec);
// read AES key
pkCipher.init(Cipher.DECRYPT_MODE, pk);
aesKey = new byte[AES_Key_Size/8];
CipherInputStream is = new CipherInputStream(new FileInputStream(in), pkCipher);
is.read(aesKey);
aeskeySpec = new SecretKeySpec(aesKey, "AES");
}
/**
* Encrypts the AES key to a file using an RSA public key
*/
#SuppressWarnings("resource")
public void saveKey(File out, File publicKeyFile) throws IOException, GeneralSecurityException {
// read public key to be used to encrypt the AES key
byte[] encodedKey = new byte[(int)publicKeyFile.length()];
new FileInputStream(publicKeyFile).read(encodedKey);
// create public key
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pk = kf.generatePublic(publicKeySpec);
// write AES key
pkCipher.init(Cipher.ENCRYPT_MODE, pk);
CipherOutputStream os = new CipherOutputStream(new FileOutputStream(out), pkCipher);
os.write(aesKey);
os.close();
}
/**
* Encrypts and then copies the contents of a given file.
*/
public void encrypt(File in, File out) throws IOException, InvalidKeyException {
aesCipher.init(Cipher.ENCRYPT_MODE, aeskeySpec);
FileInputStream is = new FileInputStream(in);
CipherOutputStream os = new CipherOutputStream(new FileOutputStream(out), aesCipher);
copy(is, os);
os.close();
}
/**
* Decrypts and then copies the contents of a given file.
*/
public void decrypt(File in, File out) throws IOException, InvalidKeyException {
aesCipher.init(Cipher.DECRYPT_MODE, aeskeySpec);
CipherInputStream is = new CipherInputStream(new FileInputStream(in), aesCipher);
FileOutputStream os = new FileOutputStream(out);
copy(is, os);
is.close();
os.close();
}
/**
* Copies a stream.
*/
private void copy(InputStream is, OutputStream os) throws IOException {
int i;
byte[] b = new byte[1024];
while((i=is.read(b))!=-1) {
os.write(b, 0, i);
System.out.println("I is copying!");
// read what we wrote
for (int j = 0; j < b.length; j++) {
System.out.print("" + (char) is.read() + '\n');
}
}
}
public void encryption(){
}
public void decryption(){
}
public static void main(String[] args) throws GeneralSecurityException, IOException {
System.out.println("Begin Encyption!"); // Display the string.
CryptoFunction secure = new CryptoFunction();
// Encrypt code
File encryptFile = new File("encrypt.data");
File publicKeyData = new File("src/publickey.der");
File originalFile = new File("src/stufftoencrypt.txt");
File secureFile = new File("secure.data");
// create AES key
secure.makeKey();
// save AES key using public key
secure.saveKey(encryptFile, publicKeyData);
// save original file securely
secure.encrypt(originalFile, secureFile);
// Decrypt code
File encryptFile = new File("encrypt.data");
File privateKeyFile = new File("privatekey");
File secureFile = new File("secure.data");
File unencryptedFile = new File("unencryptedFile");
// load AES key
secure.loadKey(encryptFile, privateKeyFile);
// decrypt file
secure.decrypt(secureFile, unencryptedFile);
System.out.println("End Encryption!"); // Display the string.
}
}
Assuming the code works (I didn't test it), you may need to just refresh your eclipse project. If the secure.data file is created in there, eclipse won't automatically refresh to show it. If you want to create the files in a specific location, put an absolute path into new File(...) e.g. to create in C:\temp, use:
File secureFile = new File("c:\\temp\\secure.data");
File unencryptedFile = new File("c:\\temp\\unencryptedFile");
You can check the absolute path of these files using:
System.out.println(unencryptedFile.getAbsolutePath());
System.out.println(secureFile.getAbsolutePath());
I know that there are several other questions about that topic, but none of them have helped me out. I tried the BouncyCastle lib as well. Could someone please help me here?
The PEM file looks like:
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAq2eYtnTsEc/qyqS ...
... zY3WG++SA+amcXiO721hJWNC+uTbZ1bzQ==
-----END RSA PRIVATE KEY-----
I'm using this method
public static PrivateKey getPemPrivateKey(String filename) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
File f = new File(PEMFILES_FOLDER+filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
String temp = new String(keyBytes);
//TODO care about the linefeeds
String privKeyPEM = temp.replace("-----BEGIN RSA PRIVATE KEY-----\n", "");
privKeyPEM = privKeyPEM.replace("-----END RSA PRIVATE KEY-----", "");
System.out.println("Private key: \n"+privKeyPEM);
Base64 b64 = new Base64();
byte [] decoded = b64.decode(privKeyPEM);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance(RSA);
return kf.generatePrivate(spec);
}
I am getting this error:
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
I hope this can help you. I have copied a working copy of getPemPrivatekey and the way I call it in the main function:
public PrivateKey getPemPrivateKey(String filename, String algorithm) throws Exception {
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
String temp = new String(keyBytes);
String privKeyPEM = temp.replace("-----BEGIN PRIVATE KEY-----", "");
privKeyPEM = privKeyPEM.replace("-----END PRIVATE KEY-----", "");
//System.out.println("Private key\n"+privKeyPEM);
BASE64Decoder b64=new BASE64Decoder();
byte[] decoded = b64.decodeBuffer(privKeyPEM);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance(algorithm);
return kf.generatePrivate(spec);
}
The main program looks like this:
....
gcsr = new ...... // instantiate the class here
privateKey= gcsr.getPemPrivateKey("c:\\testdir\\java_private.pem", "RSA");
BASE64Encoder encoder1= new BASE64Encoder();
String s1=encoder1.encodeBuffer(gcsr.getPrivateKey().getEncoded());
System.out.println("Private Key in Base64:"+s1+"\n");
This is currently working (Java 8 on my computer!). "gcsr" is the name of the object I instantiated from the class containing the function.
Regards.