Android DES algorithm default attributes - java

I am using following code for encryption in Android project:
SecretKeyFactory kf = SecretKeyFactory.getInstance("DES");
String key = "abcdefg";
DESKeySpec keySpec = new DESKeySpec(key.getBytes());
SecretKey _key = kf.generateSecret(keySpec);
String xform = "DES";
Cipher cipher = Cipher.getInstance(xform);
byte[] IV = { 11, 22, 33, 44, 55, 66, 77, 88, 99, 18, 69, 17, 72, 94, 18, 30 };
IvParameterSpec ips = new IvParameterSpec(IV);
cipher.init(Cipher.ENCRYPT_MODE, _key, ips);
String plainText = "abcdeffdkflsdkf";
byte[] cipherText = cipher.doFinal(plainText.getBytes());
I write this encrypted data in file and I want to decrypt this file in my java project where I am using following code:
SecretKeyFactory kf = SecretKeyFactory.getInstance("DES");
String key = "abcdefg";
DESKeySpec keySpec = new DESKeySpec(key.getBytes());
SecretKey _key = kf.generateSecret(keySpec);
String xform = "DES";
Cipher cipher = Cipher.getInstance(xform);
byte[] IV = { 11, 22, 33, 44, 55, 66, 77, 88, 99, 18, 69, 17, 72, 94, 18, 30 };
IvParameterSpec ips = new IvParameterSpec(IV);
cipher.init(Cipher.DECRYPT_MODE, _key, ips);
String cipherText;
//cipher text is read from file.
byte[] plainText = cipher.doFinal(cipherText.getBytes());
But it's not working.
When we do not specify mode of operation(i.e. CBC/ECB) and padding method(i.e. PKCS5Padding/NoPadding) and only algorithm name is specified for getting instance of Cipher then what are the default values which Android and Java use?
Does Android uses CBC by default? As it doesn't give error for IV. If I specify IV in init method and don't specify mode in my java project, it throws exception as IV cannot be used for ECB.
Thanks.

Both Android and Java default to "AES/ECB/PKCS5Padding". Both your key and IV are not of the correct size (so the code should not even run!) and you forget to perform (character) encoding/decoding correctly. Especially the ciphertext should never be directly converted into a string, as unprintable characters are likely to be dropped or converted.

Related

How to add negative number in byte into buffer

I have a problem with encoding a password in java and nodejs.
After I encode the password, I get the same result with javacode and nodejs.
result = [69, 83, 53, 95, -77, -40, -124, 109, 38, -85, 91, -105, 72, -61, 84, 54].
Then I convert it into a buffer in nodejs.
When i use crypto. I have use a method:
const content = 'et'
key = buffer.from(result)
const iv= Buffer.alloc(0)
crypto.createCipheriv("aes-128-ecb", key, iv)
let encrypted = cipher.update(content, "utf8", "base64");
encrypted += cipher.final("base64"); //result: Rrx7BNH5l/miPfFbGgAkMA==
I get a result that is different from the javacode
My java code
String content = 'et'
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] result = cipher.doFinal(content);
system.out.printLn(Base64.encode(encryptResult)) //result:TJ4Z8bGSoJKiRFHwE929mg==
I think This problem occurs when converting the bytes into a buffer. So if array don't have negative number I'll get the correct result.
Please help me any solution

java.security.InvalidKeyException: Wrong key size DESede encryption

I am encrypting string using java "DESede" with 16 bit. I copied code from application which was working fine. But when i tried it is not working. I don't want to change key and encryption
Key in byte[] key = {-104, 26, -6, -116, -34, -72, -88, -9, -27, -96, 81, -76, 9, -69, 68, -49}
Key secretKeySpec = new SecretKeySpec(newChar, "DESede");
Cipher instance = Cipher.getInstance("DESede");
instance.init(Cipher.ENCRYPT_MODE, secretKeySpec);
Error:
java.security.InvalidKeyException: Wrong key size
at com.sun.crypto.provider.DESedeCrypt.init(DESedeCrypt.java:69)
at com.sun.crypto.provider.ElectronicCodeBook.init(ElectronicCodeBook.java:94)
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:591)
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:467)
at com.sun.crypto.provider.DESedeCipher.engineInit(DESedeCipher.java:166)
at javax.crypto.Cipher.implInit(Cipher.java:801)
at javax.crypto.Cipher.chooseProvider(Cipher.java:863)
at javax.crypto.Cipher.init(Cipher.java:1248)
at javax.crypto.Cipher.init(Cipher.java:1185)

Same password results in different AES keys in Java

I am attempting to implement a basic example of AES encryption across two devices in Java. However using the same password(128 bit) on both devices to generate an AES key results in different keys on both devices, each time we run the application. So we are unable to decrypt the text that we send between devices.
The methods I'm using are below, they are slightly modified versions of code I've found elsewhere:
public String encryptMessage(String message, String password) throws Exception {
// Creating key and cipher
SecretKeySpec aesKey = new SecretKeySpec(password.getBytes("UTF-8"), "AES");
byte[] iv = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
IvParameterSpec ivspec = new IvParameterSpec(iv);
//AES cipher
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
// encrypt the text
cipher.init(Cipher.ENCRYPT_MODE, aesKey, ivspec);
byte[] encrypted;
encrypted = cipher.doFinal(message.getBytes());
return new String(encrypted, "UTF-8");
}
public String decryptMessage(String encryptedMessage, String password) throws Exception {
// Creating key and cipher
byte[] passwordBytes = password.getBytes("UTF-8");
SecretKeySpec aesKey = new SecretKeySpec(passwordBytes, "AES");
byte[] iv = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
IvParameterSpec ivspec = new IvParameterSpec(iv);
//AES cipher
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
// decrypting the text
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivspec);
String decrypted = new String(cipher.doFinal(encryptedMessage.getBytes(Charset.forName("UTF-8"))));
//returning decrypted text
return decrypted;
}
Every time I run this code and print out aesKey, it is different.
My understanding of AES and symmetric encryption is that given the same password it should generate the same key, otherwise how is it able to decrypt the artefact?
Have I got the wrong end of the stick wrt AES or can someone suggest what might be going on?
Your understanding is correct and the key in your code is the same.
You can't "print" the aesKey since SecretKeySpec has no toString() method. So the built-in Object.toString() will be called, which just prints the address of the object in memory
javax.crypto.spec.SecretKeySpec#14c7f // <--- useless info //
There are just a few issues with your code:
Don't convert encrypted bytes to a UTF-8 string. There can be combinations which are invalid in UTF-8, as well as 00 bytes. Use Base64 or Hex encoding for printing encrypted data.
You shouldn't use ASCII bytes as a key, that greatly reduces security of the key. Derive a key from the password, at least with SHA-256, but preferably PBKDF2 or scrypt.
Use a high entropy random IV and store it with the ciphertext.
Here's an updated version demonstrating that it's working:
public static String encryptMessageGH(String message, String password) throws Exception {
MessageDigest sha = MessageDigest.getInstance("SHA-256");
byte[] key = sha.digest(password.getBytes("UTF-8"));
SecretKeySpec aesKey = new SecretKeySpec(key, "AES");
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, aesKey, new IvParameterSpec(iv));
byte[] ciphertext = cipher.doFinal(message.getBytes());
byte[] encrypted = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, encrypted, 0, iv.length);
System.arraycopy(ciphertext, 0, encrypted, iv.length, ciphertext.length);
return Base64.getEncoder().encodeToString(encrypted);
}
public static String decryptMessageGH(String encryptedMessage, String password) throws Exception {
MessageDigest sha = MessageDigest.getInstance("SHA-256");
byte[] key = sha.digest(password.getBytes("UTF-8"));
SecretKeySpec aesKey = new SecretKeySpec(key, "AES");
byte[] encrypted = Base64.getDecoder().decode(encryptedMessage);
byte[] iv = new byte[16];
System.arraycopy(encrypted, 0, iv, 0, iv.length);
byte[] ciphertext = new byte[encrypted.length - iv.length];
System.arraycopy(encrypted, iv.length, ciphertext, 0, ciphertext.length);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, aesKey, new IvParameterSpec(iv));
return new String(cipher.doFinal(ciphertext), "UTF-8");
}
public static void main(String[] args) throws Exception {
String orig = "Test message";
String enc = encryptMessageGH(orig, "abcdef123");
System.out.println("Encrypted: " + enc);
String dec = decryptMessageGH(enc, "abcdef123");
System.out.println("Decrypted: " + dec);
}
Output:
Encrypted: lcqcd9UZpjLSY9SsQ/N7kV/cpdzL3c7HQcCSiIs6p/k=
Decrypted: Test message

How to pass AES String crypted through HTTP header using javax.crypto

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.

Java AES decryption error, encryption is working fine

I have a question about using Decrypt in AES. I wrote the same program that encrypts the text.
Here is my Decrypt class. (I use a 16 byte key).
public static byte[] decryptAES(String message) throws Exception
{
String secretKey = "JohnIsAwesome!1!";
SecretKeySpec key = new SecretKeySpec(secretKey.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(message.getBytes());
}
Here is my main. The encrypt is working perfectly.
public static void main (String[] args) throws Exception
{
String text = "MySuperSecretPassword!";
//Ecrypt the Text, then print it out in an array
String encryptText = Arrays.toString(encryptAES(text));
System.out.println("Encrypted Message"+ encryptText);
//Decrypt the Text, then print it out in an array
String decryptText = Arrays.toString(decryptAES(text1));
System.out.println("Decrypted Message"+ decryptText);
}
Encrypt output:
Encrypted Message[16, 69, 84, 118, 68, -36, -67, 125, -86, -106, -4, 24, -59, -77, -41, -32, -37, 104, -44, -42, 112, 87, 87, 101, 28, 99, 60, -27, 34, -88, -17, -114]
If anyone has any ideas why the decryption would not work, It would be greatly appreciated. I've been banging my head against the wall on this one.
Thank you
Sorry, forgot to add my Encrypt class here as well.
public static byte[] encryptAES(String message) throws Exception
{
String secretKey = "JohnIsAwesome!1!";
SecretKeySpec key = new SecretKeySpec(secretKey.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(message.getBytes());
}
Arrays.toString(byte[] a) "Returns a string representation of the contents of the specified array." It does not convert a byte array to a String. Instead try using:
new String(decryptAES(text1), "UTF-8");

Categories