Write and read RSA key from file : java - java

I'm new to Cryptography.
I would like to generate RSA key in java and write it to a file. Later I would like to read from that file and get the RSA key back.
Code for key generation and writing to file :
public void generate() throws Exception{
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair kpair = kpg.genKeyPair();
byte[] publicKeyBytes = kpair.getPublic().getEncoded();
FileOutputStream fos = new FileOutputStream("publicKey");
fos.write(publicKeyBytes);
fos.close();
}
Code for reading and getting back the RSA public key :
public static Key getKeyFromFile(String fileName) throws Exception{
Key pk = null;
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();
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
pk = kf.generatePublic(spec);
return pk;
}
When I run the program I get the following error :
algid parse error, not a sequence
Cannot figure out what's wrong.

Related

java.security.SignatureException: object not initialized for signing

I'm getting the error:
java.security.SignatureException: object not initialized for signing
at md.update(signature.sign());
when I try to sign my signature. Basically what I'm trying to do is have some data, sign the data with my private key, save it to a file, open the file and check if the signed data is the same as the original data by comparing the two message digests that handle the data. Not really sure if that's the way it's supposed to be done, but I'm just trying different things at the moment.
According to most guides, I'm supposed to initVerify with my pubicKey, update with byte and then sign to check the data, but everytime I try to do the signing I get the exception.
PublicKey publicKey;
boolean verifyData = false;
byte[] sign = null;
MessageDigest md = MessageDigest.getInstance("SHA");
MessageDigest md2 = MessageDigest.getInstance("SHA");
ObjectInputStream keyIn = new ObjectInputStream(new FileInputStream(publicKeyLocation));
publicKey = (PublicKey)keyIn.readObject();
keyIn.close();
BufferedReader reader = new BufferedReader(new FileReader(dataLocation+ ".txt"));
String data = reader.readLine();
reader.close();
ObjectInputStream signatureIn = new ObjectInputStream(new FileInputStream(signatureLocation));
byte[] signatureToVerify = (byte[])signatureIn.readObject();
signatureIn.close();
Signature signature = Signature.getInstance("SHA256withDSA");
signature.initVerify(publicKey);
signature.update(signatureToVerify);
md.update(signature.sign());
md2.update(data.getBytes());;
verifyData = md.digest().equals(md2.digest());
//verifyData = signature.verify(signature.sign());
System.out.println(verifyData);
Here is the verifying class
PrivateKey privateKey;
ObjectInputStream keyIn = new ObjectInputStream(new FileInputStream(privateKeyLocation));
privateKey = (PrivateKey)keyIn.readObject();
keyIn.close();
BufferedReader reader = new BufferedReader(new FileReader(dataLocation+ ".txt"));
String data = reader.readLine();
reader.close();
Signature signature = Signature.getInstance("SHA256withDSA");
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(data.getBytes());
signature.initSign(privateKey);
signature.update(md.digest());
ObjectOutputStream outSignature = new ObjectOutputStream(new FileOutputStream(signatureLocation));
outSignature.writeObject(signature.sign());
outSignature.close();
System.out.println("Finished SignHandler");
and this is the signature initializing signature class.
The privatekey and publickey are handled with a keypairgenerator with the algorithm "DSA" in another class and binary serialized.

Decrypt with private key file: invalid stream header

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?

Android - InvalidKeySpecException - java lang runtime exception SSLInternal:TOO_LONG when trying to read .der file

I am trying to read a private key from a der file. I am getting the following error in my logcat. And the returned value is empty.
java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0c0000af:ASN.1 encoding routines:OPENSSL_internal:TOO_LONG
I tried searching for situations where this had already happened but could find none. I would like to know what this error means and how I can solve it.
Here is my code:
public static String decryptionWithFile(String encrypted,String privateFile2)throws Exception {
PrivateKey privateKey = getPrivateKey(privateFile2);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] bts = Hex.decodeHex(encrypted.toCharArray());
bts = cipher.doFinal(bts);
bts = getFinalBytesOfDycryptedString(bts);
String decryptedMessage = new String(cipher.doFinal(encrypted.getBytes()));
return new String(bts,"UTF-8");
}
And here is the getPrivateKey(); Method:
private static PrivateKey getPrivateKey(String privateFile2)throws Exception {
File f = new File(privateFile2);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
RSAPrivateKey privKey = (RSAPrivateKey) keyFactory.generatePrivate(spec);
return privKey;
}
Important Note: I added the .der file to my assets folder and then saved it to a file in Internal Storage in order to access the path - which my function requires. Do you think, anything must have happened to the file during this process? (It worked fine with the Public Key)
The problem turned out to be what I'd guessed. When I was writing the .der file to the Internal Storage, it was getting altered somehow and thus wasn't working.
So instead of that. I directly used the InputStream returned from getAssets().open(filename) to read the private key. That fixed it.

Why always give me false in signature?

I try in this code to verify my code, I have public key , my data and signature.I am read my signature from file and convert my string to public key then get my data and verify to signature.
public static boolean verify () {
String publickey = "MIGfMA0GCSqGSIb3DQE";
byte[] encKey = Base64.decodeBase64(publickey.getBytes());
try {
byte[] MACaddress = GetData();
BufferedReader in = new BufferedReader(new FileReader(
"EndSignatuer.txt"));
FileInputStream keyfis = new FileInputStream("EndSignatuer.txt");
byte[] Signen = new byte[keyfis.available()];
keyfis.read(Signen);
keyfis.close();
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, pubKey);
byte[] deSignen = Base64.decodeBase64(Signen);
byte[] decrypted_digest = cipher.doFinal(deSignen);
MessageDigest md5_digest = MessageDigest.getInstance("MD5");
md5_digest.update(MACaddress);
byte[] digest = md5_digest.digest();
if (decrypted_digest == digest) {
return true;
}else {
return false;//her why give me false
}
Code encryption:
public static void GenarationKEY(byte[] data) {
try {
File fileEndSignatuer = new File("EndSignatuer.txt");
FileOutputStream fopEndSignatuer = new FileOutputStream(
fileEndSignatuer);
// /Read private key from file
FileInputStream keyfis = new FileInputStream("PiveteKey.txt");
byte[] PrivateKeyB = new byte[keyfis.available()];
keyfis.read(PrivateKeyB);
keyfis.close();
byte[] decodePrivetekey = Base64.decodeBase64(PrivateKeyB);
// /get private key
PKCS8EncodedKeySpec pubKeySpec = new PKCS8EncodedKeySpec(
decodePrivetekey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privKey = keyFactory.generatePrivate(pubKeySpec);
// / make hash
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privKey);
// /make encoding
MessageDigest md5_digest = MessageDigest.getInstance("MD5");
byte[] digest = md5_digest.digest(data);
byte[] cipherText = cipher.doFinal(digest);
byte[] degnatureencode = Base64.encodeBase64(cipherText);
fopEndSignatuer.write(degnatureencode);
fopEndSignatuer.flush();
fopEndSignatuer.close();
}
First of all, what you do is not quite RSA digital signature. Use Signature class instead of combination Cipher and MessageDigest. Or if you insist to do it on low level, then consult specification, particularly section 9.2.
Secondly, string MIGfMA0GCSqGSIb3DQE doesn't represent RSA public key neither it is proper Base64-encoded data.
Also you want to use Arrays.equals(byte[], byte[]) instead of equality operator, as the latter just ensures that array object is the same, while the former compares actual content of the arrays.

Converting a PEM private key file to a JAVA PrivateKey Object

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.

Categories