I have generated on my android application a pair of RSA Keys.
I receive from a web service
- an AES Key, encrypted with my RSA public key
- a String encoded with the AES key.
So I must do the following:
- decrypt the AES Key
- decrypt the string with the obtained AES Key.
To generate the RSA Keys I did:
keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(size);
keypair = keyGen.genKeyPair();
privateKey = keypair.getPrivate();
publicKey = keypair.getPublic();
On RSA decrypt I use :
public static byte[] decryptRSA( PrivateKey key, byte[] text) throws Exception
{
byte[] dectyptedText = null;
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
dectyptedText = cipher.doFinal(text);
return dectyptedText;
}
On AES decrypt I use:
public static byte[] decryptAES(byte[] key, byte[] text) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(text);
return decrypted;
}
So, in my code, to obtain the decrypted AES Key I do
byte[] decryptedAESKey = sm.decryptRSA(key, Base64.decode(ReceivedBase64EncryptedAESKey));
byte[] decryptedString = sm.decryptAES(decryptedAESKey, Base64.decode(ReceivedEncryptedAESString));
On the end I get a null for decryptedString.
What am I doing wrong ?
Well, the thing is that the key decrypted was 8 byte long and I had to make it 16 byte to be AES 128 bits compatible
So, I made a method to convert it back
private static byte[] GetKey(byte[] suggestedKey)
{
byte[] kRaw = suggestedKey;
ArrayList<Byte> kList = new ArrayList<Byte>();
for (int i = 0; i < 128; i += 8)
{
kList.add(kRaw[(i / 8) % kRaw.length]);
}
byte[] byteArray = new byte[kList.size()];
for(int i = 0; i<kList.size(); i++){
byteArray[i] = kList.get(i);
}
return byteArray;
}
And the rewritten decrypt method:
public static byte[] decryptAES(byte[] key, byte[] text) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(GetKey(key), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding","BC");
byte [] iv = new byte[cipher.getBlockSize()];
for(int i=0;i<iv.length;i++)iv[i] = 0;
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec);
byte[] decrypted = cipher.doFinal(text);
return decrypted;
}
I'm not sure what language or libraries you are using (looks like Java?), but some really general things to try:
Did you get the encrypted string, ok? Check the length of ReceivedEncryptedAESString and the output of the Base64.decode to check they look alright.
AES decryption can't fail so it must be a problem in the library initialisation. Check the value/state of cipher after the construction step and the init step.
Try a simpler testcase: ignore the RSA encryption and just try to decrypt something using your Cipher object.
Related
I want to encrypt the data in flutter using the AES cbc-128 algorithm. below is the java code for that i want to achieve the same functionality as below but in dart. i have tried
cryptography
dependency in flutter but the problem with that is that i want to use my own key in the algorithm as below in the java code. if you know any method for achieving this please let me know.
public static String Decrypt(String text, String key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] keyBytes = new byte[16];
byte[] b = key.getBytes("UTF-8");
int len = b.length;
if (len > keyBytes.length)
len = keyBytes.length;
System.arraycopy(b, 0, keyBytes, 0, len);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(keyBytes);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
BASE64Decoder decoder = new BASE64Decoder();
byte[] results = cipher.doFinal(decoder.decodeBuffer(text));
return new String(results, "UTF-8");
}
public static String Encrypt(String text, String key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] keyBytes = new byte[16];
byte[] b = key.getBytes("UTF-8");
int len = b.length;
if (len > keyBytes.length)
len = keyBytes.length;
System.arraycopy(b, 0, keyBytes, 0, len);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(keyBytes);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] results = cipher.doFinal(text.getBytes("UTF-8"));
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(results);
}
Test Case
For the following input
plainText="This is plain text";
key="sahtojetrout2";
i want the encrypted result to be
encryptedText="8FmSMnDsFJVyNUXunhJLSmhFnRq89fl5DyTp0wdYfgk=";
which Topaco has written in an online editor you can check out that here Java Code. In flutter i have tried the program given at the Flutter site
You can do AES CBC-128 encryption in flutter with the help of crypt library. It supports the AES cbc encryption. The following sample code accepts key-string and plain-text as arguments and encrypts it as you have mentioned. You can pass your own key here. For AES-128, you need 128 bit key or 16 character string.
import 'package:encrypt/encrypt.dart';
void main() {
final key = "Your16CharacterK";
final plainText = "lorem ipsum example example";
Encrypted encrypted = encrypt(key, plainText);
String decryptedText = decrypt(key, encrypted);
print(decryptedText);
}
String decrypt(String keyString, Encrypted encryptedData) {
final key = Key.fromUtf8(keyString);
final encrypter = Encrypter(AES(key, mode: AESMode.cbc));
final initVector = IV.fromUtf8(keyString.substring(0, 16));
return encrypter.decrypt(encryptedData, iv: initVector);
}
Encrypted encrypt(String keyString, String plainText) {
final key = Key.fromUtf8(keyString);
final encrypter = Encrypter(AES(key, mode: AESMode.cbc));
final initVector = IV.fromUtf8(keyString.substring(0, 16));
Encrypted encryptedData = encrypter.encrypt(plainText, iv: initVector);
return encryptedData;
}
In the above example, the IV is created from the key itself to keep the code easy to read. Use random data for IV for better security. Referred article for flutter encryption.
I have implemented a CBC Mode AES encryption and decryption mechanism in which I am generating Random IV and Random Secret key for each encryption attempt which is recommended.
Now, I have saved my key in a separate file and IV in another file, but after going through the different forums I have found that the IV should not be kept secure and shall be appended with the Ciphertext while encryption and at the time of decryption we can get the 16 bytes plucked out from that cipher byte array..
Now, I tried a snippet of code to achieve the same, but the result were not good as the first block was not encrypting properly; however the rest of the block does.
Can someone tell me whats wrong with my approach?
Any help will be highly appreciated thanks :).
public static byte[] encrypt (byte[] plaintext,SecretKey key,byte[] IV ) throws Exception {
//Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
//Create IvParameterSpec
IvParameterSpec ivSpec = new IvParameterSpec(IV);
System.out.println( "IV encrypt= " + ivSpec );
//Initialize Cipher for ENCRYPT_MODE
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
//Perform Encryption
byte[] cipherText = cipher.doFinal(plaintext);
ByteArrayOutputStream b = new ByteArrayOutputStream();
b.write(IV);
b.write( cipherText );
return b.toByteArray();
}
--------------------------------------------------------------------------
public static String decrypt (byte[] cipherText, SecretKey key ) throws Exception
{
//Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
byte[] iv = Arrays.copyOfRange( cipherText , 0, 16);
//Create IvParameterSpec
IvParameterSpec ivSpec = new IvParameterSpec(iv);
//Initialize Cipher for DECRYPT_MODE
cipher.init(Cipher.DECRYPT_MODE, keySpec,ivSpec);
//Perform Decryption
byte[] decryptedText = cipher.doFinal(cipherText);
return new String(decryptedText);
}
----------------------------------------------------------------------------
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
// Generate Key
SecretKey key = keyGenerator.generateKey();
// Generating IV.
byte[] IV = new byte[16];
SecureRandom random = new SecureRandom();
random.nextBytes(IV);
System.out.println("Original Text : " + plainText);
byte[] cipherText = encrypt(plainText.getBytes("UTF-8") ,key, IV);
String decryptedText = decrypt(cipherText,key, IV);
System.out.println("DeCrypted Text : "+decryptedText);
RESULT
Original Text : This is a plain text which need to be encrypted by AES Algorithm with CBC Mode
DeCrypted Text : ûª¯Î¥pAï2EÞi+¼‹Ý$8ŶÄDDNâOæàtext which need to be encrypted by AES Algorithm with CBC Mode
Just because you copy out the IV here:
byte[] iv = Arrays.copyOfRange( cipherText , 0, 16);
Doesn't mean it isn't still present when you try to decrypt it during:
byte[] decryptedText = cipher.doFinal(cipherText);
You should decrypt everything in ciphertext except for the first 16 bytes. At the moment you're also performing AES decryption on the IV - which is why you're getting garbage.
First, please excuse my bad english, this is not my native language.
i am trying to encode and then decode some text of different lengths.
I tried to encode some text with RSA algorithm in java. In the progress, I found out that RSA-encoding is limited to a specific size of bytes, so this approach doesn't fit my specs as my text could be large.
As I want to stay with a public and a private key only, I came up with the idea to use a 'hard-coded' AES key which will be encoded with RSA public key and than use this to encode the data. (see code)
//harcoded secretkey base64 encoded
private static final String secretkey_base64 = "xtnN6Pove5AovbLXtGRJKw==";
//this is how RSA key are generated
public static KeyPair genKeys() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance( "RSA" );
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();
return kp;
}
//here text should be encoded
public static String encodeText(String plainText, PublicKey publicKey) throws Exception
{
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] cipherText = encryptCipher.doFinal(CryptoUtil.secretkey_base64.getBytes(UTF_8));
String encodedSecKey = Base64.getEncoder().encodeToString(cipherText);
SecretKeySpec k = new SecretKeySpec(Base64.getDecoder().decode(encodedSecKey), "AES");
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.ENCRYPT_MODE, k);
byte[] byteCipherText = aesCipher.doFinal(plainText.getBytes());
return Base64.getEncoder().encodeToString(byteCipherText);
}
Obviously this doesn't work because the RSA encoded AES key is too long for AES encryption. Throws "Invalid AES key length: 256 bytes"
Does anyone have an idea how I could get the encryption running with this particular approach ?
Is this even possible, or do I have to use a different approach?
Thanks in advance.
//edit - Solved thanks to #ewramner
// here is the working solution:
public static String encodeText(String plainText, PublicKey publicKey) throws Exception
{
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(128); // The AES key size in number of bits
SecretKey secKey = generator.generateKey();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.PUBLIC_KEY, publicKey);
byte[] encSecKey = cipher.doFinal(secKey.getEncoded());
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.ENCRYPT_MODE, secKey);
byte[] byteCipherText = aesCipher.doFinal(plainText.getBytes());
return Base64.getEncoder().encodeToString(encSecKey) + "#" + Base64.getEncoder().encodeToString(byteCipherText);
}
public static String decodeText(String cipherText, PrivateKey privateKey) throws Exception
{
String[] split = cipherText.split("#");
String encSecKey = split[0];
String encText = split[1];
byte[] bytesSecKey = Base64.getDecoder().decode(encSecKey);
byte[] bytesText = Base64.getDecoder().decode(encText);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.PRIVATE_KEY, privateKey);
byte[] decSecKey = cipher.doFinal(bytesSecKey);
SecretKey secKey = new SecretKeySpec(decSecKey , 0, decSecKey .length, "AES");
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.DECRYPT_MODE, secKey);
return new String(aesCipher.doFinal(bytesText));
}
I have my application's UI built in Meteor and it gets and send the data from REST API (Spring CXF). I would like to encrypt the data in Meteor, and decrypt the same in REST API code. I am using AES for encryption and Decryption. In Meteor i am using https://atmospherejs.com/jparker/crypto-aes package for encryption. I have written the below code in java for decryption the encryption key send by Meteor.
public class AESTest {
private static String AESStr = "<Encrypted KEY>";
public static void main(String[] args) throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
System.out.println(decrypt(AESStr, "Test"));
}
public static String decrypt(String responseStr, String passPhrase) throws GeneralSecurityException {
String decryptedStr = "";
try {
Cipher cipher = getCipher(Cipher.DECRYPT_MODE, passPhrase);
byte[] decoded = Base64.decodeBase64(responseStr.getBytes());
byte[] decryptedWithKey = cipher.doFinal(decoded);
byte[] decrypted = Arrays.copyOfRange(decryptedWithKey, 16, decryptedWithKey.length);
decryptedStr = new String(decrypted, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return decryptedStr;
}
private static Cipher getCipher(int mode, String passPhrase) throws Exception {
SecretKeySpec secretKeySpec = new SecretKeySpec(passPhrase.getBytes(), "AES");
byte[] IV = new byte[16];
new Random().nextBytes(IV);
AlgorithmParameterSpec paramSpec = new IvParameterSpec(IV);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(mode, secretKeySpec, paramSpec);
return cipher;
}
}
When I run the code i am getting below exception
javax.crypto.BadPaddingException: pad block corrupted
at org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at com.tph.r3.EncodeTest.decrypt(EncodeTest.java:37)
at com.tph.r3.EncodeTest.main(EncodeTest.java:26)
Can anyone guide me with the issue?
There is a problem with the decryption logic w.r.t IV. You are selecting an IV randomly to initialize decryption cipher which is wrong. You need to use the same IV that was used to encrypt the responseStr which forms its first 16 bytes usually.
In the current form your getCipher() can be used only for encryption where IV is selected randomly but not for decryption. Better write another method.
Psuedocode for decryption:
decCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(securityKey , "AES");
//IV + Cipher
byte [] cipherWithIV = Base64.decodeBase64(responseStr.getBytes()));
//Extract IV
byte [] iv = new byte [16];
byte [] cipherWithoutIV = new byte [cipherWithIV.length - 16 ];
//First 16 bytes
for(i < 16; i++) {
iv [i] = cipherWithIV [i];
}
//Rest of the cipher ie 16 -> cipherWithIV.length
for(i < cipherWithIV.length; i++) {
cipherWithoutIV [j] = cipherWithIV[i];
j++;
}
//
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
//
decCipher.init(Cipher.DECRYPT_MODE, keySpec, ivParamSpec);
//Decrypt cipher without IV
decText = decCipher.doFinal(cipherWithoutIV);
//Convert to string
decString = new String(decText,"UTF8");
I am doing a simple implementation for AES Algorithm.
// Get the Key Generator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey secret = kgen.generateKey();
byte[] raw = secret.getEncoded();
String key = new String(Base64.encodeBase64(raw));
I am saving "key" in database.
Now again in another operation i am fetching a "key" from database n trying to decrypt data. i call decryption function as
String dencryptReq = Utils.decrypt2(new String(Base64.decodeBase64(secretKeyInformation.getSecretKey().getBytes())),Base64.decodeBase64(encryptReq.getBytes()) );
public static String decrypt2(String key, byte[] encrypted)
throws GeneralSecurityException {
byte[] raw = Base64.decodeBase64(key.getBytes());
if (raw.length != 16) {
throw new IllegalArgumentException("Invalid key size.");
}
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec,
new IvParameterSpec(new byte[16]));
byte[] original = cipher.doFinal(encrypted);
return new String(original, Charset.forName("US-ASCII"));
}
But it is throwing me invalid key size exception.
If i do in one time this without saving in databse and fetching from database it is working fine.
I have tried your code with some modifications I have used apache commons codec library for Base64 conversion,
/* Derive the key, given password and salt. */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec("password".toCharArray(), "salt".getBytes(), 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
/* Encrypt the message. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal("Hello, World! My data is here.. !".getBytes("UTF-8"));
System.out.println("cipher :"+new String(ciphertext));
/*String-key convertion */
String stringKey=Base64.encodeBase64String(secret.getEncoded());//To String key
byte[] encodedKey = Base64.decodeBase64(stringKey.getBytes());
SecretKey originalKey = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");// Convert from string
/* Decrypt the message, given derived key and initialization vector. */
Cipher cipher1 = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher1.init(Cipher.DECRYPT_MODE, originalKey, new IvParameterSpec(iv));
String plaintext = new String(cipher1.doFinal(ciphertext), "UTF-8");
System.out.println(plaintext);
This code worked perfectly in my system.