I'm trying to convert this java code:
package ffsd;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class GatewayEncryptUtil {
public static String encrypt(String plainText, String key) throws Exception {
return new String(Base64.getEncoder().encode(doCrypt(plainText.getBytes(), 1, key)));
}
public static byte[] doCrypt(byte[] inputText, int operation, String key) throws Exception {
MessageDigest mDigest = MessageDigest.getInstance("SHA-512");
byte[] secretKey = key.getBytes();
byte[] digestSeed = mDigest.digest(secretKey);
byte[] hashKey = Arrays.copyOf(digestSeed, 16);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec skspec = new SecretKeySpec(hashKey, "AES");
String cipherKey = new String(secretKey);
GCMParameterSpec gcmParams = new GCMParameterSpec(128, cipherKey.substring(0, 16).getBytes());
cipher.init(operation, skspec, gcmParams);
return cipher.doFinal(inputText);
}
public static void main(String[] args) {
try {
System.out.println(encrypt("Password", "1234567890123456"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
to C# .NET 5.0 using the new AesGcm class:
using System;
using System.Security.Cryptography;
using System.Text;
namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
string inputText = "Password";
string msgId = "1234567890123456";
byte[] hashKey;
byte[] secretKey = Encoding.ASCII.GetBytes(msgId);
using (var hasher = SHA512.Create())
{
byte[] digestSeed = hasher.ComputeHash(secretKey);
hashKey = new byte[16];
Array.Copy(digestSeed, hashKey, hashKey.Length);
}
using (var aesGcm = new AesGcm(hashKey))
{
byte[] nonce = new byte[AesGcm.NonceByteSizes.MaxSize];
byte[] plainText = Encoding.ASCII.GetBytes(inputText);
byte[] authTag = new byte[AesGcm.TagByteSizes.MaxSize];
byte[] cipherText = new byte[plainText.Length];
aesGcm.Encrypt(nonce, plainText, cipherText, authTag);
string cipherTextBase64 = Convert.ToBase64String(cipherText);
string authTagBase64 = Convert.ToBase64String(authTag);
Console.WriteLine(cipherTextBase64);
Console.WriteLine(authTagBase64);
}
}
}
}
But I don't know what the nonce is supposed to be. Not seeing that in the java code anywhere. Can anyone give me any pointers to this?
The result of the java code is: "gc1zTHlIPQusN5e+Rjq+veDoIYdU1nCQ"
mine is obviously incomplete and not coming close.
IV and Nonce mean the same thing.
The IV is the second argument to GCMParameterSpec here:
GCMParameterSpec gcmParams = new GCMParameterSpec(128, cipherKey.substring(0, 16).getBytes());
.NET calls this Nonce.
I was able to use BouncyCastle to convert this to C#:
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using System;
using System.Security.Cryptography;
using System.Text;
namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
string inputText = "Password";
string msgId = "1234567890123456";
string value = Encrypt(inputText, msgId);
Console.WriteLine(value);
}
public static string Encrypt(string plainText, string msgId)
{
const byte GcmTagSize = 16;
byte[] hashKey;
byte[] secretKey = Encoding.ASCII.GetBytes(msgId);
using (var hasher = SHA512.Create())
{
byte[] digestSeed = hasher.ComputeHash(secretKey);
hashKey = new byte[16];
Array.Copy(digestSeed, hashKey, hashKey.Length);
}
var keyParameter = new KeyParameter(hashKey);
var keyParameters = new AeadParameters(keyParameter, GcmTagSize * 8, secretKey);
var cipher = CipherUtilities.GetCipher("AES/GCM/NoPadding");
cipher.Init(true, keyParameters);
var plainTextData = Encoding.ASCII.GetBytes(plainText);
var cipherText = cipher.DoFinal(plainTextData);
return Convert.ToBase64String(cipherText);
}
}
}
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'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 "";
}
}
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);
}
}