I'm trying to load an AES SecretKey from a .pem file encrypted with a private key.
SecretKey loadAESKeyfromCertFile(String filename, PrivateKey prv) {
try {
FileInputStream in = new FileInputStream(filename);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, prv);
byte[] b = new byte[256];
in.read(b);
byte[] keyb = cipher.doFinal(b);
return new SecretKeySpec(keyb, "AES");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
But i keep getting java.security.InvalidKeyException: No installed provider supports this key: sun.security.rsa.RSAPrivateCrtKeyImpl
Related
I'm trying to decrypt data that is encrypted with AES/SIC/PKCS7Padding. I'm making use of BouncyCastleProvider to achieve this, but on decryption BadPaddingException is thrown.
Here's the code i'm using to decrypt, am i doing something wrong? Any help is appreciated, thanks!
public class AesEncryption {
public static String decrypt(String aesKey, String cipherText) {
try {
Security.addProvider(new BouncyCastleProvider());
Cipher dcipher;
SecretKey key = new SecretKeySpec(Base64.getDecoder().decode(aesKey), "AES");
dcipher = Cipher.getInstance("AES/SIC/PKCS7Padding", "BC");
dcipher.init(Cipher.DECRYPT_MODE, key, generateIv());
byte[] dec = Base64.getDecoder().decode(cipherText);
byte[] utf8 = dcipher.doFinal(dec);
return new String(utf8, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static IvParameterSpec generateIv() {
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
return new IvParameterSpec(iv);
}
}
I am generating Asymmetric public key and private using the code below.
public static KeyPair generateRSAKkeyPair()
throws Exception {
SecureRandom secureRandom = new SecureRandom();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(
2048, secureRandom);
return keyPairGenerator
.generateKeyPair();
}
public static byte[] do_RSAEncryption(
String plainText,
PublicKey publicKey)
throws Exception {
Cipher cipher = Cipher.getInstance(RSA);
cipher.init(
Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(
plainText.getBytes());
}
public static String do_RSADecryption(
byte[] cipherText,
PrivateKey privateKey)
throws Exception {
Cipher cipher = Cipher.getInstance(RSA);
cipher.init(Cipher.DECRYPT_MODE,
privateKey);
byte[] result = cipher.doFinal(cipherText);
return new String(result);
}
public static PublicKey getKey(String key) {
try {
byte[] byteKey = Base64.getDecoder().decode(key.getBytes());
X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(byteKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(X509publicKey);
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static PrivateKey getPrivateKey(String key) {
try {
byte[] byteKey = Base64.getDecoder().decode(key.getBytes());
X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(byteKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(X509publicKey);
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
This works as expected.
String plainText = "This is the PlainText. I want to Encrypt using RSA.";
byte[] cipherText = do_RSAEncryption(plainText, keypair.getPublic());
String decryptedText = do_RSADecryption(cipherText, keypair.getPrivate());
Now, I am storing the generated the generated public key and private key in a string and trying to encrypt and decrypt and it is failing.
String base64EncodedEncryptionKey = DatatypeConverter.printBase64Binary(keypair.getPublic().getEncoded()));
String base64EccodedDecryptionKey = DatatypeConverter.printBase64Binary(keypair.getPrivate().getEncoded()));
PublicKey pubKey = getKey(base64EncodedEncryptionKey);
byte[] cipherText1 = do_RSAEncryption(plainText, pubKey);
System.out.println("Encrypted Text ===> "+ DatatypeConverter.printHexBinary(cipherText1));
PrivateKey privateKey = getPrivateKey(base64EccodedDecryptionKey);
String decryptedText1 = do_RSADecryption(cipherText1,privateKey);
System.out.println("DecryptedText ====>>> "+decryptedText1);
Error:-
java.security.spec.InvalidKeySpecException: Only RSAPrivate(Crt)KeySpec and PKCS8EncodedKeySpec supported for RSA private keys
at java.base/sun.security.rsa.RSAKeyFactory.generatePrivate(RSAKeyFactory.java:389)
at java.base/sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:247)
at java.base/java.security.KeyFactory.generatePrivate(KeyFactory.java:390)
at com.apple.ist.alloy.memsqlloader.service.Asymmetric.getPrivateKey(Asymmetric.java:96)
at com.apple.ist.alloy.memsqlloader.service.Asymmetric.main(Asymmetric.java:135)
Exception in thread "main" java.security.InvalidKeyException: No installed provider supports this key: (null)
at java.base/javax.crypto.Cipher.chooseProvider(Cipher.java:930)
at java.base/javax.crypto.Cipher.init(Cipher.java:1286)
at java.base/javax.crypto.Cipher.init(Cipher.java:1223)
at com.apple.ist.alloy.memsqlloader.service.Asymmetric.do_RSADecryption(Asymmetric.java:68)
at com.apple.ist.alloy.memsqlloader.service.Asymmetric.main(Asymmetric.java:137)
Private keys are encoded with PKCS8EncodedKeySpec. You should replace X509EncodedKeySpec with that.
I try to use java7 to achieve AES-256-GCM decoding
I encountered a mac check failure response in GCM
Please help me out, thank you all.
import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public static void main(String[] args) throws Exception {
String iv = "35d117c42d1c1835420b6b9942dd4f1b"; // utf-8
String key = "3a7bd3e2360a3d29eea436fcfb7e44c7"; // utf-8
String hexCipherString = "07a604fc0c143a6e"; // hex
String hexAuthTagString = "984e81176ff260717beb184db3d73753"; //hex
byte[] decodedCipherHexBtye = Hex.decodeHex(hexCipherString.toCharArray());
byte[] base64Cipher = Base64.decodeBase64(decodedCipherHexBtye);
byte[] decodedAuthTagHex = Hex.decodeHex(hexAuthTagString.toCharArray());
byte[] base64AuthTag = Base64.decodeBase64(decodedAuthTagHex);
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
byte[] gcmIv = iv.getBytes("UTF-8");
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
GCMParameterSpec params = new GCMParameterSpec(base64AuthTag.length * Byte.SIZE, gcmIv);
cipher.init(Cipher.DECRYPT_MODE, skeySpec, params);
cipher.updateAAD(base64AuthTag);
byte[] result = cipher.doFinal(base64Cipher);
System.out.println(Base64.encodeBase64(result));
}
I am looking forward to the expected result of Base64Encode: dGVzdA==
How to address the Security warning comment to implement safe code.
The core issue in the warning is the need for a randomly generated IV (a set of truly random bytes) for each encryption
when you make a Cypher here is a concrete example using Java 8 Oracle SE
val cipher = Cipher.getInstance("some scheme", "some provider");
then when we use the the Cypher safely we need a true random initialization vector IV to correctly resolve the issue this warning is directing us too,
turns out at least in Oracle Java 8 this is easy,
with the above cypher
cipher.getIV()
the instance method for the cipher object is safe see
generally use with IvParameterSpec
ivParams = IvParameterSpec( cipher.getIV())
cipher.init(Cipher.ENCRYPT_MODE, key, ivParams);
I found a solution and modified the code as follows:
public static void main(String[] args) throws Exception {
String iv = "35d117c42d1c1835420b6b9942dd4f1b"; // utf-8
String key = "3a7bd3e2360a3d29eea436fcfb7e44c7"; // utf-8
String hexCipherString = "07a604fc0c143a6e"; // hex
String hexAuthTagString = "984e81176ff260717beb184db3d73753"; //hex
byte[] decodedCipherHexBtye = Hex.decodeHex(hexCipherString.toCharArray());
byte[] decodedAuthTagHex = Hex.decodeHex(hexAuthTagString.toCharArray());
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
byte[] gcmIv = iv.getBytes("UTF-8");
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
GCMParameterSpec params = new GCMParameterSpec(decodedAuthTagHex.length * Byte.SIZE, gcmIv);
cipher.init(Cipher.DECRYPT_MODE, skeySpec, params);
cipher.update(decodedCipherHexBtye);
byte[] result = cipher.doFinal(decodedAuthTagHex);
System.out.println(new String(result));
}
Refer to this article
Tag mismatch error in AES-256-GCM Decryption using Java
This is the easiest way to encryption and decryption using AES-256.
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
String decryptedData = AESUtils.decrypt("Your Encrypted Data");
System.out.println(decryptedData);
}
public class AESUtils {
private static final String ENCRYPTION_KEY = "RwcmlVpg";
private static final String ENCRYPTION_IV = "5183666c72eec9e4";
#RequiresApi(api = Build.VERSION_CODES.O)
public static String encrypt(String src) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, makeKey(), makeIv());
byte[] origignal= Base64.encode(cipher.doFinal(src.getBytes()),Base64.DEFAULT);
return new String(origignal);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
#RequiresApi(api = Build.VERSION_CODES.O)
public static String decrypt(String src) {
String decrypted = "";
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, makeKey(), makeIv());
byte[] decode = Base64.decode(src, Base64.NO_WRAP);
decrypted= new String(cipher.doFinal(decode), "UTF-8");
} catch (Exception e) {
Log.v("AES Exception", String.valueOf(e));
throw new RuntimeException(e);
}
return decrypted;
}
static AlgorithmParameterSpec makeIv() {
try {
return new IvParameterSpec(ENCRYPTION_IV.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
static Key makeKey() {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] key = md.digest(ENCRYPTION_KEY.getBytes("UTF-8"));
return new SecretKeySpec(key, "AES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
}
I was using this code segment to encrypt my image data 2 years ago. At that time my target SDK was 22. But When i try to update my SDK i face that i can not decrypt it. I find out that Android deprecated this encryption method. Is there any way to solve this problem so that i can decrypt my images.
Thanks in advance.
public byte[] EncryptByte(byte[] rawInputByte){
byte[] fileBytes = null;
try {
byte[] yourKey = generateKey("password");
fileBytes = encodeFile(yourKey, rawInputByte);
} catch (Exception e) {
e.printStackTrace();
}
return fileBytes;
}
public byte[] generateKey(String password) throws Exception
{
byte[] keyStart = password.getBytes("UTF-8");
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
sr.setSeed(keyStart);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
return skey.getEncoded();
}
At last i can solve this. Thanks James K Polk for giving me the link. My code segment is given below and InsecureSHA1PRNGKeyDerivator link is this. Hope it will help you guys.
public byte[] decodeFile(byte[] fileData) throws Exception
{
SecretKey insecureKey = deriveKeyInsecurely("password", 16);
byte[] decryptedData = decryptData(fileData, insecureKey);
return decryptedData;
}
#RequiresApi(api = Build.VERSION_CODES.KITKAT)
private static SecretKey deriveKeyInsecurely(String password, int
keySizeInBytes) throws Exception {
byte[] passwordBytes = password.getBytes(StandardCharsets.US_ASCII);
return new SecretKeySpec( InsecureSHA1PRNGKeyDerivator.deriveInsecureKey( passwordBytes, keySizeInBytes), "AES");
}
private static byte[] decryptData(
byte[] data, SecretKey key) {
try {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key );
return cipher.doFinal(data);
} catch (GeneralSecurityException e) {
throw new RuntimeException("This is unconceivable!", e);
}
}
I have a problem with my code, when I encrypt data, for example, in this case, the simmetric key I encrypted with the receiver's public key, then saved to a text file, when I read that text file and try to decrypt it, using the receiver's private key, I get a different key, therefore I cannot use it to decrypt the encrypted message.
Sender's code:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
class Sender{
public static void main(String[] args) {
//infile.txt
File inFile = new File(args[0]);
//outfile.txt
File outFile = new File(args[1]);
//mykeystore.jks
File keyStoreFile = new File(args[2]);
//mykeystore info
String alias = args[3];
String password = args[4];
String storepass = args[5];
//receptor certificate
String receptorCert = args[6];
try {
//Read plain text
FileInputStream rawDataFromFile = new FileInputStream(inFile);
byte[] plainText = new byte[(int) inFile.length()];
rawDataFromFile.read(plainText);
//Create simmetric key
String key = "Bar12345Bar12345"; // 128 bit key
String initVector = "RandomInitVector"; // 16 bytes IV
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
//Encrypt plaintext
byte[] ciphertext = cipher.doFinal(plainText);
//Hash plaintext
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(plainText);
byte[] digest = md.digest();
//Encrypt simmetric key with receiver's public key
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
PublicKey receptorPublicKey = getPublicKeyFromCert(receptorCert);
rsaCipher.init(Cipher.ENCRYPT_MODE, receptorPublicKey);
byte[] simmetricKey = rsaCipher.doFinal(skeySpec.getEncoded());
//Encrypt hash with my private key
KeyStore myKeyStore = KeyStore.getInstance("JKS");
FileInputStream inStream = new FileInputStream(keyStoreFile);
myKeyStore.load(inStream, storepass.toCharArray());
PrivateKey privatekey = (PrivateKey) myKeyStore.getKey(alias, password.toCharArray());
rsaCipher.init(Cipher.ENCRYPT_MODE, privatekey);
byte[] encodedHash = rsaCipher.doFinal(digest);
//Write to outputfile
FileOutputStream outToFile = new FileOutputStream(outFile);
outToFile.write(simmetricKey);
outToFile.write(encodedHash);
outToFile.write(ciphertext);
outToFile.close();
rawDataFromFile.close();
} catch (Exception e) {
e.printStackTrace();
e.getMessage();
}
}
public static PublicKey getPublicKeyFromCert(String certLocation) {
PublicKey pub = null;
try {
InputStream inStream = new FileInputStream(certLocation);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream);
inStream.close();
pub = (PublicKey) cert.getPublicKey();
} catch (Exception e) {
e.printStackTrace();
}
return pub;
}
}
Receiver's code:
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class Receiver {
public static void main(String[] args) {
//Sender's out file
File inFile = new File(args[0]);
//receiver's keystore
File keyStoreFile = new File(args[1]);
//receiver's keystore info
String password = args[2];
String alias = args[3];
String storepass = args[4];
//sender's cetificate
File cert = new File(args[5]);
try {
//get Sender's out file
FileInputStream rawDataFromFile = new FileInputStream(inFile);
byte[] simmetricKey = new byte[256];
byte[] hash = new byte[256];
byte[] message;
rawDataFromFile.read(simmetricKey);
rawDataFromFile.read(hash);
int b = rawDataFromFile.available();
message = new byte[b];
rawDataFromFile.read(message);
//decrypt the simmetric key with receiver's private key
KeyStore myKeyStore = KeyStore.getInstance("JKS");
FileInputStream inStream = new FileInputStream(keyStoreFile);
myKeyStore.load(inStream, storepass.toCharArray());
PrivateKey privatekey = (PrivateKey) myKeyStore.getKey(alias, password.toCharArray());
//
Cipher deCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
deCipher.init(Cipher.DECRYPT_MODE, privatekey);
byte[] key = deCipher.doFinal(simmetricKey);
System.out.println(Base64.encodeBase64String(key));
} catch (Exception e) {
System.out.println("Error del sistema " + e);
e.printStackTrace();
}
}
}
UPDATE:
Now I can decrypt the simmetric key using the receiver's private key. But I dont know how to create a decoder using the same argument when I encrypted the message.
Sender's code to encrypt plain text.
String key = "Bar12345Bar12345"; // 128 bit key
String initVector = "RandomInitVector"; // 16 bytes IV
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
System.out.println(iv.getIV());
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] ciphertext = cipher.doFinal(plainText);
Receiver's decryption 1
SecretKeySpec keySpec = new SecretKeySpec(decryptedKeySpec, "AES");
Cipher decoder = Cipher.getInstance("AES");
decoder.init(Cipher.DECRYPT_MODE, keyspec);
byte[] original = descipher.doFinal(message);
ERROR: Given final block not properly padded
Receiver's decryption 2
SecretKeySpec keySpec = new SecretKeySpec(decryptedKeySpec, "AES");
Cipher decoder = Cipher.getInstance("AES/CBC/PKCS5PADDING");
decoder.init(Cipher.DECRYPT_MODE, keyspec);
byte[] original = descipher.doFinal(message);
ERROR: Parameters missing
FINAL UPDATE:
Now my code works, thanks for all the help.
This code can be downloaded from here (btw, it's in spanish, but i don't think it matters):
download
The issue is you are using AES to encrypt
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
whereas to decipher,
you are using RSA,
Cipher deCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
Code snippet to use for encrypt/decrypt using AES
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
public class EncryptionDecryptionAES {
static Cipher cipher;
public static void main(String[] args) throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
SecretKey secretKey = keyGenerator.generateKey();
cipher = Cipher.getInstance("AES");
String plainText = "AES Symmetric Encryption Decryption";
System.out.println("Plain Text Before Encryption: " + plainText);
String encryptedText = encrypt(plainText, secretKey);
System.out.println("Encrypted Text After Encryption: " + encryptedText);
String decryptedText = decrypt(encryptedText, secretKey);
System.out.println("Decrypted Text After Decryption: " + decryptedText);
}
public static String encrypt(String plainText, SecretKey secretKey)
throws Exception {
byte[] plainTextByte = plainText.getBytes();
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedByte = cipher.doFinal(plainTextByte);
Base64.Encoder encoder = Base64.getEncoder();
String encryptedText = encoder.encodeToString(encryptedByte);
return encryptedText;
}
public static String decrypt(String encryptedText, SecretKey secretKey)
throws Exception {
Base64.Decoder decoder = Base64.getDecoder();
byte[] encryptedTextByte = decoder.decode(encryptedText);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decryptedByte = cipher.doFinal(encryptedTextByte);
String decryptedText = new String(decryptedByte);
return decryptedText;
}
}
Please check http://javapapers.com/java/java-symmetric-aes-encryption-decryption-using-jce/
It seems that your receiver tries to read 256 byte for the encrypted symmetric key, but I think the RSA encrypted key only is 128 byte long.
So maybe it works using
byte[] simmetricKey = new byte[128];