2

我正在使用 cryptoSwift 库进行加密和解密。但它只使用 16 个字节的字符串。如果我传递的是小字符串或少于 16 个字节,则结果为零。

我正在使用增量操作使用 Cryptor 的实例并一次加密/解密一个部分。

请在这里帮助我,我做错了什么。

提前致谢。

func encAndDec(){
        do {
            // Encryption start
            let data =  Data.init(base64Encoded: "12345678901234567890123456789012".base64Encoded()!)
            let iv : Array<UInt8> = [0,0,0,0,0,0,0,0,0,0,0,0]
            let nIv = Data(iv)
            let gcmEnc = GCM(iv: nIv.bytes, mode: .detached)
            var enc = try? AES(key: data!.bytes, blockMode: gcmEnc, padding: .noPadding).makeEncryptor()
            let arrStr = ["My name is tarun"] // Working
            //let arrStr = ["tarun"] // Not working for this string
            var ciphertext = Array<UInt8>()
            for txt in arrStr{
                 let ciperText = try? enc?.update(withBytes: Array(txt.utf8)) // Getting nil for small string.
                 ciphertext.append(contentsOf: ciperText!)
            }
            var res = try? enc?.finish()
            gcmEnc.authenticationTag = self.randomGenerateBytes(count: 16)?.bytes
            res?.append(contentsOf: (gcmEnc.authenticationTag)!)
            let cipherData = Data(ciphertext) + Data(res!)
            let strEnc = String(decoding: cipherData, as: UTF8.self)
            print(strEnc)
        
            // Decryption start from here
            do {
                let gcmDec = GCM.init(iv: nIv.bytes, additionalAuthenticatedData: nil, tagLength: 16, mode: .detached)
                var aesDec = try! AES(key: data!.bytes, blockMode: gcmDec, padding: .noPadding).makeDecryptor()
                let tag_length = 16
                let encData = cipherData.subdata(in: 0..<cipherData.count - tag_length)
                let tag = cipherData.subdata(in: encData.count ..< cipherData.count)
                
                let decData = try? aesDec.update(withBytes: encData.bytes) //Getting nil here for small string
                let strData = String(decoding: decData!, as: UTF8.self)
                print(strData)
                
                do{
                    var res = try? aesDec.finish(withBytes: tag.bytes)
                    res?.append(contentsOf: tag)
                    
                }catch{
                    
                }
                
            } catch {
                // failed
            }
    }
    }
    func randomGenerateBytes(count: Int) -> Data? {
        let bytes = UnsafeMutableRawPointer.allocate(byteCount: count, alignment: 1)
        defer { bytes.deallocate() }
        let status = CCRandomGenerateBytes(bytes, count)
        guard status == kCCSuccess else { return nil }
        return Data(bytes: bytes, count: count)
    }
4

2 回答 2

1

正如一些评论者所建议的那样,CryptoSwift 中的实现没有任何问题aes-256-gcm,只是代码中有一些错误。希望以下内容对您有所帮助。

为简洁起见,我将在下面将其称为 GCM。

GCM 加密将明文、密钥和初始化向量作为输入,并生成密文和身份验证标签。在您的代码中,您将身份验证标签设置为随机字节,从而覆盖身份验证标签。

我认为如果你把你的代码分解成一些函数,每个函数都有一个明确定义的目的,那就更清楚了。为了清楚起见,我还删除了一些从Datato 和 from的转换[UInt8]

下面是加密函数的样子:

func enc(plainText: [String], key: [UInt8], iv: [UInt8]) throws -> (cipherText: [UInt8], authenticationTag: [UInt8]?)
{
    let gcmEnc = GCM(iv: iv, mode: .detached)
    var enc = try AES(key: key, blockMode: gcmEnc, padding: .noPadding).makeEncryptor()
    var ciphertext = Array<UInt8>()
    for txt in plainText {
         ciphertext  += try enc.update(withBytes: Array(txt.utf8))
    }
    ciphertext += try enc.finish()
    return (ciphertext, gcmEnc.authenticationTag)
}

当您解密 GCM 时,您需要传入密文、密钥、初始化向量身份验证标签。看起来像这样:

func dec(cipherText: [UInt8], authenticationTag: [UInt8]?, key: [UInt8], iv: [UInt8]) throws -> [UInt8]? {
    let tagLength = authenticationTag?.count ?? 0
    let gcmDec = GCM.init(iv: iv, additionalAuthenticatedData: nil, tagLength: tagLength, mode: .detached)
    gcmDec.authenticationTag = authenticationTag
    var aesDec = try AES(key: key, blockMode: gcmDec, padding: .noPadding).makeDecryptor()
    var decData = try aesDec.update(withBytes: cipherText)
    decData += try aesDec.finish()
    return decData
}

在这两种情况下,您都需要确保将finish调用的输出附加到密文或明文中。这对于少量数据尤其重要,因为该update方法可能不会产生任何结果!

编写完这两个函数后,您可以按如下方式重写您的测试函数:

func encAndDec(){
    do {
        guard let key =  Data.init(base64Encoded: "12345678901234567890123456789012".base64Encoded()!)
            else {
                fatalError("Failed to create key")
        }
        let iv : Array<UInt8> = [0,0,0,0,
                                 0,0,0,0,
                                 0,0,0,0]

        //let arrStr = ["My name is tarun"] // Working
        let arrStr = ["tarun"] // Not working for this string
        
        let (cipherText, authenticationTag) = try enc(plainText: arrStr, key: key.bytes, iv: iv)
        guard let decrypedPlainText = try dec(cipherText: cipherText,
                                              authenticationTag: authenticationTag, key: key.bytes, iv: iv) else {
            fatalError("Decryption return nil")
        }
        guard let decryptedString = String(bytes: decrypedPlainText, encoding: .utf8) else {
            fatalError("Failed to convert plaintext to string using UTF8 encoding.")
        }

        print("Decrypted Plaintext: \(decryptedString)")

    }
    catch let e {
        print("EXCEPTION: \(e)")
    }
}

如果你运行它,你会发现它产生了预期的输出。

完整的示例代码可以在以下位置找到:https ://gist.github.com/iosdevzone/45456d2911bf2422bc4a6898876ba0ab

于 2020-10-23T18:19:57.180 回答
-2

我不相信 GCM 需要填充。这是一个几乎直接来自 NODE.JS 文档的示例,该示例运行良好且不使用填充。下面的行将显示密文长度为5

我已经对 Ruby、Java、Go 和其他人做了同样的事情,并且没有一个需要填充或输入值是 16 字节的倍数,就像 Swift 库似乎需要的那样。其他人帮助确认这是 GCM 的 Swift 实现中的错误吗?

const crypto = require('crypto');

const key = '12345678901234567890123456789012';
const iv = '000000000000'

const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);  
const plaintext = 'Hello';  
const ciphertext = cipher.update(plaintext, 'utf8');  
**console.log("ciphertext length %d", ciphertext.length)**  

cipher.final();  
const tag = cipher.getAuthTag();

const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);  
decipher.setAuthTag(tag);  
const receivedPlaintext = decipher.update(ciphertext, null, 'utf8');  

try {
  decipher.final();
} catch (err) {
  console.error('Authentication failed!');
  return;
}

console.log(receivedPlaintext);
于 2020-10-23T06:08:03.937 回答