I'm implementing AES/GCM/NoPadding encryption in Swift for iOS with a minimum deployment target of 12.1. I'm trying to achieve similar functionality as my Java code below:
Cipher cipherAes = initCipher(Cipher.ENCRYPT_MODE, key, iv);
byte[] encryptedMessageByte = cipherAes.doFinal(messageBytes);
byte[] cipherByte = ByteBuffer.allocate(iv.length + salt.length + encryptedMessageByte.length)
.put(iv)
.put(salt)
.put(encryptedMessageByte)
.array();
In Swift, I've written the following code:
let gcm = GCM(iv: iv.bytes, additionalAuthenticatedData: nil, tagLength: tagLength)
let aes = try AES(key: key.bytes, blockMode: gcm, padding: .noPadding)
let encryptedMessage = try aes.encrypt(message.bytes)
let cipherData = Data(iv) + Data(salt) + Data(encryptedMessage)
However, the length of encryptedMessage in Swift differs from encryptedMessageByte in Java. I expected both to produce ciphertext of similar lengths.
Assurance: I'm sure that the lengths of key, iv, and salt are the same in both Java and Swift implementations.
Question: Are there any additional configurations or parameters needed for AES/GCM/NoPadding encryption in Swift to match the behavior in Java?
In the Java code, ciphertext and GCM authentication tag are concatenated by default; in the CryptoSwift code, both are handled detached by default.
To make the CryptoSwift code compatible with Java code, either ciphertext and tag can be explicitly concatenated:
...
let gcm = GCM(iv: iv.bytes, additionalAuthenticatedData: nil, tagLength: tagLength, mode: .detached) // detached is the default
let aes = try! AES(key: key.bytes, blockMode: gcm, padding: .noPadding)
let encryptedMessage = try! aes.encrypt(message.bytes)
let tag = gcm.authenticationTag!
let cipherData = Data(iv) + Data(salt) + Data(encryptedMessage) + Data(tag) // concatenate explicitly
...
or encryption is done in combined mode, whereby ciphertext and tag are implicitly concatenated as well:
...
let gcm = GCM(iv: iv.bytes, additionalAuthenticatedData: nil, tagLength: tagLength, mode: .combined) // apply combined mode
let aes = try! AES(key: key.bytes, blockMode: gcm, padding: .noPadding)
let encryptedMessageTag = try! aes.encrypt(message.bytes)
let cipherData = Data(iv) + Data(salt) + Data(encryptedMessageTag)
...
See also the AES-GCM examples in the documentation