【问题标题】:CryptoSwift + CryptoJS making wrong JSON in Swift 2.3CryptoSwift + CryptoJS 在 Swift 2.3 中生成错误的 JSON
【发布时间】:2016-10-25 05:30:14
【问题描述】:
do {
let JSONObject:[String:String] = 
[
"username" : "username",
"password" : "Password",
"domain": "domain"
]
let my64data:NSData = NSData(base64EncodedString:Credentials.SecretKey, options: NSDataBase64DecodingOptions(rawValue: UInt(0)))!

let jsonData = try NSJSONSerialization.dataWithJSONObject(JSONObject, options: NSJSONWritingOptions.PrettyPrinted)

let myString = String(data: jsonData, encoding: NSUTF8StringEncoding)

    // let jsonString = AES1.encrypt(myString!, secretKey: Credentials.SecretKey, options:["iv":my64data])
    // print(" My Encrypted Json = \(jsonString)")
    //            
    // let Decrypt = AES1.decrypt(jsonString, secretKey: Credentials.SecretKey, options: ["iv":my64data])
    //print(" My Decrypted Json = \(Decrypt)")

let iv: Array<UInt8> = AES.randomIV(128/8)
let salt: Array<UInt8> = AES.randomIV(128/8)

let value = try! PKCS5.PBKDF2(password: Credentials.SecretKey.utf8.map({$0}), salt: salt, iterations: 1000, variant: .sha256).calculate()
                value.toHexString()

                _ = CryptoJS.mode.ECB()
                _ = CryptoJS.pad.Iso97971()
                _ = CryptoJS.pad.AnsiX923()
                _ = CryptoJS.pad.Iso10126()
                _ = CryptoJS.pad.ZeroPadding()

                let encrypted : Array<UInt8>

                     encrypted = try AES(key: value, iv: iv, blockMode: .CBC, padding: PKCS7()).encrypt((myString?.utf8.map({$0}))!)
                    let decrypted = try AES(key: value, iv: iv, blockMode: .CBC, padding: PKCS7()).decrypt(encrypted)

    //            let jsonString = AES1.encrypt(myString!, secretKey: String(value), options:["iv":iv.toHexString(),"mode":CryptoJS.mode().ECB,"padding":CryptoJS.pad().ZeroPadding])
                print(" My Encrypted Json = \(encrypted.toHexString())")
    //            
    //            let pkcs = PKCS7()
    //            
    //            let Decrypt = AES1.decrypt(jsonString, secretKey: String(value), options: ["iv":iv.toHexString(),"mode":CryptoJS.mode().ECB,"padding":pkcs])
               print(" My Decrypted Json = \(decrypted.toHexString())")

                let myInputIV : String = String(iv.toHexString())
                let myInputSalt :String = String(salt.toHexString())

                let finalJSONObject:[String:String] = [

                    "ciphertext" : "\(encrypted.toHexString())",
                    "iv" : "\(myInputIV)",
                    "salt": "\(myInputSalt)"

                ]

    print("Final Json Object = \(finalJSONObject)")

                let requestURL: NSURL = NSURL(string:"<myURL>")!
                print(requestURL)
                let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
                urlRequest.HTTPMethod = "POST"
                urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
                urlRequest.HTTPBody = try!NSJSONSerialization.dataWithJSONObject(finalJSONObject, options:.PrettyPrinted)

                let session = NSURLSession.sharedSession()
                let task = session.dataTaskWithRequest(urlRequest, completionHandler:{
                    data, response, error -> Void in

                    // Asynchronously call...
                    if (data != nil) {

                        print("Dataaa = \(data!)")

                        self.loginServiceResponse(data!)

                    }else{

                        Singleton.SharedInstance.myAlert(alertTitle:Constants.SERVER_ERROR_TITLE, alertMessage: Constants.SERVER_ERROR_MESSAGE, alertButtonTitle:Constants.OK)
                        self.activityIndicator.stopAnimating()

                    }


                })

                task.resume()

            } catch {
                print(error)

            }

我收到了这个错误:

Json 错误域 = NSCocoaErrorDomain 代码 = 3840 “字符 0 周围的值无效。” UserInfo={NSDebugDescription=字符 0 周围的值无效。}

【问题讨论】:

  • 在邮递员中我得到了填充错误
  • 最好避免使用 CryptoSwift,除此之外,它比基于 Common Crypto 的实现慢 500 到 1000 倍。 Apple 的 Common Crypto 已通过 FIPS 认证,因此经过严格审查,使用 CryptoSwift 在正确性和安全性方面是有机会的。
  • minimal reproducible example 更新代码,只需要输入和输出的加密部分。首先让简单的加密工作,然后添加 JSON,然后添加通信。在工作代码上增量构建,“大爆炸”方法很难调试。
  • AES(key: value, iv: iv, blockMode: .CBC, padding: PKCS7()).encrypt((myString?.utf8.map({$0}))!)AES1.encrypt(myString!, secretKey: String(value), options:["iv":iv.toHexString(),"mode":CryptoJS.mode().ECB,"padding":CryptoJS.pad().ZeroPadding]) 这样的复合语句很难理解和调试,因为中间值不可用。带有中间变量的多个更简单的语句更容易,并且只依赖编译器来优化代码。可读性是代码的第一名。
  • @zaph:你有什么常见的加密安装技巧吗?你能帮我吗

标签: ios swift encryption swift2 cryptojs


【解决方案1】:

在 Swift 中使用 Common Crypto

  1. 添加桥接头
  2. 在标题中添加#import &lt;CommonCrypto/CommonCrypto.h&gt;

该示例使用CBC模式,并在加密数据前加上IV,这是一种通常用于处理IV的方法。

已弃用文档部分的示例:

CBC 模式下的 AES 加密与随机 IV (Swift 3.0)

iv 是加密数据的前缀

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

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

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

PKCS#7 padding是默认设置的。

此示例需要 Common Crypto
项目必须有桥接头:
#import &lt;CommonCrypto/CommonCrypto.h&gt;
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;
}

示例用法:

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)")
}

示例输出:

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>

注意事项:
CBC 模式示例代码的一个典型问题是,它将随机 IV 的创建和共享留给用户。此示例包括生成 IV、为加密数据添加前缀并在解密期间使用前缀 IV。这将临时用户从CBC mode 所需的详细信息中解放出来。

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

还缺少从密码中导出密钥的密钥,建议使用PBKDF2,将文本密码用作密钥材料。

有关强大的生产就绪多平台加密代码,请参阅RNCryptor

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-15
    • 1970-01-01
    相关资源
    最近更新 更多