I am trying to develop two java programs. One for encrypting plain text and another to decrypt that encrypted text.
Below is my code:
Encryption.java
public class Encryption {
private static Cipher cipher = null;
public static void main(String args[]) throws Exception {
Scanner scan = new Scanner(System.in);
String keyText = "9ofAGtArndXw9Ffu3lRTGWy9svXuUBl8";
byte[] keyBytes = keyText.getBytes("UTF-8");
SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
cipher = Cipher.getInstance("AES");
System.out.println("Enter the plain text to be encrypted: ");
String plainText = scan.nextLine();
byte[] plainTextByte = plainText.getBytes("UTF-8");
byte[] encryptedBytes = encrypt(plainTextByte, secretKey);
String encryptedText = new String(encryptedBytes, "UTF-8");
System.out.println("Encrypted Text After Encryption: " + encryptedText);
}
static byte[] encrypt(byte[] plainTextByte, SecretKey secretKey) throws Exception {
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedBytes = cipher.doFinal(plainTextByte);
return encryptedBytes;
}
}
INPUT FOR Encryption.java : nonu AND
Ouput FROM Encryption.java : ??8???M?wFg(Ee
But when I enter the output from encryption.java in the decryption.java, it is giving me some error rather than giving me back the plain text.
Here is the Decryption.java code
public class Decryption {
private static Cipher cipher = null;
public static void main(String[] args) throws Exception {
Scanner scan = new Scanner(System.in);
String keyText = "9ofAGtArndXw9Ffu3lRTGWy9svXuUBl8";
byte[] keyBytes = keyText.getBytes("UTF-8");
SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
cipher = Cipher.getInstance("AES");
System.out.println("Enter the encrypted text to be decrypted: ");
String encryptedText = scan.nextLine();
byte[] encryptedBytes = encryptedText.getBytes("UTF-8");
byte[] decryptedBytes = decrypt(encryptedBytes, secretKey);
String decryptedText = new String(decryptedBytes, "UTF-8");
System.out.println("Plain Text is: " + decryptedText);
}
static byte[] decrypt(byte[] encryptedBytes, SecretKey secretKey)
throws Exception {
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return decryptedBytes;
}
}
It is giving me this error
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:936)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:847)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at javax.crypto.Cipher.doFinal(Cipher.java:2164)
at encryption.Decryption.decrypt(Decryption.java:36)
at encryption.Decryption.main(Decryption.java:27)
How can I solve this error?
There is at least one problem, namely with this line:
String encryptedText = new String(encryptedBytes, "UTF-8");
The encrypted binary data will in general not be a valid UTF-8 encoding. This means you loose information when converting the bytes to a string.
Use base64 or some other encoding for binary data to convert the binary bytes to a string.
Related
I want to use JAVA encryption and decryption logic in qt creator code i tried and search lot of stuff but success for "DES/CBC" Encryption and Decryption Logic but not useful for me Because JAVA code is in "DESede/CBC/PKCS5Padding" Triple DES logic.
JAVA CODE is as follows:
public static String initializationVector = "abhijeet";
public static String key="XB13347FE570DC4FFB13647F";
public String encryptText(String plainText) throws Exception {
// ---- Use specified 3DES key and IV from other source --------------
byte[] plaintext = plainText.getBytes();
byte[] tdesKeyData = Config.key.getBytes();
// byte[] myIV = initializationVector.getBytes();
Cipher c3des = Cipher.getInstance("DESede/CBC/PKCS5Padding");
SecretKeySpec myKey = new SecretKeySpec(tdesKeyData, "DESede");
IvParameterSpec ivspec = new IvParameterSpec(Config.initializationVector.getBytes());
c3des.init(Cipher.ENCRYPT_MODE, myKey, ivspec);
byte[] cipherText = c3des.doFinal(plaintext);
return new BASE64Encoder().encode(cipherText);
}
public static String decryptText(String cipherText) throws Exception {
byte[] encData = new BASE64Decoder().decodeBuffer(cipherText);
Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
byte[] tdesKeyData = Config.key.getBytes();
SecretKeySpec myKey = new SecretKeySpec(tdesKeyData, "DESede");
IvParameterSpec ivspec = new IvParameterSpec(Config.initializationVector.getBytes());
decipher.init(Cipher.DECRYPT_MODE, myKey, ivspec);
byte[] plainText = decipher.doFinal(encData);
return new String(plainText);
}
Example :
Input string - "Hello"
After Encryption Output string - "c13FZpr4odg="
Please Help for the same as i stuck here
What I am trying to do is to encrypt a string with AES, encrypt the AES key getEncoded() value with RSA, then decrypt that AES getEncoded() value so that I get my original string. The public key is loaded from the users certificate, and the private key from file.
The code is given below.
public class Main {
public static void main(String[] args) throws Exception {
String myString = "My Message";
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
SecretKey secretKey = keyGenerator.generateKey();
byte[] initializationVector = new byte[128 / 8];//16
SecureRandom prng = new SecureRandom();
prng.nextBytes(initializationVector);
Cipher AESCipherForEncryption = Cipher.getInstance("AES/CBC/PKCS5PADDING");
AESCipherForEncryption.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(initializationVector));
byte[] byteVersionOfMyMessage = myString.getBytes();
byte[] byteVersionOfCipherText = AESCipherForEncryption.doFinal(byteVersionOfMyMessage);
String cipherText = new BASE64Encoder().encode(byteVersionOfCipherText);
InputStream in1 = new FileInputStream("user.crt");
CertificateFactory cf1 = CertificateFactory.getInstance("X509");
Certificate c1 = cf1.generateCertificate(in1);
X509Certificate toSendcert = (X509Certificate) c1;
PublicKey publicKey = toSendcert.getPublicKey();
String cipherTextRSA = encryptRSA(publicKey, new String(secretKey.getEncoded()));
String decypheredRSA = decryptRSA(getPrivateKey("user.pk8", "RSA"), cipherTextRSA);
System.out.println(cipherTextRSA);
System.out.println(decypheredRSA);
SecretKey originalKey = new SecretKeySpec(new String(decypheredRSA.getBytes("UTF-8")).getBytes(), 0, new String(decypheredRSA.getBytes("UTF-8")).getBytes().length, "AES");
Cipher AESCipherForDecryption = Cipher.getInstance("AES/CBC/PKCS5PADDING");
AESCipherForDecryption.init(Cipher.DECRYPT_MODE, originalKey, new IvParameterSpec(initializationVector));
byte[] byteVersionOfDecriptedText = AESCipherForDecryption.doFinal(new BASE64Decoder().decodeBuffer(cipherText));
String decMessage = new String(byteVersionOfDecriptedText);
System.out.println(decMessage);
}
public static String encryptRSA(PublicKey pubKey, String message) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
Base64.Encoder encoder = Base64.getEncoder();
String encryptedString = encoder.encodeToString(cipher.doFinal(message.getBytes("UTF-8")));
return encryptedString;
}
public static PrivateKey getPrivateKey(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-----", "");
privKeyPEM = privKeyPEM.replace("\n", "");
byte[] decoded = Base64.getDecoder().decode(privKeyPEM);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance(algorithm);
return kf.generatePrivate(spec);
}
public static String decryptRSA(PrivateKey prKey, String encrypted) throws Exception {
Base64.Decoder decoder = Base64.getDecoder();
byte[] input = decoder.decode(encrypted);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, prKey);
return new String(cipher.doFinal(input));
}
The error that I keep getting is:
Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 28 bytes
at com.sun.crypto.provider.AESCipher.engineGetKeySize(AESCipher.java:509)
at javax.crypto.Cipher.passCryptoPermCheck(Cipher.java:1067)
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1038)
at javax.crypto.Cipher.implInit(Cipher.java:805)
at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
at javax.crypto.Cipher.init(Cipher.java:1396)
at javax.crypto.Cipher.init(Cipher.java:1327)
at com.company.Main.main(Main.java:79)
If I don't encrypt and decrypt the secretKey.getEncoded() value, and just use AES without RSA it works properly. Also working with RSA, if I just encrypt some string with a public key, and decrypt it with a private it works. My question would be: "How could I properly encrypt and decrypt the secretKey.getEncoded() value with RSA, so that I can properly encrypt and decrypt myString?".
new String(secretKey.getEncoded())
This won't work as AES keys contain random bytes, and not every byte is a character representative. The problem with the standard string conversion in Java is that it drops unknown characters and bytes instead of generating an exception during encoding / decoding.
RSA operates on bytes, you should not turn the key into string and then back again into bytes as the transformation may be lossy (e.g. dropping 4 of the 32 bytes).
Alternatively - and probably even better - you may want to try the wrapping modes of cipher instead. This should be compatible with some hardware solutions out there. In that case you don't even have to call getEncoded.
OAEP encryption and authenticated encryption modes such as GCM should be preferred over PKCS#1 padding (the default for the Sun providers) and CBC mode encryption.
i am trying to decrypt using AES/CFB mode by using below code,
final static public String ENCRYPT_KEY = "4EBB854BC67649A99376A7B90089CFF1";
final static public String IVKEY = "ECE7D4111337A511F81CBF2E3E42D105";
private static String deCrypt(String key, String initVector, String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skSpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, skSpec, iv);
byte[] original = cipher.doFinal(encrypted.getBytes());
return new String(original);
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
and throw below error,
Wrong IV length: must be 16 bytes long.
Above ENCRYPT_KEY and IVKEY are valid one. can any one help in that ?
You're calling "ECE7D4111337A511F81CBF2E3E42D105".getBytes("UTF-8"); which will result in byte[] of size 32, not to mention a completely wrong IV.
You need to parse the String into a byte[] instead, for example by borrowing the DatatypeConverter from javax.xml.bind.
IvParameterSpec iv = new IvParameterSpec(
javax.xml.bind.DatatypeConverter.parseHexBinary(initVector));
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");
Why does not this AES encryption work? I've written it in Java to test, but I am not able to decrypt. I get garbage upon decryption. Why? Its so simple - In the main method, print plain text, encrypt, print cipher text, decrypt, print plain text again. Am I doing something wrong? Please help me figure out the problem.
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AESTest {
public static void main(String [] args) {
try {
String plainText = "Hello World!!!!!";
String encryptionKey = "E072EDF9534053A0B6C581C58FBF25CC";
System.out.println("Before encryption - " + plainText);
String cipherText = encrypt(plainText, encryptionKey);
System.out.println("After encryption - " + cipherText);
String decrypted = decrypt(cipherText, encryptionKey);
System.out.println("After decryption - " + decrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String encrypt(String plainText, String passkey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
String cipherText = new String(cipher.doFinal(plainText.getBytes()));
return cipherText;
}
public static String decrypt(String cipherText, String passkey) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
String plainText = new String(cipher.doFinal(cipherText.getBytes()));
return plainText;
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
}
The output of the cipher is a sequence of random-looking bytes. You have no guarantee that these bytes will be a valid encoding for a character string in whatever is your system's default encoding. So this line:
String cipherText = new String(cipher.doFinal(.....));
is likely to lose information that you'll need for decryption.
Therefore you will not get the right bytes reconstructed in your decrypt operation. For example, if your default encoding is UTF-8, it is overwhelmingly unlikely that the correct ciphertext is something that String.getBytes() is even able to produce.
Two things:
No padding can only work if you use input that is an exact mulitple of your key size, which is 128 bit or 16 bytes. So in your particular case "Hello World!!!!!".getBytes() is actually a multiple of 16, but this is of course not true for arbitrary Strings.
Use "AES/CBC/PKCS5Padding" instead to solve this issue.
Do not turn your encrypted data into a String - this will and change the encrypted output. There's no guarantee that new String(byte[]).getBytes() returns the exact same byte array!
So you should leave the encrypted data as what it is - a stream of bytes. Thus encrypt should return byte[] instead and decrypt should take byte[] as input - this is a working example:
public class NewClass {
public static void main(String [] args) {
try {
String plainText = "Hello World!!!!";
String encryptionKey = "E072EDF9534053A0B6C581C58FBF25CC";
System.out.println("Before encryption - " + plainText);
byte[] cipherText = encrypt(plainText, encryptionKey);
System.out.println("After encryption - " + cipherText);
String decrypted = decrypt(cipherText, encryptionKey);
// -> Hello World!!!!
System.out.println("After decryption - " + decrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] encrypt(String plainText, String passkey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
return cipher.doFinal(plainText.getBytes());
}
public static String decrypt(byte[] cipherText, String passkey) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
return new String(cipher.doFinal(cipherText));
}
You need to create the SecretKeySpec object once and use it for both encrypt and decrypt. Currently the code is creating two different keys for each operation and this will definitely lead to incorrect results.