SWIFT AES加密和解密-获得不同的结果

时间:2018-11-07 18:27:22

标签: ios swift aes

我正在迅速实施AES加密。 Java和C#的加密解密工作正常。

我很快就得到了与实际结果不同的结果。 调试时,我注意到Java默认使用sign int。 因此,我实现了相同的方式,从而能够验证两个应用程序(Java和Swift)中的derivedKey是否相同。 但是在创建keyData和ivData时,它会丢失签名的数据。不确定是不是造成了问题。

我尝试了以下AES Encryption .net to swift中解释的代码

func decrypt(encryptedText: String, keys :String)  -> String{
    let encryptedData = encryptedText.data(using: .utf16LittleEndian)
    let derivedKey = generateDerivedKey(keyString: keys)
    let key = Array(derivedKey[0..<32])
    let iv = Array(derivedKey[32..<48])   
    let keyData = Data(bytes: key, count: key.count)
    let ivData = Data(bytes: iv, count: iv.count)        
    let decryptedData = testDeCrypt(data: encryptedData!, keyData: keyData, ivData: ivData, operation: kCCDecrypt)      

    return String(bytes: decryptedData, encoding: .unicode)!
}

func generateDerivedKey(keyString :String) -> [Int8] {
    let salt: [UInt8] = [0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76]
    var key = [UInt8](repeating: 0, count: 48)
    CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2), keyString, keyString.utf8.count, salt, salt.count, CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1), 1000, &key, 48)
    let derivedKey : [Int8] = key.map {Int8(bitPattern: $0)}

    return derivedKey
}

func testDeCrypt(data: Data, keyData: Data, ivData: Data, operation: Int) -> Data {
    assert(keyData.count == Int(kCCKeySizeAES128) || keyData.count == Int(kCCKeySizeAES192) || keyData.count == Int(kCCKeySizeAES256))
    var decryptedData = Data(count: data.count)
    var num_bytes_decrypted: size_t = 0
    let operation = CCOperation(operation)
    let algoritm = CCAlgorithm(kCCAlgorithmAES)
    let options = CCOptions(kCCOptionPKCS7Padding)  
    let decryptedDataCount = decryptedData.count
    let cryptoStatus = keyData.withUnsafeBytes {keyDataBytes in
        ivData.withUnsafeBytes {ivDataBytes in
            data.withUnsafeBytes {dataBytes in
                decryptedData.withUnsafeMutableBytes {decryptedDataBytes in
                    CCCrypt(operation, algoritm, options, keyDataBytes, keyData.count, ivDataBytes, dataBytes, data.count, decryptedDataBytes, decryptedDataCount, &num_bytes_decrypted)
                }
            }
        }
    }      
    if cryptoStatus == CCCryptorStatus(kCCSuccess) {
        decryptedData.count = num_bytes_decrypted
        return decryptedData
    } else {
        return Data()
    }
}

Java代码

public static String aesDecrypt(String text, String key) {
    byte[] decValue = null;
    try {
        byte[] salt = new byte[] { 0x49, 0x76, 0x61, 0x6E, 0x20, 0x4D,
                0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 };
        SecretKeyFactory factory = SecretKeyFactory
                .getInstance("PBKDF2WithHmacSHA1");
        PBEKeySpec pbeKeySpec = new PBEKeySpec(key.toCharArray(), salt,
                1000, 384);

        Key secretKey = factory.generateSecret(pbeKeySpec);
        byte[] keys = new byte[32];
        byte[] iv = new byte[16];
        System.arraycopy(secretKey.getEncoded(), 0, keys, 0, 32);
        System.arraycopy(secretKey.getEncoded(), 32, iv, 0, 16);

        SecretKeySpec secretSpec = new SecretKeySpec(keys, "AES");

        AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

        try {
            cipher.init(Cipher.DECRYPT_MODE, secretSpec, ivSpec);
        } catch (InvalidKeyException e) {

        } catch (InvalidAlgorithmParameterException e) {
        }

        org.apache.commons.codec.binary.Base64 decoder = new org.apache.commons.codec.binary.Base64();
        byte[] decodedValue = decoder.decode(text.getBytes());

        decValue = cipher.doFinal(decodedValue);

    } catch (Exception e) {
    }

    if (decValue != null) {
        return new String(decValue, Charset.forName("UTF_16LE"));
    } else {
        return null;
    }
}

测试数据 密钥:“ ThisIsATestPassword444Encryption” 文字:“ TestStringToEncrypt”

Java输出 编码的密文文本:[97,47,77,79,118,111,79,70,47,87,90,67,81,98,51,74,83,88,97,68,84,105,72 ,71、67、121、122、86、81、116、106、104、117、78、108、118、49、48、65、77、69、53、114、43、120、104、89、120 ,50、98、80、66、50、77、87、80、103、110、117、118、118、97、78、106]

加密文本:a / MOvoOF / WZCQb3JSXaDTiHGCyzVQtjhuNlv10AME5r + xhYx2bPB2MWPgnuvvaNj

解密text.getbytes:[97,47,77,79,118,111,79,70,47,87,90,67,81,98,51,74,83,88,97,68,84 ,105、72、71、67、121、122、86、81、116、106、104、117、78、108、118、49、48、65、77、69、53、114、43、120、104 ,89、120、50、98、80、66、50、77、87、80、103、110、117、118、118、97、78、106]

解码后的解密文本:[107,-13、14,-66,-125,-123,-3、102、66、65,-67,-55、73、118,-125、78、33, -58、11、44,-43、66,-40,-31,-72,-39、111,-41、64、12、19,-102,-2,-58、22、49,-39 ,-77,-63,-40,-59,-113,-126,123,-81,-67,-93,99]

快速输出: encryptionText:a / MOvoOF / WZCQb3JSXaDTiHGCyzVQtjhuNlv10AME5r + xhYx2bPB2MWPgnuvvaNj

decryptedText:۽瑒왿᪰߆牷ྐྵ䐫徺ꋴ锏ݐ斑ﷃ翴㦦જ㤉ꄕ䞴櫘勐鼍ᐏ┓ീ学䥏㿖칵斗솽ᢼ铡鴷⤃ꗞ䛂䋗 쿠蒻⯨䍊䂷篥럟⤫俷违둘๔Ꞵ‵

Swift和Java加密匹配。

非常感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

Swift代码中最糟糕的两部分是:

#1

let encryptedData = encryptedText.data(using: .utf16LittleEndian)

和:

#2

return String(bytes: decryptedData, encoding: .unicode)!

#1

在您的Java代码中,您将text解码为Base-64,但是在您的Swift代码中,您仅获得了.utf16LittleEndian的字节表示,这与Base-无关。 64。

您可能需要这样的东西:

guard let encryptedData = Data(base64Encoded: encryptedText) else {
    print("Data is not a valid base-64")
    return nil
}

(您的decrypt(encryptedText:keys:)应该返回String?而不是String,因为解密可能会失败。)


#2

在Java代码中,您使用new String(decValue, Charset.forName("UTF_16LE"))将解密的字节转换为String。 UTF_16LE代表UTF-16 Little Endian。 Swift String.Encoding中的等效项是utf16LittleEndian

该行应如下所示:

return String(bytes: decryptedData, encoding: .utf16LittleEndian)

使用generateDerivedKey(keyString:)作为其返回类型时,[UInt8]可以简化。 (您最好使用UInt8来表示Swift中的中间字节类型。)

所有这些东西结合在一起,您的Swift代码应该是:

func decrypt(encryptedText: String, keys: String)  -> String? { //### `String?` rather than `String`
    //### Decode `encryptedText` as Base-64
    guard let encryptedData = Data(base64Encoded: encryptedText) else {
        print("Data is not a valid Base-64")
        return nil
    }
    let derivedKey = generateDerivedKey(keyString: keys)
    //### A little bit shorter, when `derivedKey` is of type `[UInt8]`
    let keyData = Data(bytes: derivedKey[0..<32])
    let ivData = Data(bytes: derivedKey[32..<48])
    if let decryptedData = testDeCrypt(data: encryptedData, keyData: keyData, ivData: ivData, operation: kCCDecrypt) {
        //### Use `utf16LittleEndian`
        return String(bytes: decryptedData, encoding: .utf16LittleEndian)
    } else {
        //### return nil, when `testDeCrypt` fails
        return nil
    }
}

func generateDerivedKey(keyString: String) -> [UInt8] { //### `[UInt8]`
    let salt: [UInt8] = [0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76]
    var key = [UInt8](repeating: 0, count: 48)
    CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2), keyString, keyString.utf8.count, salt, salt.count, CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1), 1000, &key, 48)

    //### return the Array of `UInt8` directly
    return key
}

func testDeCrypt(data: Data, keyData: Data, ivData: Data, operation: Int) -> Data? { //### make it Optional
    assert(keyData.count == Int(kCCKeySizeAES128) || keyData.count == Int(kCCKeySizeAES192) || keyData.count == Int(kCCKeySizeAES256))
    var decryptedData = Data(count: data.count)
    var numBytesDecrypted: size_t = 0
    let operation = CCOperation(operation)
    let algoritm = CCAlgorithm(kCCAlgorithmAES)
    let options = CCOptions(kCCOptionPKCS7Padding)
    let decryptedDataCount = decryptedData.count
    let cryptoStatus = keyData.withUnsafeBytes {keyDataBytes in
        ivData.withUnsafeBytes {ivDataBytes in
            data.withUnsafeBytes {dataBytes in
                decryptedData.withUnsafeMutableBytes {decryptedDataBytes in
                    CCCrypt(operation, algoritm, options, keyDataBytes, keyData.count, ivDataBytes, dataBytes, data.count, decryptedDataBytes, decryptedDataCount, &numBytesDecrypted)
                }
            }
        }
    }
    if cryptoStatus == CCCryptorStatus(kCCSuccess) {
        decryptedData.count = numBytesDecrypted
        return decryptedData
    } else {
        return nil //### returning `nil` instead of `Data()`
    }
}

使用上面的新Swift代码,我可能会生成与您的Java代码相同的结果:

let test = "a/MOvoOF/WZCQb3JSXaDTiHGCyzVQtjhuNlv10AME5r+xhYx2bPB2MWPgnuvvaNj"
let keys = "ThisIsATestPassword444Encryption"

if let result = decrypt(encryptedText: test, keys: keys) {
    print(result) //->TestStringToEncrypt
} else {
    print("*Cannot decrypt*")
}

(我需要更新旧的Java环境以比较Java和Swift之间的中间结果,但这是另一回事了……)

答案 1 :(得分:0)

我遇到了同样的问题,并且通过检查缓冲区uint数组中的第一项是否为“ 0x00”(如果未在索引0处添加“ 0x00”)来解决此问题 这是接收加密数据并在解密后再次发送的示例

    let rsaKeyValue = xmlRep["RSAKeyValue"]
    let modulus = rsaKeyValue["Modulus"].element?.text
    let exponent = rsaKeyValue["Exponent"].element?.text


    var modBuffer: [UInt8] = [UInt8](Data(base64Encoded: modulus!)!)
    let expBuffer: [UInt8] = [UInt8](Data(base64Encoded: exponent!)!)

    if let prefix = modBuffer.first, prefix != 0x00 {
        modBuffer.insert(0x00, at: 0)
    }



    let modulusEncoded: [UInt8] = modBuffer.encodeAsInteger()
    let exponentEncoded: [UInt8] = expBuffer.encodeAsInteger()

    let sequenceEncoded: [UInt8] = (modulusEncoded + exponentEncoded).encodeAsSequence()

    let keyData = Data(bytes: sequenceEncoded)

    let keySize = (modBuffer.count * 8)

    let attributes: [String: Any] = [
        kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
        kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
        kSecAttrKeySizeInBits as String: keySize,
        kSecAttrIsPermanent as String: false
    ]

    var err : Unmanaged<CFError>?
    let publicKey = SecKeyCreateWithData(keyData as CFData, attributes as CFDictionary, &err)

    guard let tokenData = Authentication.getUserToken()?.data(using: .utf8)  else { return }


    let chunks = tokenData.toUInt8Array().chunked(into: 200)
    var encryptedChunks = [[UInt8]]()

    for chunk in chunks
    {
        var encryptionError: Unmanaged<CFError>?

        let cipher = SecKeyCreateEncryptedData(publicKey!, .rsaEncryptionPKCS1, Data(bytes: chunk) as CFData, &encryptionError)
        encryptedChunks.append([UInt8](cipher! as Data))
    }


    var str = "["
    for chunk in encryptedChunks {
        for byte in chunk {
            str.append("\(byte),")
        }
        str.remove(at: String.Index(encodedOffset: str.count - 1))
        str.append(";")
    }
    str.append("]")
    let finalStr = str.replacingOccurrences(of: ";]", with: "]")

这是用于快速加密的扩展

internal extension Array where Element == UInt8 {

func encodeAsInteger() -> [UInt8] {
    var tlvTriplet: [UInt8] = []
    tlvTriplet.append(0x02)
    tlvTriplet.append(contentsOf: lengthField(of: self))
    tlvTriplet.append(contentsOf: self)

    return tlvTriplet
}

func encodeAsSequence() -> [UInt8] {
    var tlvTriplet: [UInt8] = []
    tlvTriplet.append(0x30)
    tlvTriplet.append(contentsOf: lengthField(of: self))
    tlvTriplet.append(contentsOf: self)

    return tlvTriplet
}

func chunked(into size: Int) -> [[Element]] {
    return stride(from: 0, to: count, by: size).map {
        Array(self[$0 ..< Swift.min($0 + size, count)])
    }
}

}