Copying Byte Array - java

I'm experimenting with AES encryption. However, I get an error I wrote from the following code:
private static byte[] readKey(String request) throws KeyFileNotFoundException,
UnsupportedEncodingException, UnknownKeyException {
File keyFile = new File(Logging.getCurrentDir() + "\\cikey.key");
Properties keys = new Properties();
byte[] storage;
if (!keyFile.exists())
throw new KeyFileNotFoundException("Key file not located.");
if (keys.containsKey(request) == false)
throw new UnknownKeyException("Key not found."); //I RECIEVE THIS ERROR
storage = keys.getProperty(request).getBytes(); //read the STRING value and turn into a byte array
return storage;
}
This is the code from the method calling readKey(). I also am having problems copying the byte array read in through the readKey() method to the decrypt() method. Please read the comments in the methods for more detailed explanations.
public static String decrypt(String in) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException,
IllegalBlockSizeException, BadPaddingException, IOException,
KeyFileNotFoundException, UnknownKeyException {
String out = " "; //decrypted String to return
byte[] key = readKey("key").clone(); //my attempt to copy a byte array
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
byte iv[] = readKey("iv"); //works here? same as above so I don't know.
IvParameterSpec ivspec = new IvParameterSpec(iv);
//initialize the cipher for decryption
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivspec);
// decrypt the message
byte[] decrypted = cipher.doFinal(in.getBytes());
out = asHex(decrypted);
return out;
}
Any ideas to fixing this problem?

You need to use System.arrayCopy() to copy arrays.
EDIT
Another option is to use Arrays.copyOf() although that just uses System.arrayCopy() under the hood.

You have to load the keys variable using one of Properties.load methods before checking for a key.
Additionally you can clone the array using System.arraycopy, the clone method or Arrays.copyOfRange

You are not initialize Property with values in the file so keys is empty and when you are calling keys.contains("") its returning false you have write
InputStream stream=new FileInputStream(keyFile);
keys.load(stream);
after property keys=new Property();
in readKey() method then you will get
a byte array if you will try to find a
valid key.

Related

Problem with java Cipher when i want to decipher it

I have made an app with javafx that I can write something and save it to database. my database is sqlite. it's a very simple app. although I've added login app to my writing app, still the sqlite can be opened by any software.
instead of encrypting the sqlite db(which i didn't know and i found really confusing to do :) ) I decided to encrypt the text in java and later when i want to read it i would turn it back to normal and show it.
I learned how to do it from this link and i changed it to print the string instead of writing to a file
so my final code looks like this:
public static void main(String[] args) throws Exception {
String textA = "";
String textB="";
byte[] thisismykey = "Hello How manyBytes are in#hts A".getBytes();
SecretKey secKey = new SecretKeySpec(thisismykey, "AES");
Cipher aesCipher = Cipher.getInstance("AES");
//turn your original text to byte
byte[] myoriginaltexttobyte = "Your Plain Text Here".getBytes();
//activate the encrypt method
aesCipher.init(Cipher.ENCRYPT_MODE, secKey);
//encrypt the text and assign the encrypted text to a byte array
byte[] bytecipheredoforgtext = aesCipher.doFinal(myoriginaltexttobyte);
//change it to string with new string
textA = new String(bytecipheredoforgtext);
System.out.println(textA);
//get the bytes of encrypted text and assign it to a byte array
byte[] byteofencryptedtext = textA.getBytes();
//activate the decrypt mode of the cipher
aesCipher.init(Cipher.DECRYPT_MODE, secKey);
//decrypt the encrypted text and assign it to a byte array
byte[] byteofencryptedbacktonormaltext = aesCipher.doFinal(byteofencryptedtext);
//change it to string with new string
textB = new String(byteofencryptedbacktonormaltext);
System.out.println(textB);
}
now that encrypt and decrypt are at the same method it works perfectly but I want to change it to a class with different methods so i could encrypt a text with one method and decrypt it with another. but when i separate things decrypt doesn't work well. Encrypt work well. this is the code now:
public class CipherFinalB {
//from https://stackoverflow.com/questions/20796042/aes-encryption-and-decryption-with-java/20796446#20796446
private final byte[] thisismykey;
private final SecretKey secKey;
private final Cipher aesCipher;
public CipherFinalB() throws NoSuchPaddingException, NoSuchAlgorithmException {
thisismykey = "HellodHowfmanyBytesgarehin#htseA".getBytes();
secKey = new SecretKeySpec(thisismykey, "AES");
aesCipher = Cipher.getInstance("AES");
}public String encrypt (String originaltext) throws InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
byte[] myoriginaltexttobyte =originaltext.getBytes();
aesCipher.init(Cipher.ENCRYPT_MODE, secKey);
byte[] bytecipheredoforgtext = aesCipher.doFinal(myoriginaltexttobyte);
String textA = new String(bytecipheredoforgtext);
System.out.println(textA);
return new String(bytecipheredoforgtext);
}
public String decrypt (String encryptedtext) throws InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
byte[] byteofencryptedtext = encryptedtext.getBytes();
aesCipher.init(Cipher.DECRYPT_MODE, secKey);
byte[] byteofencryptedbacktonormaltext = aesCipher.doFinal(byteofencryptedtext);
return new String(byteofencryptedbacktonormaltext);
}
}
when i use the encrypt method it gives me back a string. and when i send the same string to decrypt method it doesn't work and gives me the following error:
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:936)
at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:847)
at java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2191)
at main.CipherFinalB.decrypt(CipherFinalB.java:66)
at main.CipherTest.main(CipherTest.java:16)
What should i do?
UPDATE ANSWER:
as #Juan said the problem was "when data is ciphered you may have any byte in the array, not only printable characters." So I changed the method to return byte for encrypt method and decrypt method. decrypt method now gets byte as parameter instead of string and now everything works fine.
the updated code looks like this:
public byte[] encrypt (String originaltext) throws InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
byte[] myoriginaltexttobyte =originaltext.getBytes();
aesCipher.init(Cipher.ENCRYPT_MODE, secKey);
byte[] bytecipheredoforgtext = aesCipher.doFinal(myoriginaltexttobyte);
String textA = new String(bytecipheredoforgtext);
System.out.println(textA);
return bytecipheredoforgtext;
}
public byte[] decrypt (byte[] encryptedtext) throws InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
byte[] byteofencryptedtext = encryptedtext;
aesCipher.init(Cipher.DECRYPT_MODE, secKey);
byte[] byteofencryptedbacktonormaltext = aesCipher.doFinal(byteofencryptedtext);
return byteofencryptedbacktonormaltext;
}
I am not sure that the error is caused by this but the conversion between String and byte[] you are doing may work with bytes in the character set range, but when data is ciphered you may have any byte in the array, not only printable characters.
After you get the byte[] with the cyphered text, encode it as Base64 and store the string representation.
When decrypting, first decode the Base64 into the byte[] and then decipher it.
See Base64 class

Java AES encryption/decryption procedure and usage of Initialization Vector

I want to learn the basics of AES encryption so I started to make a very simple Java program. The program loads a text file in to a String and asks for a key from the user. The program then uses AES to encrypt the text creating a new text file with the encrypted text. The program prints the Initialization Vector (IV) to the user.
The program also has the decryption function. The user specifies the encrypted text file along with the Initialization Vector and the key to decrypt it back to the original text in a new text file.
However I think I'm doing something wrong. Is it normal procedure in AES encryption that the user needs to have both key and IV to decrypt the file? I have browsed through the internet and almost in every example, the encrypted data can be decrypted by the user specifying only the key but in my case the user needs to have both the key and the IV. The program is working fine but I think it isn't efficient.
So should I use a constant, known IV which is used in all the encryptions and decryptions or what? Also some tutorials are using "salt", what is it and should I use it?
Here are my encrypt and decrypt methods:
public String encrypt(String stringToEncrypt, String userKey)
throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
// User gives string key which is formatted to 16 byte and to a secret
// key
byte[] key = userKey.getBytes();
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
// Cipher initialization
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// Encryption and encoding
String encryptedData = new BASE64Encoder().encode(cipher
.doFinal(stringToEncrypt.getBytes()));
// IV is printed to user
System.out.println("\nENCRYPTION IV: \n"
+ new BASE64Encoder().encode(cipher.getIV()) + "\n");
// Function returns encrypted string which can be writed to text file
return encryptedData;
}
public String decrypt(String stringToDecrypt, String userKey, String userIv)
throws NoSuchAlgorithmException, IOException,
NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException,
BadPaddingException {
// User gives the same string key which was used for encryption
byte[] key = userKey.getBytes();
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
// Decode string iv to byte
byte[] iv = new BASE64Decoder().decodeBuffer(userIv);
// Cipher initialization
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
// Decryption and decoding
String decryptedData = new String(cipher.doFinal(new BASE64Decoder()
.decodeBuffer(stringToDecrypt)));
// Function returns decrypted string which can be writed to text file
return decryptedData;
}
UPDATE
I updated my code now to use "PBKDF2WithHmacSHA256" algorithm with salt and etc. I also combined the Initialization Vector (IV) byte array to the cipher text byte array as prefix so I can split them in decrypt method and get the IV there (That's working fine).
However there's now an issue with the key, because I'm generating new encrypted key also in decryption method which of course is a wrong key for encrypted data. I want to be able to close the program so I can't store the key as a class variable. It's very hard to explain the issue but I hope you understand the problem...
public static byte[] getEncryptedPassword(String password, byte[] salt,
int iterations, int derivedKeyLength)
throws NoSuchAlgorithmException, InvalidKeySpecException {
KeySpec mKeySpec = new PBEKeySpec(password.toCharArray(), salt,
iterations, derivedKeyLength);
SecretKeyFactory mSecretKeyFactory = SecretKeyFactory
.getInstance("PBKDF2WithHmacSHA256");
return mSecretKeyFactory.generateSecret(mKeySpec).getEncoded();
}
public String encrypt(String dataToEncrypt, String key) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidParameterSpecException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException {
byte[] mEncryptedPassword = getEncryptedPassword(key, generateSalt(),
16384, 128);
SecretKeySpec mSecretKeySpec = new SecretKeySpec(mEncryptedPassword, "AES");
Cipher mCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
mCipher.init(Cipher.ENCRYPT_MODE, mSecretKeySpec);
byte[] ivBytes = mCipher.getIV();
byte[] encryptedTextBytes = mCipher.doFinal(dataToEncrypt.getBytes());
byte[] combined = new byte[ivBytes.length+encryptedTextBytes.length];
System.arraycopy(ivBytes, 0, combined, 0, ivBytes.length);
System.arraycopy(encryptedTextBytes, 0, combined, ivBytes.length, encryptedTextBytes.length);
return Base64.getEncoder().encodeToString(combined);
}
public String decrypt(String dataToDecrypt, String key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidKeySpecException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
byte[] encryptedCombinedBytes = Base64.getDecoder().decode(dataToDecrypt);
byte[] mEncryptedPassword = getEncryptedPassword(key, generateSalt(),
16384, 128);
byte[] ivbytes = Arrays.copyOfRange(encryptedCombinedBytes,0,16);
SecretKeySpec mSecretKeySpec = new SecretKeySpec(mEncryptedPassword, "AES");
Cipher mCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
mCipher.init(Cipher.DECRYPT_MODE, mSecretKeySpec, new IvParameterSpec(ivbytes));
byte[] encryptedTextBytes = Arrays.copyOfRange(encryptedCombinedBytes, 16, encryptedCombinedBytes.length);
System.out.println(encryptedTextBytes.length);
byte[] decryptedTextBytes = mCipher.doFinal(encryptedTextBytes);
return Base64.getEncoder().encodeToString(decryptedTextBytes);
}
public byte[] generateSalt() {
SecureRandom random = new SecureRandom();
byte saltBytes[] = new byte[16];
random.nextBytes(saltBytes);
return saltBytes;
}}
I hope somebody knows how to make this better. Thanks!
Just save the IV in the file before the encrypted data.
You should never use the same IV more than once (it's ok-ish, if you roll a new IV every time, and it just so happens that you roll the same twice, so you don't have to store and check that). Using the same IV many times poses a great security risk, as encrypting the same content twice reveals that it's - in fact - the same content.
Storing IV alongside the encrypted data is a common, and secure procedure, as it's role is to introduce "randomness" to the encryption scheme, and it shouldn't be secret, just securely (and in some schemes randomly) generated.

How to encrypt a string with AES to a string with only specific characters?

I want to use a barcode (code 39) to represent a string, and I want this string to be encrypted using AES.
However, I can only display 43 characters with the barcode. How can I encrypt it so that the result uses only the available set of characters?
Here's what I have so far:
public static byte[] encryptAES(String seed, String cleartext)
throws Exception {
byte[] rawKey = getRawKey(seed.getBytes("ASCII"));
SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
return cipher.doFinal(cleartext.getBytes("ASCII"));
}
private static byte[] getRawKey(byte[] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
kgen.init(BLOCKS, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
public static void main(String [] args)
{
String str = "312432432";
String key = "4AFJ3243J";
String result = new String(encryptAES(key,str), "ASCII");
}
Thanks!
What you have is an encoding issue, the problem being you want to convert to a non-standard encoding. What I would do is convert to a base43 encoding. However, you will likely need to implement your own conversion. You should look into how to convert between arbitrary bases, and do the conversion on the byte output of the encryption. Essentially you will take the base10 value of the byte (between 0 and 255 if unsigned), and convert it to two different base43 characters.
A quick Google search for base43 gave me this. Which I haven't used myself, but looks like it could work.

Memory issues/leaks in AES encryption/decryption of strings in java

My problem is that I am decrypting/encrypting some string sets of random values from different threads but after many iterations the memory increases rapidly.
My observation is that memory increases because each encryption/decryption result in new String and due to it the memory increases.
One more point to note is that my decrypted/encrypted strings would be having so many same values as same sets of strings (some strings might be new) are encrypted/decrypted from many thread but since in each encrpt/decrypt the cipher returns the byte array and to constitute the String again I have to use 'new String()' function and this might or will increase memory rapidly.
This is my code to encrypt/decrypt strings
public static String encrypt(String key, String value) throws GeneralSecurityException
{
byte[] raw = 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.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
byte[] cipherBytes= cipher.doFinal(value.getBytes());
byte[] encoded = org.apache.commons.codec.binary.Base64.encodeBase64(cipherBytes);
return new String(encoded);
}
public static String decrypt(String key, String encrypted) throws GeneralSecurityException
{
byte[] raw = 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[] byteDecodedText = org.apache.commons.codec.binary.Base64.decodeBase64(encrypted.getBytes()) ;
byte[] original = cipher.doFinal(byteDecodedText);
return new String(original);
}
String intern method is probably is the way to go. It returns canonical representation of the string, so for to identical strings you will have only one instance.
So when you return a String, just try to do:
new String(byteDecryptedText).intern();
an instance of the String that you create by using new will be collected after you get out of the scope. Internal object will be returned.
It is true that each iteration will result in new String but I do not think that will increase memory very rapidly as the memory required by a String is in bytes.
According to String memory usage you can calculate size of String you encrypt or decrypt. This can provide you some idea whether memory increased is due to formation of new String everytime.

AES BadPaddingException

if I use a wrong key or wrong salt for decryption an BadPaddingException is thrown.
I would expect an incorrect string to be returned.
The doFinal() causes the exception in the decrypt-method
Message : This is just an example
Unfug : 'ΩÙΩ„SåF?V®ßs.k˚·ºç€èÀHfif∫ÙÉÕ
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at casino.AES.decryptString(AES.java:130)
at casino.AES.main(AES.java:172)
public static void main(String[] args) throws Exception {
//Encryption
AES encr = new AES();
encr.setKey("KEY");
encr.setSalt("SALT");
encr.setup();
String message = "This is just an example";
System.out.println("Message : " + message);
byte[] code = encr.encrypt(message);
System.out.println("Encrypted Strinng : "+ new String(code, "UTF-8"));
//Decryption
AES dec = new AES();
dec.setKey("INCORRECT"); //<--- incorrect
dec.setSalt("SALT");
dec.setup();
System.out.println(dec.decryptString(code));
}
public synchronized void setKey(String key) throws UnsupportedEncodingException {
this.key = key.getBytes("UTF-8");
isPasswordAlreadySet = true;
}
public synchronized void setSalt(String salt) throws UnsupportedEncodingException {
this.salt = salt.getBytes("UTF-8");
}
public synchronized void setup() throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(key);
digest.update(salt);
byte[] raw = digest.digest();
skeySpec = new SecretKeySpec(raw, "AES");
cipher = Cipher.getInstance("AES");
}
public synchronized byte[] encrypt(byte[] klartext) throws Exception {
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(klartext);
return encrypted;
}
public synchronized byte[] encrypt(String klartext) throws Exception{
return encrypt(klartext.getBytes("UTF-8"));
}
public synchronized byte[] decrypt(byte[] code) throws Exception {
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] original = cipher.doFinal(code);
return original;
}
public synchronized double decryptDouble(byte[] code) throws Exception {
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] original = cipher.doFinal(code);
return doubleFromBytes( original);
}
Thank you!
Frederik
The padding is a fine sanity check. Assuming the incorrectly decrypted data is uniformly distributed, it will only appear to be correctly PKCS5/PKCS7 padded about 1 time for every 255 incorrect passwords. (1/256 + 1/256^2 + 1/256^3 ...)
So it's helpful, but it's not something you should rely on --- what is effectively an almost-8-bit message digest is not a sufficient test of data integrity.
One more thing: If an attacker can repeatedly alter the ciphertext and get you to decrypt it, (an example might be encrypted data stored in a cookie) and if they can distinguish between your behavior when the decrypted data throws an exception for bad padding and when it is simply garbage, then they can determine the plaintext via a "padding oracle attack".
By the way, if you actually want the behavior you expected, you can use "AES/CTR/NoPadding", which will not require an exact block size and will always return a decrypted byte[], whether or not the keys match.
You should either use AES with an implicit padding declaration (see the available modes) or force the length (in bytes) of data encrypted/decrypted to be a multiple of 16.
Also, by default, java uses ECB mode, which can be really unsecured depending on which type of data you are using, you should probably use a CBC mode.
SecretKeySpec specifies a secret key in a provider-independent fashion
http://themasterofmagik.wordpress.com/2014/03/19/simple-aes-encryption-and-decryption-in-java/

Categories