AES加密在swift中

Ank*_*hah 24 cryptography aes ios commoncrypto swift

我正在尝试在swift中实现AES加密.Android和C#的加密解密工作正常.我需要在swift中实现它.这是Android和C#的当前代码,后面是这个.

我试着用

  1. CryptoSwift
  2. 跨平台AES加密

但它都不起作用.当我在服务器上发送加密的字符串时,它没有被解密.

任何帮助将不胜感激

zap*_*aph 38

一定要使用相同的参数,这些参数似乎是带有CBC模式的AES,带有iv PKCS5Padding(实际上是PKCS#7)填充和一个16字节(128位)密钥.

PKCS#5填充和PKCS#7填充基本相同,有时出于历史原因,PKCS#5填充被指定用于AES但实际填充是PKCS#7.

确保密钥,iv和加密数据的编码都匹配.Hex将它们转储到两个平台上以确保它们完全相同.加密功能并不难使用,如果所有输入参数都正确,输出将是正确的.

为了使这更安全,iv应该是随机字节并且在加密数据之前加以使用以在解密期间使用.

跨平台的AES加密使用256位密钥,以便将无法工作原样.

例:

斯威夫特2

// operation: kCCEncrypt or kCCDecrypt
func testCrypt(data data:[UInt8], keyData:[UInt8], ivData:[UInt8], operation:Int) -> [UInt8]? {
    let cryptLength  = size_t(data.count+kCCBlockSizeAES128)
    var cryptData    = [UInt8](count:cryptLength, repeatedValue:0)

    let keyLength             = size_t(kCCKeySizeAES128)
    let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
    let options:  CCOptions   = UInt32(kCCOptionPKCS7Padding)

    var numBytesEncrypted :size_t = 0

    let cryptStatus = CCCrypt(CCOperation(operation),
                              algoritm,
                              options,
                              keyData, keyLength,
                              ivData,
                              data, data.count,
                              &cryptData, cryptLength,
                              &numBytesEncrypted)

    if UInt32(cryptStatus) == UInt32(kCCSuccess) {
        cryptData.removeRange(numBytesEncrypted..<cryptData.count)

    } else {
        print("Error: \(cryptStatus)")
    }

    return cryptData;
}

let message       = "Don´t try to read this text. Top Secret Stuff"
let messageData   = Array(message.utf8)
let keyData       = Array("12345678901234567890123456789012".utf8)
let ivData        = Array("abcdefghijklmnop".utf8)
let encryptedData = testCrypt(data:messageData,   keyData:keyData, ivData:ivData, operation:kCCEncrypt)!
let decryptedData = testCrypt(data:encryptedData, keyData:keyData, ivData:ivData, operation:kCCDecrypt)!
var decrypted     = String(bytes:decryptedData, encoding:NSUTF8StringEncoding)!

print("message:       \(message)");
print("messageData:   \(NSData(bytes:messageData,   length:messageData.count))");
print("keyData:       \(NSData(bytes:keyData,       length:keyData.count))");
print("ivData:        \(NSData(bytes:ivData,        length:ivData.count))");
print("encryptedData: \(NSData(bytes:encryptedData, length:encryptedData.count))");
print("decryptedData: \(NSData(bytes:decryptedData, length:decryptedData.count))");
print("decrypted:     \(String(bytes:decryptedData,encoding:NSUTF8StringEncoding)!)");
Run Code Online (Sandbox Code Playgroud)

输出:

message:       Don´t try to read this text. Top Secret Stuff  
messageData:   446f6ec2 b4742074 72792074 6f207265 61642074 68697320 74657874 2e20546f 70205365 63726574 20537475 6666  
keyData:       31323334 35363738 39303132 33343536 37383930 31323334 35363738 39303132  
ivData:        61626364 65666768 696a6b6c 6d6e6f70  
encryptedData: b1b6dc17 62eaf3f8 baa1cb87 21ddc35c dee803ed fb320020 85794848 21206943 a85feb5b c8ee58fc d6fb664b 96b81114  
decryptedData: 446f6ec2 b4742074 72792074 6f207265 61642074 68697320 74657874 2e20546f 70205365 63726574 20537475 6666  
decrypted:     Don´t try to read this text. Top Secret Stuff  

Swift 3的[UInt8]类型

func testCrypt(data:[UInt8], keyData:[UInt8], ivData:[UInt8], operation:Int) -> [UInt8]? {
    let cryptLength  = size_t(data.count+kCCBlockSizeAES128)
    var cryptData    = [UInt8](repeating:0, count:cryptLength)

    let keyLength             = size_t(kCCKeySizeAES128)
    let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
    let options:  CCOptions   = UInt32(kCCOptionPKCS7Padding)

    var numBytesEncrypted :size_t = 0

    let cryptStatus = CCCrypt(CCOperation(operation),
                              algoritm,
                              options,
                              keyData, keyLength,
                              ivData,
                              data, data.count,
                              &cryptData, cryptLength,
                              &numBytesEncrypted)

    if UInt32(cryptStatus) == UInt32(kCCSuccess) {
        cryptData.removeSubrange(numBytesEncrypted..<cryptData.count)

    } else {
        print("Error: \(cryptStatus)")
    }

    return cryptData;
}
Run Code Online (Sandbox Code Playgroud)

Swift 3和4 Data型号

func testCrypt(data:Data, keyData:Data, ivData:Data, operation:Int) -> Data {
    let cryptLength  = size_t(data.count + kCCBlockSizeAES128)
    var cryptData = Data(count:cryptLength)

    let keyLength             = size_t(kCCKeySizeAES128)
    let options   = CCOptions(kCCOptionPKCS7Padding)


    var numBytesEncrypted :size_t = 0

    let cryptStatus = cryptData.withUnsafeMutableBytes {cryptBytes in
        data.withUnsafeBytes {dataBytes in
            ivData.withUnsafeBytes {ivBytes in
                keyData.withUnsafeBytes {keyBytes in
                    CCCrypt(CCOperation(operation),
                              CCAlgorithm(kCCAlgorithmAES),
                              options,
                              keyBytes, keyLength,
                              ivBytes,
                              dataBytes, data.count,
                              cryptBytes, cryptLength,
                              &numBytesEncrypted)
                }
            }
        }
    }

    if UInt32(cryptStatus) == UInt32(kCCSuccess) {
        cryptData.removeSubrange(numBytesEncrypted..<cryptData.count)

    } else {
        print("Error: \(cryptStatus)")
    }

    return cryptData;
}

let message     = "Don´t try to read this text. Top Secret Stuff"
let messageData = message.data(using:String.Encoding.utf8)!
let keyData     = "12345678901234567890123456789012".data(using:String.Encoding.utf8)!
let ivData      = "abcdefghijklmnop".data(using:String.Encoding.utf8)!

let encryptedData = testCrypt(data:messageData,   keyData:keyData, ivData:ivData, operation:kCCEncrypt)
let decryptedData = testCrypt(data:encryptedData, keyData:keyData, ivData:ivData, operation:kCCDecrypt)
var decrypted     = String(bytes:decryptedData, encoding:String.Encoding.utf8)!
Run Code Online (Sandbox Code Playgroud)

日落文档部分的示例:

CBC模式下的AES加密,随机IV(Swift 3+)

iv以加密数据为前缀

aesCBC128Encrypt将创建一个随机IV并加密前缀为加密代码.
aesCBC128Decrypt将在解密期间使用前缀IV.

输入是数据,键是数据对象.如果需要的编码形式(如Base64)在调用方法中转换为和/或来自.

密钥长度应精确为128位(16字节),192位(24字节)或256位(32字节).如果使用其他密钥大小,则会引发错误.

PKCS#7填充默认设置.

此示例需要Common Crypto
.必须为项目提供桥接标头:
#import <CommonCrypto/CommonCrypto.h>
添加Security.framework到项目中.

这是示例,而不是生产代码.

enum AESError: Error {
    case KeyError((String, Int))
    case IVError((String, Int))
    case CryptorError((String, Int))
}

// The iv is prefixed to the encrypted data
func aesCBCEncrypt(data:Data, keyData:Data) throws -> Data {
    let keyLength = keyData.count
    let validKeyLengths = [kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256]
    if (validKeyLengths.contains(keyLength) == false) {
        throw AESError.KeyError(("Invalid key length", keyLength))
    }

    let ivSize = kCCBlockSizeAES128;
    let cryptLength = size_t(ivSize + data.count + kCCBlockSizeAES128)
    var cryptData = Data(count:cryptLength)

    let status = cryptData.withUnsafeMutableBytes {ivBytes in
        SecRandomCopyBytes(kSecRandomDefault, kCCBlockSizeAES128, ivBytes)
    }
    if (status != 0) {
        throw AESError.IVError(("IV generation failed", Int(status)))
    }

    var numBytesEncrypted :size_t = 0
    let options   = CCOptions(kCCOptionPKCS7Padding)

    let cryptStatus = cryptData.withUnsafeMutableBytes {cryptBytes in
        data.withUnsafeBytes {dataBytes in
            keyData.withUnsafeBytes {keyBytes in
                CCCrypt(CCOperation(kCCEncrypt),
                        CCAlgorithm(kCCAlgorithmAES),
                        options,
                        keyBytes, keyLength,
                        cryptBytes,
                        dataBytes, data.count,
                        cryptBytes+kCCBlockSizeAES128, cryptLength,
                        &numBytesEncrypted)
            }
        }
    }

    if UInt32(cryptStatus) == UInt32(kCCSuccess) {
        cryptData.count = numBytesEncrypted + ivSize
    }
    else {
        throw AESError.CryptorError(("Encryption failed", Int(cryptStatus)))
    }

    return cryptData;
}

// The iv is prefixed to the encrypted data
func aesCBCDecrypt(data:Data, keyData:Data) throws -> Data? {
    let keyLength = keyData.count
    let validKeyLengths = [kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256]
    if (validKeyLengths.contains(keyLength) == false) {
        throw AESError.KeyError(("Invalid key length", keyLength))
    }

    let ivSize = kCCBlockSizeAES128;
    let clearLength = size_t(data.count - ivSize)
    var clearData = Data(count:clearLength)

    var numBytesDecrypted :size_t = 0
    let options   = CCOptions(kCCOptionPKCS7Padding)

    let cryptStatus = clearData.withUnsafeMutableBytes {cryptBytes in
        data.withUnsafeBytes {dataBytes in
            keyData.withUnsafeBytes {keyBytes in
                CCCrypt(CCOperation(kCCDecrypt),
                        CCAlgorithm(kCCAlgorithmAES128),
                        options,
                        keyBytes, keyLength,
                        dataBytes,
                        dataBytes+kCCBlockSizeAES128, clearLength,
                        cryptBytes, clearLength,
                        &numBytesDecrypted)
            }
        }
    }

    if UInt32(cryptStatus) == UInt32(kCCSuccess) {
        clearData.count = numBytesDecrypted
    }
    else {
        throw AESError.CryptorError(("Decryption failed", Int(cryptStatus)))
    }

    return clearData;
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

let clearData = "clearData0123456".data(using:String.Encoding.utf8)!
let keyData   = "keyData890123456".data(using:String.Encoding.utf8)!
print("clearData:   \(clearData as NSData)")
print("keyData:     \(keyData as NSData)")

var cryptData :Data?
do {
    cryptData = try aesCBCEncrypt(data:clearData, keyData:keyData)
    print("cryptData:   \(cryptData! as NSData)")
}
catch (let status) {
    print("Error aesCBCEncrypt: \(status)")
}

let decryptData :Data?
do {
    let decryptData = try aesCBCDecrypt(data:cryptData!, keyData:keyData)
    print("decryptData: \(decryptData! as NSData)")
}
catch (let status) {
    print("Error aesCBCDecrypt: \(status)")
}
Run Code Online (Sandbox Code Playgroud)

示例输出:

clearData:   <636c6561 72446174 61303132 33343536>
keyData:     <6b657944 61746138 39303132 33343536>
cryptData:   <92c57393 f454d959 5a4d158f 6e1cd3e7 77986ee9 b2970f49 2bafcf1a 8ee9d51a bde49c31 d7780256 71837a61 60fa4be0>
decryptData: <636c6561 72446174 61303132 33343536>
Run Code Online (Sandbox Code Playgroud)

注意:
CBC模式示例代码的一个典型问题是它将随机IV的创建和共享留给用户.此示例包括生成IV,加密数据前缀并在解密期间使用前缀IV.这使临时用户免于CBC模式所需的细节.

为了安全起见,加密数据也应该具有身份验证,此示例代码不提供为了小,并允许其他平台更好的互操作性.

还缺少密钥的密钥派生密钥,建议使用PBKDF2是文本密码用作密钥材料.

对于强大的生产准备好的多平台加密代码,请参阅RNCryptor.


Den*_*Den 10

迅捷5

我重构了@ingconti的代码。

import Foundation
import CommonCrypto

struct AES {

    // MARK: - Value
    // MARK: Private
    private let key: Data
    private let iv: Data


    // MARK: - Initialzier
    init?(key: String, iv: String) {
        guard key.count == kCCKeySizeAES128 || key.count == kCCKeySizeAES256, let keyData = key.data(using: .utf8) else {
            debugPrint("Error: Failed to set a key.")
            return nil
        }

        guard iv.count == kCCBlockSizeAES128, let ivData = iv.data(using: .utf8) else {
            debugPrint("Error: Failed to set an initial vector.")
            return nil
        }


        self.key = keyData
        self.iv  = ivData
    }


    // MARK: - Function
    // MARK: Public
    func encrypt(string: String) -> Data? {
        return crypt(data: string.data(using: .utf8), option: CCOperation(kCCEncrypt))
    }

    func decrypt(data: Data?) -> String? {
        guard let decryptedData = crypt(data: data, option: CCOperation(kCCDecrypt)) else { return nil }
        return String(bytes: decryptedData, encoding: .utf8)
    }

    func crypt(data: Data?, option: CCOperation) -> Data? {
        guard let data = data else { return nil }

        let cryptLength = data.count + kCCBlockSizeAES128
        var cryptData   = Data(count: cryptLength)

        let keyLength = key.count
        let options   = CCOptions(kCCOptionPKCS7Padding)

        var bytesLength = Int(0)

        let status = cryptData.withUnsafeMutableBytes { cryptBytes in
            data.withUnsafeBytes { dataBytes in
                iv.withUnsafeBytes { ivBytes in
                    key.withUnsafeBytes { keyBytes in
                    CCCrypt(option, CCAlgorithm(kCCAlgorithmAES), options, keyBytes.baseAddress, keyLength, ivBytes.baseAddress, dataBytes.baseAddress, data.count, cryptBytes.baseAddress, cryptLength, &bytesLength)
                    }
                }
            }
        }

        guard UInt32(status) == UInt32(kCCSuccess) else {
            debugPrint("Error: Failed to crypt data. Status \(status)")
            return nil
        }

        cryptData.removeSubrange(bytesLength..<cryptData.count)
        return cryptData
    }
}
Run Code Online (Sandbox Code Playgroud)

这样使用

let password = "UserPassword1!"
let key128   = "1234567890123456"                   // 16 bytes for AES128
let key256   = "12345678901234561234567890123456"   // 32 bytes for AES256
let iv       = "abcdefghijklmnop"                   // 16 bytes for AES128

let aes128 = AES(key: key128, iv: iv)
let aes256 = AES(key: key256, iv: iv)

let encryptedPassword128 = aes128?.encrypt(string: password)
aes128?.decrypt(data: encryptedPassword128)

let encryptedPassword256 = aes256?.encrypt(string: password)
aes256?.decrypt(data: encryptedPassword256)
Run Code Online (Sandbox Code Playgroud)

结果

在此处输入图片说明


bac*_*h-f 10

基于@zaph 很好的答案,我创建了这个Playground

斯威夫特 5

import Foundation
import CommonCrypto

protocol Cryptable {
    func encrypt(_ string: String) throws -> Data
    func decrypt(_ data: Data) throws -> String
}

struct AES {
    private let key: Data
    private let ivSize: Int         = kCCBlockSizeAES128
    private let options: CCOptions  = CCOptions(kCCOptionPKCS7Padding)

    init(keyString: String) throws {
        guard keyString.count == kCCKeySizeAES256 else {
            throw Error.invalidKeySize
        }
        self.key = Data(keyString.utf8)
    }
}

extension AES {
    enum Error: Swift.Error {
        case invalidKeySize
        case generateRandomIVFailed
        case encryptionFailed
        case decryptionFailed
        case dataToStringFailed
    }
}

private extension AES {

    func generateRandomIV(for data: inout Data) throws {

        try data.withUnsafeMutableBytes { dataBytes in

            guard let dataBytesBaseAddress = dataBytes.baseAddress else {
                throw Error.generateRandomIVFailed
            }

            let status: Int32 = SecRandomCopyBytes(
                kSecRandomDefault,
                kCCBlockSizeAES128,
                dataBytesBaseAddress
            )

            guard status == 0 else {
                throw Error.generateRandomIVFailed
            }
        }
    }
}

extension AES: Cryptable {

    func encrypt(_ string: String) throws -> Data {
        let dataToEncrypt = Data(string.utf8)

        let bufferSize: Int = ivSize + dataToEncrypt.count + kCCBlockSizeAES128
        var buffer = Data(count: bufferSize)
        try generateRandomIV(for: &buffer)

        var numberBytesEncrypted: Int = 0

        do {
            try key.withUnsafeBytes { keyBytes in
                try dataToEncrypt.withUnsafeBytes { dataToEncryptBytes in
                    try buffer.withUnsafeMutableBytes { bufferBytes in

                        guard let keyBytesBaseAddress = keyBytes.baseAddress,
                            let dataToEncryptBytesBaseAddress = dataToEncryptBytes.baseAddress,
                            let bufferBytesBaseAddress = bufferBytes.baseAddress else {
                                throw Error.encryptionFailed
                        }

                        let cryptStatus: CCCryptorStatus = CCCrypt( // Stateless, one-shot encrypt operation
                            CCOperation(kCCEncrypt),                // op: CCOperation
                            CCAlgorithm(kCCAlgorithmAES),           // alg: CCAlgorithm
                            options,                                // options: CCOptions
                            keyBytesBaseAddress,                    // key: the "password"
                            key.count,                              // keyLength: the "password" size
                            bufferBytesBaseAddress,                 // iv: Initialization Vector
                            dataToEncryptBytesBaseAddress,          // dataIn: Data to encrypt bytes
                            dataToEncryptBytes.count,               // dataInLength: Data to encrypt size
                            bufferBytesBaseAddress + ivSize,        // dataOut: encrypted Data buffer
                            bufferSize,                             // dataOutAvailable: encrypted Data buffer size
                            &numberBytesEncrypted                   // dataOutMoved: the number of bytes written
                        )

                        guard cryptStatus == CCCryptorStatus(kCCSuccess) else {
                            throw Error.encryptionFailed
                        }
                    }
                }
            }

        } catch {
            throw Error.encryptionFailed
        }

        let encryptedData: Data = buffer[..<(numberBytesEncrypted + ivSize)]
        return encryptedData
    }

    func decrypt(_ data: Data) throws -> String {

        let bufferSize: Int = data.count - ivSize
        var buffer = Data(count: bufferSize)

        var numberBytesDecrypted: Int = 0

        do {
            try key.withUnsafeBytes { keyBytes in
                try data.withUnsafeBytes { dataToDecryptBytes in
                    try buffer.withUnsafeMutableBytes { bufferBytes in

                        guard let keyBytesBaseAddress = keyBytes.baseAddress,
                            let dataToDecryptBytesBaseAddress = dataToDecryptBytes.baseAddress,
                            let bufferBytesBaseAddress = bufferBytes.baseAddress else {
                                throw Error.encryptionFailed
                        }

                        let cryptStatus: CCCryptorStatus = CCCrypt( // Stateless, one-shot encrypt operation
                            CCOperation(kCCDecrypt),                // op: CCOperation
                            CCAlgorithm(kCCAlgorithmAES128),        // alg: CCAlgorithm
                            options,                                // options: CCOptions
                            keyBytesBaseAddress,                    // key: the "password"
                            key.count,                              // keyLength: the "password" size
                            dataToDecryptBytesBaseAddress,          // iv: Initialization Vector
                            dataToDecryptBytesBaseAddress + ivSize, // dataIn: Data to decrypt bytes
                            bufferSize,                             // dataInLength: Data to decrypt size
                            bufferBytesBaseAddress,                 // dataOut: decrypted Data buffer
                            bufferSize,                             // dataOutAvailable: decrypted Data buffer size
                            &numberBytesDecrypted                   // dataOutMoved: the number of bytes written
                        )

                        guard cryptStatus == CCCryptorStatus(kCCSuccess) else {
                            throw Error.decryptionFailed
                        }
                    }
                }
            }
        } catch {
            throw Error.encryptionFailed
        }

        let decryptedData: Data = buffer[..<numberBytesDecrypted]

        guard let decryptedString = String(data: decryptedData, encoding: .utf8) else {
            throw Error.dataToStringFailed
        }

        return decryptedString
    }
}

do {
    let aes = try AES(keyString: "FiugQTgPNwCWUY,VhfmM4cKXTLVFvHFe")

    let stringToEncrypt: String = "please encrypt meeee"
    print("String to encrypt:\t\t\t\(stringToEncrypt)")

    let encryptedData: Data = try aes.encrypt(stringToEncrypt)
    print("String encrypted (base64):\t\(encryptedData.base64EncodedString())")

    let decryptedData: String = try aes.decrypt(encryptedData)
    print("String decrypted:\t\t\t\(decryptedData)")

} catch {
    print("Something went wrong: \(error)")
}
Run Code Online (Sandbox Code Playgroud)

输出:

输出

我还基于它创建了一个Swift 包

https://github.com/backslash-f/aescryptable


ing*_*nti 5

我的两分钱:

swift 4 / xcode 9扩展数据:

extension Data{

    func aesEncrypt( keyData: Data, ivData: Data, operation: Int) -> Data {
        let dataLength = self.count
        let cryptLength  = size_t(dataLength + kCCBlockSizeAES128)
        var cryptData = Data(count:cryptLength)

        let keyLength = size_t(kCCKeySizeAES128)
        let options = CCOptions(kCCOptionPKCS7Padding)


        var numBytesEncrypted :size_t = 0

        let cryptStatus = cryptData.withUnsafeMutableBytes {cryptBytes in
            self.withUnsafeBytes {dataBytes in
                ivData.withUnsafeBytes {ivBytes in
                    keyData.withUnsafeBytes {keyBytes in
                        CCCrypt(CCOperation(operation),
                                CCAlgorithm(kCCAlgorithmAES),
                                options,
                                keyBytes, keyLength,
                                ivBytes,
                                dataBytes, dataLength,
                                cryptBytes, cryptLength,
                                &numBytesEncrypted)
                    }
                }
            }
        }

        if UInt32(cryptStatus) == UInt32(kCCSuccess) {
            cryptData.removeSubrange(numBytesEncrypted..<cryptData.count)

        } else {
            print("Error: \(cryptStatus)")
        }

        return cryptData;
    }

}




    func testAES() -> Bool {

        let message     = "secret message"
        let key         = "key890123456"
        let ivString     = "abcdefghijklmnop"   // 16 bytes for AES128

        let messageData = message.data(using:String.Encoding.utf8)!
        let keyData     = key.data(using: .utf8)!
        let ivData      = ivString.data(using: .utf8)!

        let encryptedData = messageData.aesEncrypt( keyData:keyData, ivData:ivData, operation:kCCEncrypt)
        let decryptedData = encryptedData.aesEncrypt( keyData:keyData, ivData:ivData, operation:kCCDecrypt)
        let decrypted     = String(bytes:decryptedData, encoding:String.Encoding.utf8)!

        return message == decrypted

    }
Run Code Online (Sandbox Code Playgroud)