Following code was working fine but on a Samsung SM-G950F, Android OS 8.0.0, it crashes:
private String encrypt(String value) {
try {
final byte[] bytes = value != null ? value.getBytes(UTF8) : new byte[0];
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID).getBytes(UTF8), 20));
return new String(Base64.encode(pbeCipher.doFinal(bytes), Base64.NO_WRAP), UTF8);
} catch (Exception e) {
return value;
}
}
private String decrypt(String value) {
try {
final byte[] bytes = value != null ? Base64.decode(value, Base64.DEFAULT) : new byte[0];
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID).getBytes(UTF8), 20));
return new String(pbeCipher.doFinal(bytes), UTF8);
} catch (Exception e) {
return value;
}
}
here is the full error message:
Caused by java.lang.RuntimeException: javax.crypto.BadPaddingException: pad block corrupted
at com.edaf.utils.ObscuredSharedPreferences.decrypt(SourceFile:185)
at com.edaf.utils.ObscuredSharedPreferences.getString(SourceFile:134)
at com.edaf.utils.Utils.getFromGlobals(SourceFile:145)
I changed return value on crash but I am sure it wont help
That piece of code was designed to fail if encryption was performed on one device and decryption is performed on a different device. If Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID) returns a different string then the salt parameter of the PBEParameterSpec constructor will be different.
Once the salt is different, then a different key will be calculated from the password, and your decryption is likely to fail with a padding related exception. The salt is used to create a different value even if passwords are identical (using only the ID is rather stupid though, because using the same ID will result in identical passwords, even in different applications).
If the decryption doesn't fail then you'll get a garbage plaintext by the way.
There are endless issues within the code of the question, for instance using single DES and a 20 iteration count and non-authenticated encryption. I could post a page long review, but instead I urge you to read a book on modern crypto, and revise your protocol ASAP.
Related
I have done Encryption with ,
public static String encrypt(String plainText) {
try {
byte[] keyData = secret_key.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(keyData, "AES/ECB/PKCS7Padding");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] cipherText = cipher.doFinal(plainText.getBytes("UTF-8"));
String encryptedString = Base64.encodeToString(cipherText, Base64.NO_WRAP);
return encryptedString;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
it's working well.
but part of Decryption gives Error like,
W/System.err: javax.crypto.BadPaddingException: pad block corrupted
W/System.err: at com.android.org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(JCEBlockCipher.java:701)
W/System.err: at javax.crypto.Cipher.doFinal(Cipher.java:1111)
decrypt Code like,
public static String decrypt(String encryptedText) {
try {
byte[] keyData = secret_key.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(keyData, "AES/ECB/PKCS7Padding");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] cipherText = Base64.decode(encryptedText,Base64.NO_WRAP);
String decryptedString = new String(cipher.doFinal(cipherText),"UTF-8");
return decryptedString;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
here what is the problem? How can i solve this Issue?
It is likely that your secret_key value contains bytes which are not well represented in the ambiguous encoding you're using. When you call String#getBytes() without specifying an encoding, you get the system default, which can vary.
You should use hexadecimal encoding whenever you represent your key as a String. This will be consistent across serialization/deserialization on every platform. There are many standard implementations of this encoding/decoding process available (i.e. org.bouncycastle.util.encoders.Hex.decode("0123456789ABCDEFFEDCBA9876543210"); or org.apache.commons.codec.binary.Hex.decodeHex("0123456789ABCDEFFEDCBA9876543210".toCharArray()); which both return the raw byte[]).
Some side notes:
You are using ECB mode of operation, which is extremely susceptible to frequency analysis for cryptanalysis and is effectively deprecated aside from toy crypto demonstrations. I suggest you use CBC, CTR, or GCM.
You do not provide an initialization vector (IV), so the same message encrypted with the same key will always yield identical cipher text. Use a unique and non-predictable IV for every encryption operation by generating 16 bytes from SecureRandom and populating it into an IvParameterSpec. You can prepend the IV bytes to the cipher text and transport/store it in the clear.
Your cipher text is not authenticated, allowing for malicious users to both manipulate encrypted data and to attempt decryption via padding oracle/CCA attacks. Use an authenticated encryption with associated data (AEAD) mode like GCM, or use an HMAC/SHA-256 message authentication code (MAC) over the cipher text, and verify it using a constant-time equals method before attempting any decryption.
You do not need to provide the mode of operation or padding scheme when instantiating a key. SecretKey key = new SecretKeySpec(keyData, "AES"); is sufficient.
3des with 2 different keys in java getting null.
import java.security.spec.*;
import javax.crypto.*;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class DESedeEncryption {
public static void main(String[] args) {
SecretKey k1 = generateDESkey();
SecretKey k2 = generateDESkey();
String firstEncryption = desEncryption("plaintext", k1);
System.out.println("firstEncryption Value : "+firstEncryption);
String decryption = desDecryption(firstEncryption, k2);
System.out.println("decryption Value : "+decryption);
String secondEncryption = desEncryption(decryption, k1);
System.out.println("secondEncryption Value : "+secondEncryption);
}
public static SecretKey generateDESkey() {
KeyGenerator keyGen = null;
try {
keyGen = KeyGenerator.getInstance("DESede");
} catch (Exception ex) {
}
keyGen.init(112); // key length 56
SecretKey secretKey = keyGen.generateKey();
return secretKey;
}
public static String desEncryption(String strToEncrypt, SecretKey desKey) {
try {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, desKey);
BASE64Encoder base64encoder = new BASE64Encoder();
byte[] encryptedText = cipher.doFinal(strToEncrypt.getBytes());
String encryptedString =base64encoder.encode(encryptedText);
return encryptedString;
} catch (Exception ex) {
}
return null;
}
public static String desDecryption(String strToDecrypt, SecretKey desKey) {
try {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, desKey);
BASE64Decoder base64decoder = new BASE64Decoder();
byte[] encryptedText = base64decoder.decodeBuffer(strToDecrypt);
byte[] plainText = cipher.doFinal(encryptedText);
String decryptedString= bytes2String(plainText);
return decryptedString;
} catch (Exception ex) {
}
return null;
}
private static String bytes2String(byte[] bytes) {
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i <bytes.length; i++) {
stringBuffer.append((char) bytes[i]);
}
return stringBuffer.toString();
}
}
while i'm running the above code i'm getting null values. plz help.
output:
firstEncryption Value : jAihaGgiOzBSFwBWo3gpbw==
decryption Value : null
secondEncryption Value : null
getting error:
firstEncryption Value : ygGPwCllarWvSH8td55j/w==
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
at com.sun.crypto.provider.DESedeCipher.engineDoFinal(DESedeCipher.java:
294)
at javax.crypto.Cipher.doFinal(Cipher.java:2087)
at DESedeEncryption.desDecryption(DESedeEncryption.java:145)
at DESedeEncryption.main(DESedeEncryption.java:107)
decryption Value : null
java.lang.NullPointerException
at DESedeEncryption.desEncryption(DESedeEncryption.java:130)
at DESedeEncryption.main(DESedeEncryption.java:109)
secondEncryption Value : null
Symmetric ciphers work by encrypting and decrypting with the same key, hence the name symmetric. (And for most modes also the same IV, but the IV doesn't need to be secret.) You're encrypting with one key and decrypting with an independent key which is different with overwhelming probability (i.e. it might the same once a zillion quillion eternities). That won't work.
Perhaps you are confused by the description of Triple-DES also known as 3DES DESede or TDEA. The original DES (or DEA) cipher uses a 56-bit key (in 8 bytes) which was secure in the 1960s but not now. Triple-DES was defined using DES as a building block but with a bundle of 3 keys (k1,k2,k3) which can also be treated as a combined 168-bit key (in 24 bytes); if k3=k1 the key is described as 112-bits although it is still stored as 24 bytes. Your call to KeyGenerator "DESede" .init(112) does exactly that; it generates a 24-byte bundle with k3=k1 and k2 different. For convenience in the past Triple-DES is defined to use single-DES to encrypt with k1, decrypt with k2, and encrypt with k3, and the reverse when decrypting, hence the name DES-EDE or DESede. See http://en.wikipedia.org/wiki/Triple_DES .
If you really want, you can implement Triple-DES yourself in Java using Cipher "DES" by doing E,D,E (or reverse D,E,D if used) and then wrapping the mode around that, see Java Triple DES encryption with 2 different keys . But it's much easier to just use Cipher "DESede", which it does the lot for you, treating DESede like any other symmetric cipher primitive, as answered in that question.
Also, mode ECB is dangerous. It is an exaggeration to say it is always insecure as some people do, but historically very many applications using it designed by non-experts are insecure. Unless you know much more than is evident in your question, or are following (or interfacing to) a design by someone who does, use a better established mode like CBC or CTR.
I'm implementing 3DES EDE3 with 3 different keys each of 56 bits, EK3 DK2 Ek1 plain text, 168 bits. But i don't know how to do it. i'm bigger to java.
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class DESedeEncryption {
public static void main(String[] args) {
SecretKey k1 = generateDESkey();
SecretKey k2 = generateDESkey();
SecretKey k3 = generateDESkey();
String firstEncryption = desEncryption("plaintext", k1);
String decryption = desDecryption(firstEncryption, k2);
String secondEncryption = desEncryption(decryption, k3);
System.out.println("secondEncryption: "+secondEncryption);
System.out.println("firstEncryption: "+firstEncryption);
System.out.println("decryption: "+decryption);
}
public static SecretKey generateDESkey() {
KeyGenerator keyGen = null;
try {
keyGen = KeyGenerator.getInstance("DESede");
} catch (Exception ex) {
ex.printStackTrace();
}
keyGen.init(168); // key length 112 for two keys, 168 for three keys
SecretKey secretKey = keyGen.generateKey();
return secretKey;
}
public static String desEncryption(String strToEncrypt, SecretKey desKey) {
try {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, desKey);
String encryptedString = new BASE64Encoder().encode(cipher.doFinal(strToEncrypt.getBytes()));
return encryptedString;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static String desDecryption(String strToDecrypt, SecretKey desKey) {
try {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, desKey);
String decryptedString = new String(cipher.doFinal(new BASE64Decoder().decodeBuffer(strToDecrypt)));
return decryptedString;
}
catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
}
I'm getting following error. tell me what wrong in this. plz help us.
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
at com.sun.crypto.provider.DESedeCipher.engineDoFinal(DESedeCipher.java
:294)
at javax.crypto.Cipher.doFinal(Cipher.java:2087)
at DESedeEncryption.desDecryption(DESedeEncryption.java:55)
at DESedeEncryption.main(DESedeEncryption.java:17)
java.lang.NullPointerException
at DESedeEncryption.desEncryption(DESedeEncryption.java:41)
at DESedeEncryption.main(DESedeEncryption.java:18)
secondEncryption: null
firstEncryption: sP0ySxvxaUP+0a9l5qWSLQ==
decryption: null
Thanks in advance
Your code of your main() method doesn't make any sense. You're trying to decrypt with k2 a message which has been encrypted with k1. That can't work.
You don't seem to understand what DESede does. You don't need to encrypt then decrypt then encrypt with 3 different keys. You simply provide a key that has 3 times the length of a simple DES key, and the DESede cipher splits the key in 3 parts and does the three steps for you. So, as with any other Cipher, to check that it works fine, you should simply do:
SecretKey key = generateKey();
byte[] clearText = ...;
byte[] encrypted = encrypt(clearText, key);
byte[] decrypted = decrypt(encrypted, key);
// decrypted should contain the same bytes as clearText
You seem to misunderstand 3DES. The internal algorithm performs the encrypt/decrypt/encrypt operations already, so you don't need to perform that yourself. Just pass a key that's 168 bits long (3 × 56-bit) into your cipher and it'll do the job.
Additionally, the conversion to Base64 is failing. When you're running this:
String encryptedString = new BASE64Encoder().encode(cipher.doFinal(strToEncrypt.getBytes()));
You're actually calling toString() on the result of doFinal() before it's passed to encode(), causing it to return the Java reference rather than the actual contents of the array. Someone else made exactly the same mistake in an earlier question.
That said, you're using ECB for your block cipher mode. ECB (or Electronic Codebook) mode works by running each cryptographic transform on every block independently, resulting in the same ciphertext block being produced for identical ciphertext blocks when the same key is used. Wikipedia's entry on this has a great visual representation of how this kind of cryptographic process is broken.
I suggest using Cipher Block Chaining (CBC) mode instead, which combines each plaintext block with the previous ciphertext block. This ensures that equal blocks of plaintext don't produce the same blocks of ciphertext. The additional requirement here is an Initialisation Vector (IV), which is a unique random value used as the "previous block" for the first plaintext block (which obviously can't have a previous ciphertext block - it's the first one). The IV doesn't have to be secret, so you just generate it, use it, and prepend it to the encrypted message. On decryption you strip that value off, then use it to decrypt along with the key.
In this code, this line is causing an exception:
clearText = c.doFinal(Base64.decode(encryptedText, Base64.DEFAULT));
javax.crypto.BadPaddingException: pad block corrupted
I got the code from:
http://www.techrepublic.com/blog/software-engineer/attention-android-developers-keep-user-data-safe/
Any ideas?
private String decrypt (String encryptedText) {
byte[] clearText = null;
try {
SecretKeySpec ks = new SecretKeySpec(getKey(), "AES");
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.DECRYPT_MODE, ks);
clearText = c.doFinal(Base64.decode(encryptedText, Base64.DEFAULT));
return new String(clearText, "UTF-8");
} catch (Exception e) {
return null;
}
}
Details: I am encrypting it on the android as well
owlstead's advice was helpful, but for this case when using the code in
Attention Android developers: Keep user data safe
http://www.techrepublic.com/blog/software-engineer/attention-android-developers-keep-user-data-safe/
I made some changes to the code that might be helpful for other people in the future. I completely deleted the getkey method.
private static String seed;
/**
* Encrypts the text.
* #param clearText The text you want to encrypt
* #return Encrypted data if successful, or null if unsucessful
*/
protected String encrypt(String clearText) {
byte[] encryptedText = null;
try {
byte[] keyData = seed.getBytes();
SecretKey ks = new SecretKeySpec(keyData, "AES");
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, ks);
encryptedText = c.doFinal(clearText.getBytes("UTF-8"));
return Base64.encodeToString(encryptedText, Base64.DEFAULT);
} catch (Exception e) {
return null;
}
}
/**
* Decrypts the text
* #param encryptedText The text you want to encrypt
* #return Decrypted data if successful, or null if unsucessful
*/
protected String decrypt (String encryptedText) {
byte[] clearText = null;
try {
byte[] keyData = seed.getBytes();
SecretKey ks = new SecretKeySpec(keyData, "AES");
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.DECRYPT_MODE, ks);
clearText = c.doFinal(Base64.decode(encryptedText, Base64.DEFAULT));
return new String(clearText, "UTF-8");
} catch (Exception e) {
return null;
}
}
Java + Android + Encryption + Exception means just one thing normally, somebody is using the SecureRandom class again as a key derivation function. This fails when the SecureRandom implementation of "SHA1PRNG" does not behave as the one in Sun's implementation in Java SE. Especially if the seed is added to the state of the random number generator instead of the seed being used as a starting point of the PRNG.
Basically, simply use SecretKey aesKey = new SecretKeySpec(byte[] keyData, "AES") instead, or - if you start off with a password - try and generate the key using PBKDF2.
For me, the problem is in getKey()
Make sure that two invocation of getKey() return the same value.
I used new SecureRandom(password.getBytes()) to generate key. It worked on Windows, but on Android, it returned different value for different call.
I Reffred From this : https://androidfreetutorial.wordpress.com/2017/03/14/android-encryptiondecryption-with-aes-algorithm/
Change to "AES" From "AES/ECB/PKCS7Padding";
I have previously used a RSACryptoServiceProvider in C# to encrypt some data, and now I have to replicate this encryption in an Android program. I want my Android program to generate the same result as I got in my C# program.
Public Key:
<RSAKeyValue>
<Modulus>zz4qdc39y1BHyJgVXUkINJSbsUd1ZJPISyE9nNGjqgR+ZO1a4cE3ViVCSZCw+6dBdVMFNjzZPBxl0mT57GIq7rcuoT0scesZgxOftbMasPbxp0BGrh3HTpbBMJdCopgcYV98CZERakb8Pgbb0ne/DiW9Aq0kfTBE02/iEHRNuqMNfo1GFo55m0OKbxVoM6UBb8AITQ6lbdvfCgeIvMzRlVrHCwxUNrrX5cS6gurEfJ8Da+prKQmwWpFCkwDkPWje2W+bTSPUc9l6Ads0UimYE5sGs4Zsfz6Eocz4rJjR+qCiB8qt6HtdyjKo0auqYzyXIjdRv2950flc9tOh5bRlQQ==
</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
Java Encryption Program:
byte[] modulusBytes = Base64.decode(Modoutput.getBytes("UTF-8"),
Base64.DEFAULT);
byte[] exponentBytes = Base64.decode(Expoutput.getBytes("UTF-8"),
Base64.DEFAULT);
BigInteger e = new BigInteger(1, exponentBytes);
BigInteger m = new BigInteger(1, modulusBytes);
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(m, e);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKeyn = fact.generatePublic(keySpec);
Log.i("Publickey", pubKeyn.toString());
Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, pubKeyn);
byte[] encryptedByteData = cipher.doFinal(byteData);
String outputEncrypted = Base64.encodeToString(encryptedByteData,
Base64.NO_WRAP);
Log.i("Encrypteddata", outputEncrypted);
I tried the above code but it gives an entirely different output from C#. Can anyone tell me what is wrong with my code? Thanks in advance.
Edit: As requested, here is the C# code for which I am trying to replicate the encrypted output in Java:
public static string EncryptText(string text, int keySize,
string publicKeyXml) {
var encrypted = Encrypt(Encoding.UTF8.GetBytes(text), keySize,
publicKeyXml);
return Convert.ToBase64String(encrypted);
}
public static byte[] Encrypt(byte[] data, int keySize, string publicKeyXml) {
if (data == null || data.Length == 0)
throw new ArgumentException("Data are empty", "data");
int maxLength = GetMaxDataLength(keySize);
if (data.Length > maxLength)
throw new ArgumentException(String.Format(
"Maximum data length is {0}", maxLength), "data");
if (!IsKeySizeValid(keySize))
throw new ArgumentException("Key size is not valid", "keySize");
if (String.IsNullOrEmpty(publicKeyXml))
throw new ArgumentException("Key is null or empty", "publicKeyXml");
using (var provider = new RSACryptoServiceProvider(keySize)) {
provider.FromXmlString(publicKeyXml);
return provider.Encrypt(data, _optimalAsymmetricEncryptionPadding);
}
}
Encryption by definition tries to hide all information about the plain text. This includes information about identical plain text. To do this it uses some kind of random within the various padding modes (e.g. PKCS#1 v1.5 compatible padding or OAEP padding for RSA). So speaking from a cryptographic standpoint, the implementation is broken if you ever get an identical result.
The method to check if the ciphertext is correct is by decrypting it using the private key. If that results in the plaintext you started with then your implementation is correct.
[EDIT] Note that you are using OAEP encryption in the C# code, while Java uses the PKCS#1 v1.5 compatible scheme by default. You should use "RSA/None/OAEPWithSHA1AndMGF1Padding" or "RSA/ECB/OAEPWithSHA1AndMGF1Padding". If it is not available add the Bouncy Castle provider.