I have problem with AES GCM Encryption on Swift
We have app on Java, where encryption is ok, server can read and process data, but my result server can not read
We have 2 different results.
At first I tried to use encryption CBC and ECB, but they told I should to use GCM.
If someone understand what I doing wrong, help me
Java code:
final String airSecretKey = "Wk+Uzyyn8991w/2V5OIqiQ==";
static Cipher cipher=null;
SecretKeySpec new_key=null;
Key kateKey=null;
public void onCreate() {
super.onCreate();
handler = new Handler();
if (doCryptoAes) {
new_key = new SecretKeySpec(airSecretKey.getBytes(), "AES");
kateKey = (Key) new SecretKeySpec(airSecretKey.getBytes(), "AES");
}
}
void generateCliper(){
try {
cipher = Cipher.getInstance("AES/GCM/NoPadding"); ///", "BC
} catch (NoSuchAlgorithmException e) {
Log.e("AES 1", e.toString());
} catch (NoSuchPaddingException e) {
Log.e("AES 2", e.toString());
} /*catch (NoSuchProviderException e) {
Log.e("AES 3", e.toString());
}*/
}
protected String encryptAir(String testText) {
byte[] encodedBytes = null;
String s_encode_result = "";
try {
byte[] iv = new byte[12];
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
if (cipher==null){
generateCliper();
}
cipher.init(Cipher.ENCRYPT_MODE, kateKey, ivParameterSpec); //new_key //
encodedBytes = cipher.doFinal(testText.getBytes());
for (int i=0;i<encodedBytes.length; i++){
s_encode_result+=getEncodeHex(encodedBytes[i]);//+" ";
}
} catch (Exception e) {
Log.e(e.toString());
}
return "<BJSN>"+s_encode_result+"</BJSN>";
}
protected String decryptAir(String encodedText) {
if (encodedText.length()<20) return "";
byte[] encryptedTextByte = getConvAES(encodedText);
//Base64.decode(encodedText, Base64.DEFAULT);
byte[] iv = new byte[12];
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
try {
cipher.init(Cipher.DECRYPT_MODE, kateKey, ivParameterSpec);
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
byte[] decryptedByte = new byte[0];
try {
decryptedByte = cipher.doFinal(encryptedTextByte);
} catch (BadPaddingException e) {
Log.e("AES 1", e.toString());
} catch (IllegalBlockSizeException e) {
Log.e("AES 2", e.toString());
}
String decryptedText = new String(decryptedByte);
// MainActivity.toast_str="Decrypted: "+decryptedText;
return decryptedText;
}
byte[] getConvAES(String textAesStr) {
int i_len = (textAesStr.length()-13)/2;
byte[] aesNonce= new byte[i_len];
if (textAesStr.indexOf( "<BJSN>") == 0 &&
textAesStr.indexOf( "</BJSN>") > 0 && i_len > 0) {
for (int i = 3; i < i_len+3; i++) {
String s_hex = "";
s_hex+=textAesStr.charAt(i*2);
s_hex+=textAesStr.charAt(i*2+1);
int i_binary=0;
try {
i_binary=Integer.parseInt(s_hex, 16);
aesNonce[i-3]=(byte) i_binary;
} catch (Exception e) {
aesNonce[i-3]=0;
}
}
}
return aesNonce;
}
My Swift Code:
import CryptoKit
let key = SymmetricKey(size: .bits192)
let plain = "BSD AIR"
func cryptoDemoCombinedData() {
let nonce = try! AES.GCM.Nonce(data: Data(base64Encoded: "fv1nixTVoYpSvpdA")!)
let tag = Data(base64Encoded: "Wk+Uzyyn8991w/2V5OIqiQ==")!
// Encrypt
let sealedBox = try! AES.GCM.seal(plain.data(using: .utf8)!, using: key, nonce: nonce, authenticating: tag)
// Decrypt
let sealedBoxRestored = try! AES.GCM.SealedBox(combined: sealedBox.combined!)
let decrypted = try! AES.GCM.open(sealedBoxRestored, using: key, authenticating: tag)
print("Crypto Demo II\n••••••••••••••••••••••••••••••••••••••••••••••••••\n")
print("Combined:\n\(sealedBox.combined!.base64EncodedString())\n")
print("Cipher:\n\(sealedBox.ciphertext.base64EncodedString())\n")
print("Nonce:\n\(nonce.withUnsafeBytes { Data(Array($0)).base64EncodedString() })\n")
print("Tag:\n\(tag.base64EncodedString())\n")
print("Decrypted:\n\(String(data: decrypted, encoding: .utf8)!)\n")
}
That is not how encryption works. Every time you encrypt the same plaintext, you MUST get a different output. With GCM this is achieved by using a unique random IV each time - a nonce (number used once).
GCM has its own requirements on IV, namely that an IV shall never be reused, and that is important.
String airSecretKey = "Wk+Uzyyn8991w/2V5OIqiQ=="; // this key is now public so you cannot use it
SecretKey key = new SecretKeySpec(Base64.getDecoder().decode(airSecretKey), "AES");
SecureRandom secureRandom = new SecureRandom();
byte[] iv = new byte[12];
secureRandom.nextBytes(iv);
AlgorithmParameters params = new GCMParameterSpec(128, iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, params, secureRandom);
cipher.updateAAD(...); // if used
byte[] encrypted = cipher.doFinal(plaintext);
The Swift version should perform the same operations. Java generates the tag and appends it to the ciphertext. It looks like in Swift you have to handle it manually instead.
Related
I have an AES class in java, my intention to decrypt a JSON string in js which encrypted in java,
here is my encryption code
public static void setKey(String myKey)
{
MessageDigest sha = null;
try {
key = myKey.getBytes("UTF-8");
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
System.out.println(Arrays.toString(key));
secretKey = new SecretKeySpec(key, "AES");
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public static String encrypt(String strToEncrypt, String secret)
{
try
{
setKey(secret);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8")));
}
catch (Exception e)
{
System.out.println("Error while encrypting: " + e.toString());
}
return null;
}
And I have tried many way using Cryptojs
var secret = "llssshhhhhhhhhhh";
var new_secret = CryptoJS.SHA1(getBytes(secret));
console.log(getBytes(new_secret.toString()));
var test = "<Encrypted Text>";
var bytes = CryptoJS.AES.decrypt(test, new_secret, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
var originalText = bytes.toString(CryptoJS.enc.Utf8);
console.log({originalText});
But I am not getting the original text even its return a blank string.
Can anyone know I am wrong?
When I encrypt the password !QAZxdr5 by the code below:
public static String encryptPassword(String msg) {
try {
KeySpec keySpec = new DESKeySpec(msg.getBytes());
SecretKey key = SecretKeyFactory.getInstance("DES").generateSecret(keySpec);
Cipher ecipher = Cipher.getInstance(key.getAlgorithm());
ecipher.init(Cipher.ENCRYPT_MODE, key);
//Encode the string into bytes using utf-8
byte[] utf8 = msg.getBytes("UTF8");
//Encrypt
byte[] enc = ecipher.doFinal(utf8);
//Encode bytes to base64 to get a string
return new String(Base64.getEncoder().encode(enc));
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
I got the output: QQiu2a4NT9YfDAtmHjbk1A==
Now I try to create the decryption for this:
public static String decrypt(String msg) {
try {
KeySpec keySpec = new DESKeySpec(msg.getBytes());
SecretKey key =
SecretKeyFactory.getInstance("DES").generateSecret(keySpec);
Cipher decipher = Cipher.getInstance(key.getAlgorithm());
decipher.init(Cipher.DECRYPT_MODE, key);
// Decode base64 to get bytes
byte[] dec = Base64.getDecoder().decode(msg.getBytes("UTF-8"));
//Decrypt
byte[] utf8 = decipher.doFinal(dec);
//Decode using utf-8
return new String(utf8, "UTF8");
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
However, it doesn't work properly as it returned null :(. Can you please help to check where I'm wrong?
Normally, Base64 encoding of !QAZxdr5 is IVFBWnhkcjU=, however, your code uses a key to encode extra AFAI understand, that's why you get QQiu2a4NT9YfDAtmHjbk1A==. So, decrypt() method needs to know also the key generated already, nonetheless, yours wouldn't.
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.Base64;
class EncryptDecryptTest {
public static void main(String[] args) throws Exception {
String key = "it's our key ~~~where is my +1 karma ??~~~ - soner";
String ciphertext = encrypt(key, "!QAZxdr5");
String decrypted = decrypt(key, ciphertext.trim());
String encrypted = encrypt(key, decrypted.trim());
if (ciphertext.contentEquals(encrypted.trim())) {
System.out.println("decrypted!");
} else {
System.out.println("wrong key!");
}
}
public static String encrypt(String key, String data)
throws GeneralSecurityException {
DESKeySpec desKeySpec = new DESKeySpec(key.getBytes(StandardCharsets.UTF_8));
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);
byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return Base64.getEncoder().encodeToString(cipher.doFinal(dataBytes));
}
public static String decrypt(String key, String data)
throws GeneralSecurityException {
byte[] dataBytes = Base64.getDecoder().decode(data);
DESKeySpec desKeySpec = new DESKeySpec(key.getBytes(StandardCharsets.UTF_8));
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] dataBytesDecrypted = (cipher.doFinal(dataBytes));
return new String(dataBytesDecrypted);
}
}
As a side note,
The fact that return new String(Base64.getEncoder().encode(enc)); uses toString() method of array
should be return Base64.getEncoder().encodeToString(enc); in order to get expected encoded datum.
In DES encryption, it uses the same key to encrypt and decrypt a message.
So for both operations you need to have the same key.
In your case, you have used the same string as the key and as the password.
public static String encryptPassword(String msg) {
try {
KeySpec keySpec = new DESKeySpec(msg.getBytes());
In above code segment when creating new DESKeySpec object, you need to pass the key as well.
public static String decrypt(String msg) {
try {
KeySpec keySpec = new DESKeySpec(msg.getBytes());
Even in above decypt method you have to pass the same key you have used in encrypt method.
But in this you have given the encoded string to generate the key.
That is where you have gone wrong.
So i suggest you to change the methods parameters by adding one more parameter as key and then pass the same value for key in both methods.
public static String encryptPassword(String msg, String keySp) {
try {
KeySpec keySpec = new DESKeySpec(keySp.getBytes());
}
public static String decrypt(String msg, String keySp) {
try {
KeySpec keySpec = new DESKeySpec(keySp.getBytes());
}
I have included only the lines that needed to be changed.
You can call those methods by,
String key = "!QAZxdr5";
String password = "!QAZxdr5";
String encriptedPassword = encryptPassword(password, key);
System.out.println(decrypt(encriptedPassword, key));
Am using library https://github.com/fukata/AES-256-CBC-Example for Encryption and decryption And with the help of Issue with key and iv on AES 256-CBC. Decryption is working properly.
But in the Case of Encryption this error happens
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.vinu.aessamble/com.example.vinu.aessamble.MainActivity}: java.lang.RuntimeException: javax.crypto.IllegalBlockSizeException:error:1e06c06a:Cipher functions:EVP_EncryptFinal_ex:DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
And i am using
String encrypted = AESUtil.encrypt("My PlainText to encrypt");
In AESUtils
public static String encrypt(String src) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, makeKey(), makeIv());
return Base64.encodeBytes(cipher.doFinal(src.getBytes()));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Using this for PKCS5Padding encryption is working but can't decrypted using the method. When using NoPadding for encryption it shows above error.
public static String decrypt(String src) {
String decrypted = "";
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
byte[] array= Base64.decode(src);
byte[] ivData=Arrays.copyOfRange(array,0,16);
byte[] encrypted = Arrays.copyOfRange(array, 16, array.length);
// Init the cipher with decrypt mode, key, and IV bytes array (no more hardcoded)
cipher.init(Cipher.DECRYPT_MODE, makeKey(), new IvParameterSpec(ivData));
// Decrypt same old way
decrypted = new String(cipher.doFinal(encrypted));
} catch (Exception e) {
throw new RuntimeException(e);
}
return decrypted;
}
makeIv()
static AlgorithmParameterSpec makeIv() {
try {
return new IvParameterSpec(sha256digest16(ENCRYPTION_IV));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
makeKey()
static Key makeKey() {
try {
byte[] key = ENCRYPTION_KEY.getBytes("UTF-8");
return new SecretKeySpec(key, "AES");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
Iv and key are
private static final String ENCRYPTION_KEY = qwertyuiopasdfghjklzxcvbnmqwerty";
private static final String ENCRYPTION_IV = "qbmocwtttkttpqvv";
Help me to do encryption.
My AES encryption program is returning different encrypted values on different machines. Can anyone please help on finding out what needs to be done to ensure the same encrypted value is returned on any machine the program is run..
private static SecretKeySpec secret;
private static String seed;
private static String text;
private static String salt = "Salt123";
private static int pswdIterations = 65536;
private static int keySize = 256;
/**
*
* #param mySeed
*/
public static void setSeed(String mySeed) {
try {
byte[] saltBytes = salt.getBytes("UTF-8");
PBEKeySpec spec = new PBEKeySpec(mySeed.toCharArray(), saltBytes,
pswdIterations, keySize);
SecretKeyFactory factory = SecretKeyFactory
.getInstance("PBKDF2WithHmacSHA1");
SecretKey secretKey = factory.generateSecret(spec);
secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
}
public static String getEncryptedStringFor(String text) {
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] encryptedData = cipher.doFinal(text.getBytes("UTF-8"));
return new String(Base64.encodeBase64(encryptedData));
} catch (Exception e) {
System.out.println("Error while encrypting: " + e.toString());
}
return null;
}
public static String getDecryptedStringFor(String text) {
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secret);
return (new String(cipher.doFinal(Base64
.decodeBase64(text.getBytes("UTF-8")))));
} catch (Exception e) {
System.out.println("Error while decrypting: " + e.toString());
}
return null;
}
Some sample values
seed : seed123
text : #text!
Encrypted value : RoVE3KsjzN0nNxCNsNpRPg==
seed : seed!!
text : #text123!
Encrypted value :X6pfUKCVXXrAEyqKko/kFQ==
The only thing I can see in the code is the following line:
return (new String(cipher.doFinal(Base64.decodeBase64(text.getBytes("UTF-8")))));
Now it looks like this is actually returning a String after decoding with UTF-8. But it doesn't: it uses the platform default:
return (new String(
cipher.doFinal(
Base64.decodeBase64(
text.getBytes("UTF-8")
)
)
));
Of course, the first ( is superfluous anyway, so try this:
byte[] ciphertext = Base64.decodeBase64(text.getBytes("UTF-8"));
byte[] plaintext = cipher.doFinal(ciphertext);
return new String(plaintext, "UTF-8");
Note that you can also use import static java.nio.charsets.StandardCharsets.UTF_8 nowadays, which lets you do away with the exception as well. I wish they would do the same for StandardCiphers.AES_CBC_PKCS7PADDING :)
Is there any way to get the same result as doing in MySQL
SELECT AES_ENCRYPT("text", "key")
using a Java function?
And if possible, what's the other function to simulate the AES_DECRYPT.
If you need the code to decrypt the algorithm is here JAVA
public static String aes_decrypt(String passwordhex, String strKey) throws Exception {
try {
byte[] keyBytes = Arrays.copyOf(strKey.getBytes("ASCII"), 16);
SecretKey key = new SecretKeySpec(keyBytes, "AES");
Cipher decipher = Cipher.getInstance("AES");
decipher.init(Cipher.DECRYPT_MODE, key);
char[] cleartext = passwordhex.toCharArray();
byte[] decodeHex = Hex.decodeHex(cleartext);
byte[] ciphertextBytes = decipher.doFinal(decodeHex);
return new String(ciphertextBytes);
} catch (Exception e) {
e.getMessage();
}
return null;
}
It received a standard hex format string but variable and returns the password. Test with those in main method
System.out.println(aes_encrypt("your_string_password", "your_string_key"));
System.out.println(aes_decrypt("standard_hex_format_string ", "your_string_key"));
firstable test only with encrypt, then just with decrypt.
By the way you must install 'commons-codec-1.6.jar' so you can use the Hex class
http://commons.apache.org/proper/commons-codec/download_codec.cgi
Greetings from Ecuador, Ibarra
Ok, I've managed to get it working like this.
MySQL Query:
SELECT HEX(aes_encrypt("password", "0123456789012345"));
Java function:
public static String aes_encrypt(String password, String strKey) {
try {
byte[] keyBytes = Arrays.copyOf(strKey.getBytes("ASCII"), 16);
SecretKey key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cleartext = password.getBytes("UTF-8");
byte[] ciphertextBytes = cipher.doFinal(cleartext);
return new String(Hex.encodeHex(ciphertextBytes));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} return null;
}
in my case i just code like this
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
and than create method aes 128
public static String aes_encrypt(String password, String strKey) {
try {
byte[] keyBytes = Arrays.copyOf(strKey.getBytes("ASCII"), 16);
SecretKey key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cleartext = password.getBytes("UTF-8");
byte[] ciphertextBytes = cipher.doFinal(cleartext);
return new String(bytesToHex(ciphertextBytes));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
example :
mysql code
SELECT HEX(aes_encrypt("text", "0123456889812345"))
java code
System.out.println(aes_encrypt("text", "0123456889812345"));