a i need help to decrypt values generated in Android Java App on Flutter.
I need a class who this but in Dart/Flutter:
public class TrippleDes {
// public static String ALGO = "DESede/CBC/PKCS7Padding";
public static String ALGO = "DESede/ECB/PKCS7Padding";
public static String _encrypt(String message, String secretKey) throws Exception {
Cipher cipher = Cipher.getInstance(ALGO);
cipher.init(Cipher.ENCRYPT_MODE, getSecreteKey(secretKey));
byte[] plainTextBytes = message.getBytes("UTF-8");
byte[] buf = cipher.doFinal(plainTextBytes);
byte[] base64Bytes = Base64.encode(buf, Base64.DEFAULT);
String base64EncryptedString = new String(base64Bytes);
return base64EncryptedString;
}
public static String _decrypt(String encryptedText, String secretKey) throws Exception {
byte[] message = Base64.decode(encryptedText.getBytes(), Base64.DEFAULT);
Cipher decipher = Cipher.getInstance(ALGO);
decipher.init(Cipher.DECRYPT_MODE, getSecreteKey(secretKey));
byte[] plainText = decipher.doFinal(message);
return new String(plainText, "UTF-8");
}
public static SecretKey getSecreteKey(String secretKey) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] digestOfPassword = md.digest(secretKey.getBytes("utf-8"));
byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
SecretKey key = new SecretKeySpec(keyBytes, "DESede");
return key;
}
}
I have to decrypt some data that are sent encrypted using AES256 Rijndael.
I have the encryption/decryption mechanism used by my partner, developed in JAVA, that I'm not very familiar with, but can't transpose it in C#.
The secret key that has been given to me is 10 chars long.
I think that below code is ok, excepted the IV calculation.
You'll find first the java code, and then the C# :
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class UtilsCrypto {
/* Rijndael/CFB8/NoPadding is default cipher */
final static String CHIPHER = "Rijndael/CFB8/NoPadding";
public static final String MESSAGE_DIGEST_ALGORITHM = "MD5";
public static final String AES = "AES";
public static final String AES_ECB_NO_PADDING = "AES/ECB/NoPadding";
private static byte[] md5(final String input) throws NoSuchAlgorithmException {
final MessageDigest md = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM);
return md.digest(input.getBytes());
}
private Cipher initCipher(final int mode, final String secretKey) throws Exception {
final byte[] key = md5(secretKey);
final byte[] iv = md5(secretKey);
final SecretKeySpec skeySpec = new SecretKeySpec(key, AES);
/* This valid with other ciphers than Rijndael/CFB8/NoPadding */
// final IvParameterSpec initialVector = new IvParameterSpec(iv);
/* Use this with Rijndael/CFB8/NoPadding */
final IvParameterSpec initialVector = new IvParameterSpec(getIvBytes(iv));
final Cipher cipher = Cipher.getInstance(CHIPHER);
cipher.init(mode, skeySpec, initialVector);
return cipher;
}
public String encrypt(final String dataToEncrypt, final String secretKey) {
if (Utils.isEmpty(secretKey))
return dataToEncrypt;
String encryptedData = null;
try {
final Cipher cipher = initCipher(Cipher.ENCRYPT_MODE, secretKey);
final byte[] encryptedByteArray = cipher.doFinal(dataToEncrypt.getBytes(Charset.forName("UTF8")));
final BASE64Encoder enc = new BASE64Encoder();
encryptedData = enc.encode(encryptedByteArray);
encryptedData = encryptedData.replace("+", "-");
encryptedData = encryptedData.replace("/", "_");
} catch (Exception e) {
System.err.println("Problem encrypting the data");
e.printStackTrace();
}
return encryptedData;
}
public String decrypt(final String encryptedData, final String secretKey) {
String decryptedData = null;
String inData = encryptedData;
try {
final Cipher cipher = initCipher(Cipher.DECRYPT_MODE, secretKey);
final BASE64Decoder dec = new BASE64Decoder();
inData = inData.replace("-", "+");
inData = inData.replace("_", "/");
final byte[] encryptedByteArray = dec.decodeBuffer(inData); // ok
final byte[] decryptedByteArray = cipher.doFinal(encryptedByteArray);
decryptedData = new String(decryptedByteArray, "UTF8");
} catch (Exception e) {
System.err.println("Problem decrypting the data");
e.printStackTrace();
}
return decryptedData;
}
/**
* This method is only for Rijndael/CFB8/NoPadding
*
* #param hashedKey
* md5
* #return byte array
* #throws Exception
* If any exceptions.
*/
// on passe en arg le hash de la clé
protected byte[] getIvBytes(byte[] hashedKey) throws Exception {
byte[] inputBytes = new byte[16]; // init son tableau a 16 bytes
final SecretKey key = new SecretKeySpec(hashedKey, AES); // secretKey
final Cipher cipher = Cipher.getInstance(AES_ECB_NO_PADDING);
cipher.init(Cipher.ENCRYPT_MODE, key); // chiffre sa clé en AES avec un IV
return cipher.doFinal(inputBytes);
}
}
now this is what I tried so far :
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Xml;
using System.Net;
using System.Web;
using System.Web.Services;
using Newtonsoft.Json;
using System.Security.Cryptography;
using System.Text;
namespace test
{
public byte[] getIVBytes(byte[] hashedKey)
{
byte[] inputBytes = new byte[16];
AesManaged tdes = new AesManaged();
tdes.Key = hashedKey;
tdes.Mode = CipherMode.ECB;
tdes.BlockSize = 128;
tdes.Padding = PaddingMode.None;
ICryptoTransform crypt = tdes.CreateEncryptor();
byte[] bla = crypt.TransformFinalBlock(hashedKey, 0, inputBytes.Length);
return bla;
}
[WebMethod]
public string decrypt(String input, String key)
{
byte[] md5KeyHash;
using (MD5 md5 = MD5.Create())
{
md5KeyHash = md5.ComputeHash(Encoding.UTF8.GetBytes(key));
}
input = input.Replace("-", "+");
input = input.Replace("_", "/");
input = input.Replace(" ", "");
byte[] data = Convert.FromBase64String(input); // récupérer l'array de bytes du message chiffré encodé en b64
String decrypted;
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Mode = CipherMode.CFB;
rijAlg.BlockSize = 128;
rijAlg.Padding = PaddingMode.None;
rijAlg.Key = md5KeyHash;
rijAlg.IV = getIVBytes(md5KeyHash);
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, null);
using (MemoryStream msDecrypt = new MemoryStream(data))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
decrypted = srDecrypt.ReadToEnd();
}
}
}
}
return decrypted;
}
Code seems to be "correct" because no errors are thrown except this :
XML Error analysis : An Invalid character was found in text content.
At: http://localhost:55175/WebService1.asmx/decrypt
line 2, col44 :�Me����>m�H�ZԤ�af2ɾ`A�ٖ�H$�&/
What am I missing ?
There are some bugs in the C#-code:
In the getIVBytes-method, replace line
byte[] bla = crypt.TransformFinalBlock(hashedKey, 0, inputBytes.Length);
by
byte[] bla = crypt.TransformFinalBlock(inputBytes, 0, inputBytes.Length); // encrypt inputBytes
In the decrypt-method, add before the CreateDecryptor-call
rijAlg.FeedbackSize = 8; // Use CFB8
and replace line
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, null);
by
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV); // Consider the IV
Then the posted ciphertext can be decrypted with the C#-code into the posted plaintext.
I'm trying to replicate an encryption method based on another C# method that I found.
The C# Encryption method EncryptText(word, password) call to another method AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes) to encrypt plain text:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Security.Cryptography;
using System.IO;
using System.Text;
namespace Rextester
{
public class Program
{
public static void Main(string[] args)
{
var f = EncryptText("763059", "515t3ma5m15B4d35");//(word, password)
Console.WriteLine(f);
}
public static byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
public static string EncryptText(string input, string password)
{
byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(input);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);
string result = Convert.ToBase64String(bytesEncrypted);
return result;
}
}
}
Using word 763059 and password 515t3ma5m15B4d35, the output is the following:
3cHrXxxL1Djv0K2xW4HuCg==
UPDATE:
Now, I created a Java Class main where I'm trying to replicate previous code:
public class main {
final static String PASSWORD = "515t3ma5m15B4d35";
final static byte[] SALT = new byte[]{1, 2, 3, 4, 5, 6, 7, 8};
final static int KEY_SIZE = 256;
final static int BLOCK_SIZE = 128;
final static int ITERATIONS = 1000;
public static void main(String[] args) {
System.out.println(encryptText("763059", PASSWORD));
}
public static String encryptText(String word, String password) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(password.getBytes("UTF-8"));
password = new String(md.digest(), "UTF-8");
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password.toCharArray(), SALT, ITERATIONS, KEY_SIZE);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec skey = new SecretKeySpec(tmp.getEncoded(), "AES");
byte[] iv = new byte[BLOCK_SIZE / 8];
IvParameterSpec ivspec = new IvParameterSpec(iv);
Cipher ci = Cipher.getInstance("AES/CBC/PKCS5Padding");
ci.init(Cipher.ENCRYPT_MODE, skey, ivspec);
byte[] result = ci.doFinal(word.getBytes("UTF-8"));
return DatatypeConverter.printBase64Binary(result);
} catch (NoSuchAlgorithmException | UnsupportedEncodingException | IllegalBlockSizeException | BadPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException | InvalidKeySpecException ex) {
return null;
}
}
}
UPDATE:
I read about using 256 bits keys in Java, and I found that I need to add Java Cryptography Extensions to allow 256 keys (Because I'm working with JDK7).
Then I added the libreries to the project, also I change the line:
KeySpec spec = new PBEKeySpec(password.toCharArray(), SALT, ITERATIONS, KEY_SIZE);
With the Key Value:
final static int KEY_SIZE = 256;
Now the output is the following:
J1xbKOjIeXbQ9njH+67RNw==
I still can't achieve my goal. Any Suggestion?
Finally I decided to use the BouncyCastle API to use the functionality of RijndaelEngine, as well as to generate the 256-bit key with PKCS5S2ParametersGenerator.
I created the RijndaelEncryption class to be able to perform the encryption as in the C# code:
public class RijndaelEncryption {
public String encryptString(String word, String password, byte[] salt, int iterations, int keySize, int blockSize) {
try {
byte[] pswd = sha256String(password, "UTF-8");
PKCS5S2ParametersGenerator key = keyGeneration(pswd, salt, iterations);
ParametersWithIV iv = generateIV(key, keySize, blockSize);
BufferedBlockCipher cipher = getCipher(true, iv);
byte[] inputText = word.getBytes("UTF-8");
byte[] newData = new byte[cipher.getOutputSize(inputText.length)];
int l = cipher.processBytes(inputText, 0, inputText.length, newData, 0);
cipher.doFinal(newData, l);
return new String(Base64.encode(newData), "UTF-8");
} catch (UnsupportedEncodingException | IllegalStateException | DataLengthException | InvalidCipherTextException e) {
return null;
}
}
public BufferedBlockCipher getCipher(boolean encrypt, ParametersWithIV iv) {
RijndaelEngine rijndael = new RijndaelEngine();
BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(rijndael));
cipher.init(encrypt, iv);
return cipher;
}
public ParametersWithIV generateIV(PKCS5S2ParametersGenerator key, int keySize, int blockSize) {
try {
ParametersWithIV iv = null;
iv = ((ParametersWithIV) key.generateDerivedParameters(keySize, blockSize));
return iv;
} catch (Exception e) {
return null;
}
}
public PKCS5S2ParametersGenerator keyGeneration(byte[] password, byte[] salt, int iterations) {
try {
PKCS5S2ParametersGenerator key = new PKCS5S2ParametersGenerator();
key.init(password, salt, iterations);
return key;
} catch (Exception e) {
return null;
}
}
public byte[] sha256String(String password, Charset charset) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(password.getBytes(charset));
return md.digest();
} catch (NoSuchAlgorithmException ex) {
return null;
}
}
public byte[] sha256String(String password, String charset) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(password.getBytes(charset));
return md.digest();
} catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
return null;
}
}
}
And I tested in main method:
public static void main(String[] args) {
RijndaelEncryption s = new RijndaelEncryption();
byte[] salt = new byte[]{1, 2, 3, 4, 5, 6, 7, 8};
String encryptStr = s.encryptString("763059", "515t3ma5m15B4d35", salt, 1000, 256, 128);
System.out.println("Encryptation: " + encryptStr);
}
To get:
Encryptation: 3cHrXxxL1Djv0K2xW4HuCg==
I am not any C# expert, but there are a few things to be checked:
Reading the documentation about Rfc2898DeriveBytes I see the function is using SHA1 hash, so try you may try to use PBKDF2WithHmacSHA1
On both instances (Rfc2898DeriveBytes, PBEKeySpec) you should make sure you the key size is the same (256 bit), it is surely wrong in your Java code
You may try to encode and print the keys to really make sure they are the same.
I need to add Java Cryptography Extensions to allow 256 keys.
Depends on your JVM version. I believe Oracle JDK since v. 1.8u162 by default contains the Unlimited Strength JCE policy. If you take any current JRE version, you should be ok
Additional: you are using (static) zero array IV, which is not secure
I have an answer for the original question. For future reference without bouncycastle.
You had a few problems.
Key size needed to be 256 + 128 (blocksize as well)
C# and Java byte[] don't act the same because java bytes are always signed which messes with the encryption of the password.
Both of these pieces of code give as output:
xD4R/yvV2tHajUS9p4kqJg==
C# code:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace tryencryption
{
class Program
{
static void Main(string[] args)
{
var f = EncryptText("yme", "515t3ma5m15B4d35");//(word, password)
Console.WriteLine(f);
Console.ReadKey();
}
public static byte[] AES_Encrypt(byte[] bytesToBeEncrypted, string passwordString)
{
byte[] encryptedBytes = null;
byte[] salt = new byte[] { (byte)0x49, (byte)0x64, (byte)0x76, (byte)0x65, (byte)0x64, (byte)0x65, (byte)0x76, (byte)0x61, (byte)0x6e, (byte)0x20, (byte)0x4d, (byte)0x65, (byte)0x76 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordString, salt, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
public static string EncryptText(string input, string password)
{
byte[] bytesToBeEncrypted = Encoding.Unicode.GetBytes(input);
byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, password);
string result = Convert.ToBase64String(bytesEncrypted);
return result;
}
}
}
Java code (this was from an android project bcs that's my usecase but should work everywhere):
package com.example.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Base64;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String result = encrypt("yme", "515t3ma5m15B4d35");
}
private static String encrypt(String word, String password) {
byte[] salt = new byte[] { (byte)0x49, (byte)0x64, (byte)0x76, (byte)0x65, (byte)0x64, (byte)0x65, (byte)0x76, (byte)0x61, (byte)0x6e, (byte)0x20, (byte)0x4d, (byte)0x65, (byte)0x76};
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, 1000, 256 + 128);
Key secretKey = factory.generateSecret(pbeKeySpec);
byte[] test = secretKey.getEncoded();
byte[] key = new byte[32];
byte[] iv = new byte[16];
System.arraycopy(secretKey.getEncoded(), 0, key, 0, 32);
System.arraycopy(secretKey.getEncoded(), 32, iv, 0, 16);
SecretKeySpec secret = new SecretKeySpec(key, "AES");
AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret, ivSpec);
//Realise Im using UTF16 here! Maybe you need UTF8
byte[] plaintextintobytes =word.getBytes(StandardCharsets.UTF_16LE);
byte[] encrypted = cipher.doFinal(plaintextintobytes);
String encryptedInformation = Base64.encodeToString(encrypted, Base64.NO_WRAP);
return encryptedInformation;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return "";
}
}
I am using the service like this
String value = "test#example.com"
String encrypedValue = EncrypterService get().encrypt(value.getBytes())
String decryptedValue = EncrypterService get().decrypt(encrypedValue .getBytes())
public final class EncrypterService {
private static Key keySpec;
private static Cipher encryptCipher;
private static Cipher decryptCipher;
private static String passphrase = "IU1ZaypiTiVYc3AtPXMxNWNMYGUmVUF8YUAtUSMuKVI=";
private static final String KEY_ALGORIGHT = "HmacSHA256";
private static final String CIPHER_ALGORITHM = "AES";
private static final String MD5_ALGORITH = "MD5";
private static EncrypterService service;
private EncrypterService(){
}
private static synchronized void initialize() {
if (service == null) {
service = new EncrypterService();
service.init();
}
}
public static EncrypterService get() {
initialize();
return service;
}
public String encrypt (byte[] plaintext){
//returns byte array encrypted with key
try {
byte[] encode = encryptCipher.doFinal(plaintext);
return new String(encode);
}catch(Exception e){
throw new RuntimeException("Unable to decrypt data" + e);
}
}
public String decrypt (byte[] ciphertext) {
//returns byte array decrypted with key
try {
byte[] decode = decryptCipher.doFinal(ciphertext);
return new String(decode);
}catch(Exception e){
throw new RuntimeException("Unable to decrypt data" + e);
}
}
private static void init(){
try {
if (encryptCipher == null && decryptCipher == null) {
byte[] bytesOfMessage = Base64.decode(passphrase, Base64.NO_WRAP);
MessageDigest md = MessageDigest.getInstance(MD5_ALGORITH);
byte[] thedigest = md.digest(bytesOfMessage);
keySpec = new SecretKeySpec(thedigest, KEY_ALGORIGHT);
encryptCipher = Cipher.getInstance(CIPHER_ALGORITHM);
encryptCipher.init(Cipher.ENCRYPT_MODE, keySpec);
decryptCipher = Cipher.getInstance(CIPHER_ALGORITHM);
decryptCipher.init(Cipher.DECRYPT_MODE, keySpec);
}
}catch(Exception e){
throw new RuntimeException("Unable to initialise encryption", e);
}
}
}
stacktrace
java.lang.RuntimeException·Unable to decrypt datajavax.crypto.IllegalBlockSizeException: last block incomplete in decryption
Full TraceRaw
EncrypterService .java:59 EncrypterService .decrypt
Issue#1:
java.security.MessageDigest will provide an instance of MD5 digest.
For this, you need to import the following
import java.security.*;
Issue#2:
For encrypedValue, you are using value.getBytes() and
For decryptedValue , you are using encrypedValue .getBytes().
Here is some limitation for using getBytes(). It is platform independent.
so you should use getBytes("UTF-8") instead of getBytes()
byte[] bytesOfMessage = yourString.getBytes("UTF-8");
MessageDigest md = MessageDigest.getInstance(MD5_ALGORITH);
byte[] thedigest = md.digest(bytesOfMessage);
Resource Link: How can I generate an MD5 hash?
Issue#3: Encoding and Decoding
Mr. Andrea suggested like below:
In Java 8, there is an officially supported API for Base64 encoding
and decoding
Sample code using the "basic" encoding:
import java.util.Base64;
byte[] bytes = "Hello, World!".getBytes("UTF-8");
String encoded = Base64.getEncoder().encodeToString(bytes);
byte[] decoded = Base64.getDecoder().decode(encoded);
Resource Link: Decode Base64 data in Java
return new String(encode);
The problem is here. String is not a container for binary data. The round trip between byte[] and String is not guaranteed. You should either just pass around the original byte[] or else hex- or base64-encode it.
Aloha! I'm wondering if there's any decent way to do a good form of encryption on Android without writing my own function to do one.
Are there any libraries I should be using?
You can use AES, DES and 3DES, they all are included in java. I have posted a easy program from here http://sanjaal.com/java/186/java-encryption/tutorial-java-des-encryption-and-decryption/ which is using DES to encrypt/Decrypt
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
class DESEncryption {
private static final String UNICODE_FORMAT = "UTF8";
public static final String DES_ENCRYPTION_SCHEME = "DES";
private KeySpec myKeySpec;
private SecretKeyFactory mySecretKeyFactory;
private Cipher cipher;
byte[] keyAsBytes;
private String myEncryptionKey;
private String myEncryptionScheme;
SecretKey key;
public DESEncryption() throws Exception
{
myEncryptionKey = "ThisIsSecretEncryptionKey";
myEncryptionScheme = DES_ENCRYPTION_SCHEME;
keyAsBytes = myEncryptionKey.getBytes(UNICODE_FORMAT);
myKeySpec = new DESKeySpec(keyAsBytes);
mySecretKeyFactory = SecretKeyFactory.getInstance(myEncryptionScheme);
cipher = Cipher.getInstance(myEncryptionScheme);
key = mySecretKeyFactory.generateSecret(myKeySpec);
}
/**
* Method To Encrypt The String
*/
public String encrypt(String unencryptedString) {
String encryptedString = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainText = unencryptedString.getBytes(UNICODE_FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
BASE64Encoder base64encoder = new BASE64Encoder();
encryptedString = base64encoder.encode(encryptedText);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method To Decrypt An Ecrypted String
*/
public String decrypt(String encryptedString) {
String decryptedText=null;
try {
cipher.init(Cipher.DECRYPT_MODE, key);
BASE64Decoder base64decoder = new BASE64Decoder();
byte[] encryptedText = base64decoder.decodeBuffer(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText= bytes2String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
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();
}
/**
* Testing the DES Encryption And Decryption Technique
*/
public static void main(String args []) throws Exception
{
DESEncryption myEncryptor= new DESEncryption();
String stringToEncrypt="Sanjaal.com";
String encrypted=myEncryptor.encrypt(stringToEncrypt);
String decrypted=myEncryptor.decrypt(encrypted);
System.out.println("String To Encrypt: "+stringToEncrypt);
System.out.println("Encrypted Value :" + encrypted);
System.out.println("Decrypted Value :"+decrypted);
}
}