SWIFT AES Encrypt and Decrypt - получает разные результаты - PullRequest
0 голосов
/ 07 ноября 2018

Я в процессе реализации шифрования AES в быстром. Расшифровка шифрования для Java и C # работает правильно.

В быстром я получаю результаты, отличные от реальных. Во время отладки я заметил, что Java по умолчанию использует знак int. Таким образом, я реализовал один и тот же способ, с помощью которого я могу проверить, что производная ключ одинаков в обоих приложениях (Java и Swift). Но при создании 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]

Свифт Выход: encryptedText: a / MOvoOF / WZCQb3JSXaDTiHGCyzVQtjhuNlv10AME5r + xhYx2bPB2MWPgnuvvaNj

расшифрованный текст: ۽ 瑒 왿 ᪰߆ ྐྵ ྐྵ 䐫 ꋴ ꋴ 쿠 蒻 ⯨ 䍊 䂷  篥 럟 ⤫ 俷 違 둘 4Ꞵ`

совпадения шифрования Swift и Java.

Любая помощь очень ценится.

Ответы [ 2 ]

0 голосов
/ 10 ноября 2018

Две худшие части вашего кода 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")) для преобразования расшифрованных байтов в строку. UTF_16LE обозначает UTF-16 Little Endian. Эквивалент в String.Encoding Swift составляет 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, но это другая история ...)

0 голосов
/ 07 ноября 2018

Я столкнулся с той же проблемой, и она устранена путем проверки первого элемента в массиве буферов uint, если это «0x00», если нет, добавить «0x00» в индекс 0 Вот пример получения зашифрованных данных и их повторной отправки после расшифровки

    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: "]")

вот расширения для шифрования в swift

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

}

...