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.