I've looked for javascript libraries that can decrypt against an AES 128 encrypted String. I've found several :
http://www.movable-type.co.uk/scripts/aes.html
http://www.hanewin.net/encrypt/aes/aes-test.htm : you will have to look the source
https://code.google.com/p/crypto-js/
My problem is these algoritms take as input either a String or a HexString. My case is a bit special, because my input in a byte array. I've coded a test case in Java :
String key = "MrSShZqHM6dtVNdX";
String message = "NzZiNGM3ZjIyNjM5ZWM3M2YxMGM5NjgzZDQzZDA3ZTQ=";
String charsetName = "UTF-8";
String algo = "AES";
// decode message
byte[] decodeBase64 = Base64.decodeBase64(message.getBytes(charsetName));
System.out.println("decoded message: " + new String(decodeBase64));
// prepare the key
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(charsetName), algo);
// aes 128 decipher
Cipher cipher = Cipher.getInstance(algo);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] doFinal = cipher.doFinal(Hex.decodeHex(new String(decodeBase64).toCharArray()));
System.out.println("done with: " + new String(doFinal));
Output is :
decoded message: 76b4c7f22639ec73f10c9683d43d07e4
done with: 390902
But this is Java, right? The org.apache.commons.codec.binary.Hex.decodeHex method converts an array of characters representing hexadecimal values into an array of bytes of those same values. The returned array will be half the length of the passed array, as it takes two characters to represent any given byte. An exception is thrown if the passed char array has an odd number of elements.
In decimal representation, Hex.decodeHex method gives this byte array : [118, -76, -57, -14, 38, 57, -20, 115, -15, 12, -106, -125, -44, 61, 7, -28];
The java AES decipher takes a byte array as input, but in Javascript, no lib does that. I've tried to tweak a bit the one here but dude that's hardcore code. This is really not my field...
The closest I've been was on this online tool. My key is MrSShZqHM6dtVNdX and with apache commons Hex.encodeHex I get 4d725353685a71484d366474564e6458 giving me an output of 3339303930320a0a0a0a0a0a0a0a0a0a, which is almost my wanted output (390902)...
Related
There are a lot of questions with this topic, the same solution, but this doesn't work for me. I have a simple test with an encryption. The encryption/decryption itself works (as long as I handle this test with the byte array itself and not as Strings). The problem is that don't want to handle it as byte array but as String, but when I encode the byte array to string and back, the resulting byte array differs from the original byte array, so the decryption doesn't work anymore. I tried the following parameters in the corresponding string methods: UTF-8, UTF8, UTF-16, UTF8. None of them work. The resulting byte array differs from the original. Any ideas why this is so?
Encrypter:
public class NewEncrypter
{
private String algorithm = "DESede";
private Key key = null;
private Cipher cipher = null;
public NewEncrypter() throws NoSuchAlgorithmException, NoSuchPaddingException
{
key = KeyGenerator.getInstance(algorithm).generateKey();
cipher = Cipher.getInstance(algorithm);
}
public byte[] encrypt(String input) throws Exception
{
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] inputBytes = input.getBytes("UTF-16");
return cipher.doFinal(inputBytes);
}
public String decrypt(byte[] encryptionBytes) throws Exception
{
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] recoveredBytes = cipher.doFinal(encryptionBytes);
String recovered = new String(recoveredBytes, "UTF-16");
return recovered;
}
}
This is the test where I try it:
public class NewEncrypterTest
{
#Test
public void canEncryptAndDecrypt() throws Exception
{
String toEncrypt = "FOOBAR";
NewEncrypter encrypter = new NewEncrypter();
byte[] encryptedByteArray = encrypter.encrypt(toEncrypt);
System.out.println("encryptedByteArray:" + encryptedByteArray);
String decoded = new String(encryptedByteArray, "UTF-16");
System.out.println("decoded:" + decoded);
byte[] encoded = decoded.getBytes("UTF-16");
System.out.println("encoded:" + encoded);
String decryptedText = encrypter.decrypt(encoded); //Exception here
System.out.println("decryptedText:" + decryptedText);
assertEquals(toEncrypt, decryptedText);
}
}
It is not a good idea to store encrypted data in Strings because they are for human-readable text, not for arbitrary binary data. For binary data it's best to use byte[].
However, if you must do it you should use an encoding that has a 1-to-1 mapping between bytes and characters, that is, where every byte sequence can be mapped to a unique sequence of characters, and back. One such encoding is ISO-8859-1, that is:
String decoded = new String(encryptedByteArray, "ISO-8859-1");
System.out.println("decoded:" + decoded);
byte[] encoded = decoded.getBytes("ISO-8859-1");
System.out.println("encoded:" + java.util.Arrays.toString(encoded));
String decryptedText = encrypter.decrypt(encoded);
Other common encodings that don't lose data are hexadecimal and base64, but sadly you need a helper library for them. The standard API doesn't define classes for them.
With UTF-16 the program would fail for two reasons:
String.getBytes("UTF-16") adds a byte-order-marker character to the output to identify the order of the bytes. You should use UTF-16LE or UTF-16BE for this to not happen.
Not all sequences of bytes can be mapped to characters in UTF-16. First, text encoded in UTF-16 must have an even number of bytes. Second, UTF-16 has a mechanism for encoding unicode characters beyond U+FFFF. This means that e.g. there are sequences of 4 bytes that map to only one unicode character. For this to be possible the first 2 bytes of the 4 don't encode any character in UTF-16.
Accepted solution will not work if your String has some non-typical charcaters such as š, ž, ć, Ō, ō, Ū, etc.
Following code worked nicely for me.
byte[] myBytes = Something.getMyBytes();
String encodedString = Base64.encodeToString(bytes, Base64.NO_WRAP);
byte[] decodedBytes = Base64.decode(encodedString, Base64.NO_WRAP);
Now, I found another solution too...
public class NewEncrypterTest
{
#Test
public void canEncryptAndDecrypt() throws Exception
{
String toEncrypt = "FOOBAR";
NewEncrypter encrypter = new NewEncrypter();
byte[] encryptedByteArray = encrypter.encrypt(toEncrypt);
String encoded = String.valueOf(Hex.encodeHex(encryptedByteArray));
byte[] byteArrayToDecrypt = Hex.decodeHex(encoded.toCharArray());
String decryptedText = encrypter.decrypt(byteArrayToDecrypt);
System.out.println("decryptedText:" + decryptedText);
assertEquals(toEncrypt, decryptedText);
}
}
Your problem is that you cannot build a UTF-16 (or any other encoding) String from an arbitrary byte array (see UTF-16 on Wikipedia). It is up to you, however, to serialize and deserialize the encrypted byte array without any loss, in order to, say, persist it, and make use of it later. Here's the modified client code that should give you some insight of what's actually happening with the byte arrays:
public static void main(String[] args) throws Exception {
String toEncrypt = "FOOBAR";
NewEncrypter encrypter = new NewEncrypter();
byte[] encryptedByteArray = encrypter.encrypt(toEncrypt);
System.out.println("encryptedByteArray:" + Arrays.toString(encryptedByteArray));
String decoded = new String(encryptedByteArray, "UTF-16");
System.out.println("decoded:" + decoded);
byte[] encoded = decoded.getBytes("UTF-16");
System.out.println("encoded:" + Arrays.toString(encoded));
String decryptedText = encrypter.decrypt(encryptedByteArray); // NOT the "encoded" value!
System.out.println("decryptedText:" + decryptedText);
}
This is the output:
encryptedByteArray:[90, -40, -39, -56, -90, 51, 96, 95, -65, -54, -61, 51, 6, 15, -114, 88]
decoded:<some garbage>
encoded:[-2, -1, 90, -40, -1, -3, 96, 95, -65, -54, -61, 51, 6, 15, -114, 88]
decryptedText:FOOBAR
The decryptedText is correct, when restored from the original encryptedByteArray. Please note that the encoded value is not the same as encryptedByteArray, due to the data loss during the byte[] -> String("UTF-16")->byte[] conversion.
In one of our internal software we're implementing a new API endpoint which must be accessed by external sources through the internet and then it must be secured in some way.
Since we're not allowed to use library as OAuth or public and private keys we choosed javax.crypto AES to crypt out a "custom authorisation token" in each external source by this way:
...
Key aesKey = new SecretKeySpec("API-KEY".getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] applicationIdEncrypted = cipher.doFinal(applicationId.getBytes());
...
The token contains a custom applicationId to identify on the other side who's contacting that endpoint.
Since we must perform an HTTP call, we're converting applicationIdEncrypted into a base64 String
String base64Encoded = Base64.getEncoder().encodeToString(applicationIdEncrypted);
ON THE OTHER SIDE
We're getting the header and decode it from base64
String base64Decoded = new String(Base64.getDecoder().decode(header));
But when attempting to perform the last operation
Key aesKey = new SecretKeySpec("API-KEY".getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, aesKey);
String headerDecoded = new String(cipher.doFinal(base64Decoded.getBytes())); //<- THIS
We got javax.crypto.BadPaddingException: Given final block not properly padded
Both base64Encoded and base64Decoded have the same value in both of the ends.
Attempting to perform the same operation in one of the ends (to not use the HTTP channel) no exception is thrown -but- a different headerDecoded is returned by the new String(cipher.doFinal(base64Decoded.getBytes()));
Looked for the bytes applicationIdEncrypted and base64Decoded.getBytes() and they're slightly different:
applicationIdEncrypted
[-28, -103, 107, 70, -112, 121, 4, -14, -80, -114, -14, 92, -81, -13, -128, 97]
base64Decoded.getBytes()
[-28, -103, 107, 70, 63, 121, 4, -14, -80, -114, -14, 92, -81, -13, -128, 97]
I read that maybe passing from bytes to String could be a loss of informations (maybe?) but I cannot figure it out why of this behaviours since both base64Encoded and base64Decoded have the SAME value in both cases and scenarios.
How can I achieve the passage of a "custom authorisation token" using only Java 1.7 javax.crypto libraries?
EDIT
The "API-KEY" is something like 02E30E6BE24BF1EA
As #James K Polk says, I had a mess with the thousand of String conversion so I managed to have a cleaner code first for a better comprehensive code.
ON THE CLIENT
Key aesKey = new SecretKeySpec("API-KEY".getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] applicationIdEncrypted = cipher.doFinal(applicationId.getBytes());
byte[] base64Encoded = Base64.getEncoder().encode(applicationIdEncrypted);
String out = new String(base64Encoded);
where out is the only one conversion in String and it's the HTTP header's payload.
ON THE OTHER SIDE
byte[] in = out.getBytes();
byte[] base64Decoded = Base64.getDecoder().decode(in);
Key aesKey = new SecretKeySpec("API-KEY".getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, aesKey);
byte[] applicationIdDecrypted = cipher.doFinal(base64Decoded);
String applicationId= new String(applicationIdDecrypted);
I had ONLY two conversion into String: out (the header's base64 value) and applicationId.
In this way I used to have the same applicationId value.
Let's say I have a Base64 String:
data = AOUTl5C2QV2xFRPlzKR0Ag==
I want to generate a Key in Java (Android) from the first 10 characters of this Base64 String and then use it to AES-Decrypt a Message sent from the Server. To do this, I use the below code:
String firstTen = data.substring(0, 10);
byte[] decodedBytes = Base64.decode(firstTen, Base64.DEFAULT);
SecretKeySpec key = new SecretKeySpec(decodedBytes, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] original = cipher.doFinal(Message_to_Decrypt, Base64.DEFAULT));
But then I can a Java.security.Exception:
java.security.InvalidKeyException: Key length not 128/192/256 bits.
Is there a way that I can get a valid Key which I can use for AES decryption from the first 10 Characters of a Base64String?
Extend the 10 characters with a hash function or better yet PBKDF2 (Password Based Key Derivation Function 2).
You really need to provide a key of an expected length, AES keys can be 128, 192 or 256 bytes long. While some AED implementations may null pad the key do not rely on that, it is not part of the standard.
The error message says: Key length not 128/192/256 bits.
You are using 10 Characters, each char is 8 bits. So 10*8=80. Try with 16 characters (128/8=16).
There are a lot of questions with this topic, the same solution, but this doesn't work for me. I have a simple test with an encryption. The encryption/decryption itself works (as long as I handle this test with the byte array itself and not as Strings). The problem is that don't want to handle it as byte array but as String, but when I encode the byte array to string and back, the resulting byte array differs from the original byte array, so the decryption doesn't work anymore. I tried the following parameters in the corresponding string methods: UTF-8, UTF8, UTF-16, UTF8. None of them work. The resulting byte array differs from the original. Any ideas why this is so?
Encrypter:
public class NewEncrypter
{
private String algorithm = "DESede";
private Key key = null;
private Cipher cipher = null;
public NewEncrypter() throws NoSuchAlgorithmException, NoSuchPaddingException
{
key = KeyGenerator.getInstance(algorithm).generateKey();
cipher = Cipher.getInstance(algorithm);
}
public byte[] encrypt(String input) throws Exception
{
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] inputBytes = input.getBytes("UTF-16");
return cipher.doFinal(inputBytes);
}
public String decrypt(byte[] encryptionBytes) throws Exception
{
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] recoveredBytes = cipher.doFinal(encryptionBytes);
String recovered = new String(recoveredBytes, "UTF-16");
return recovered;
}
}
This is the test where I try it:
public class NewEncrypterTest
{
#Test
public void canEncryptAndDecrypt() throws Exception
{
String toEncrypt = "FOOBAR";
NewEncrypter encrypter = new NewEncrypter();
byte[] encryptedByteArray = encrypter.encrypt(toEncrypt);
System.out.println("encryptedByteArray:" + encryptedByteArray);
String decoded = new String(encryptedByteArray, "UTF-16");
System.out.println("decoded:" + decoded);
byte[] encoded = decoded.getBytes("UTF-16");
System.out.println("encoded:" + encoded);
String decryptedText = encrypter.decrypt(encoded); //Exception here
System.out.println("decryptedText:" + decryptedText);
assertEquals(toEncrypt, decryptedText);
}
}
It is not a good idea to store encrypted data in Strings because they are for human-readable text, not for arbitrary binary data. For binary data it's best to use byte[].
However, if you must do it you should use an encoding that has a 1-to-1 mapping between bytes and characters, that is, where every byte sequence can be mapped to a unique sequence of characters, and back. One such encoding is ISO-8859-1, that is:
String decoded = new String(encryptedByteArray, "ISO-8859-1");
System.out.println("decoded:" + decoded);
byte[] encoded = decoded.getBytes("ISO-8859-1");
System.out.println("encoded:" + java.util.Arrays.toString(encoded));
String decryptedText = encrypter.decrypt(encoded);
Other common encodings that don't lose data are hexadecimal and base64, but sadly you need a helper library for them. The standard API doesn't define classes for them.
With UTF-16 the program would fail for two reasons:
String.getBytes("UTF-16") adds a byte-order-marker character to the output to identify the order of the bytes. You should use UTF-16LE or UTF-16BE for this to not happen.
Not all sequences of bytes can be mapped to characters in UTF-16. First, text encoded in UTF-16 must have an even number of bytes. Second, UTF-16 has a mechanism for encoding unicode characters beyond U+FFFF. This means that e.g. there are sequences of 4 bytes that map to only one unicode character. For this to be possible the first 2 bytes of the 4 don't encode any character in UTF-16.
Accepted solution will not work if your String has some non-typical charcaters such as š, ž, ć, Ō, ō, Ū, etc.
Following code worked nicely for me.
byte[] myBytes = Something.getMyBytes();
String encodedString = Base64.encodeToString(bytes, Base64.NO_WRAP);
byte[] decodedBytes = Base64.decode(encodedString, Base64.NO_WRAP);
Now, I found another solution too...
public class NewEncrypterTest
{
#Test
public void canEncryptAndDecrypt() throws Exception
{
String toEncrypt = "FOOBAR";
NewEncrypter encrypter = new NewEncrypter();
byte[] encryptedByteArray = encrypter.encrypt(toEncrypt);
String encoded = String.valueOf(Hex.encodeHex(encryptedByteArray));
byte[] byteArrayToDecrypt = Hex.decodeHex(encoded.toCharArray());
String decryptedText = encrypter.decrypt(byteArrayToDecrypt);
System.out.println("decryptedText:" + decryptedText);
assertEquals(toEncrypt, decryptedText);
}
}
Your problem is that you cannot build a UTF-16 (or any other encoding) String from an arbitrary byte array (see UTF-16 on Wikipedia). It is up to you, however, to serialize and deserialize the encrypted byte array without any loss, in order to, say, persist it, and make use of it later. Here's the modified client code that should give you some insight of what's actually happening with the byte arrays:
public static void main(String[] args) throws Exception {
String toEncrypt = "FOOBAR";
NewEncrypter encrypter = new NewEncrypter();
byte[] encryptedByteArray = encrypter.encrypt(toEncrypt);
System.out.println("encryptedByteArray:" + Arrays.toString(encryptedByteArray));
String decoded = new String(encryptedByteArray, "UTF-16");
System.out.println("decoded:" + decoded);
byte[] encoded = decoded.getBytes("UTF-16");
System.out.println("encoded:" + Arrays.toString(encoded));
String decryptedText = encrypter.decrypt(encryptedByteArray); // NOT the "encoded" value!
System.out.println("decryptedText:" + decryptedText);
}
This is the output:
encryptedByteArray:[90, -40, -39, -56, -90, 51, 96, 95, -65, -54, -61, 51, 6, 15, -114, 88]
decoded:<some garbage>
encoded:[-2, -1, 90, -40, -1, -3, 96, 95, -65, -54, -61, 51, 6, 15, -114, 88]
decryptedText:FOOBAR
The decryptedText is correct, when restored from the original encryptedByteArray. Please note that the encoded value is not the same as encryptedByteArray, due to the data loss during the byte[] -> String("UTF-16")->byte[] conversion.
I am currently facing an error called Bad Base64Coder input character at ...
Here is my code in java.
String nonce2 = strNONCE;
byte[] nonceBytes1 = Base64Coder.decode(nonce2);
System.out.println("nonceByte1 value : " + nonceBytes1);
The problem now is I get Bad Base64Coder input character error and the nonceBytes1 value is printed as null. I am trying to decode the nonce2 from Base64Coder. My strNONCE value is 16
/** Generating nonce value */
public static String generateNonce() {
try {
byte[] nonce = new byte[16];
Random rand;
rand = SecureRandom.getInstance ("SHA1PRNG");
rand.nextBytes(nonce);
//convert byte array to string.
strNONCE = new String(nonce);
}catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return strNONCE;
}
//convert byte array to string.
strNONCE = new String(nonce);
That is not going to work. You need to base64 encode it.
strNONCE = Base64Coder.encode(nonce);
It simply look like you're confusing some independent concepts and are pretty new to Java as well. Base64 is a type of encoding which converts "human unreadable" byte arrays into "human readable" strings (encoding) and the other way round (decoding). It is usually used to transfer or store binary data as characters there where it is strictly been required (due to the protocol or the storage type).
The SecureRandom thing is not an encoder or decoder. It returns a random value which is in no way to be corelated with a certain cipher or encoder. Here are some extracts from the before given links:
ran·dom
adj.
1. Having no specific pattern, purpose, or objective
Cipher
In cryptography, a cipher (or cypher)
is an algorithm for performing
encryption or decryption — a series
of well-defined steps that can be
followed as a procedure.
Encoding
Encoding is the process of
transforming information from one
format into another. The opposite
operation is called decoding.
I'd strongly recommend you to align those concepts out for yourself (click the links to learn more about them) and not to throw them in one big and same hole. Here's at least an SSCCE which shows how you can properly encode/decode a (random) byte array using base64 (and how to show arrays as string (a human readable format)):
package com.stackoverflow.q2535542;
import java.security.SecureRandom;
import java.util.Arrays;
import org.apache.commons.codec.binary.Base64;
public class Test {
public static void main(String[] args) throws Exception {
// Generate random bytes and show them.
byte[] bytes = new byte[16];
SecureRandom.getInstance("SHA1PRNG").nextBytes(bytes);
System.out.println(Arrays.toString(bytes));
// Base64-encode bytes and show them.
String base64String = Base64.encodeBase64String(bytes);
System.out.println(base64String);
// Base64-decode string and show bytes.
byte[] decoded = Base64.decodeBase64(base64String);
System.out.println(Arrays.toString(decoded));
}
}
(using Commons Codec Base64 by the way)
Here's an example of the output:
[14, 52, -34, -74, -6, 72, -127, 62, -37, 45, 55, -38, -72, -3, 123, 23]
DjTetvpIgT7bLTfauP17Fw==
[14, 52, -34, -74, -6, 72, -127, 62, -37, 45, 55, -38, -72, -3, 123, 23]
A base64 encoded string would only have printable characters in it. You're generating strNONCE directly from random bytes, so it will have non-printable characters in it.
What exactly is it you're trying to do?