Расшифровка AES / CBC / PKCS5Padding в iOS

#ios #encryption #aes

#iOS #шифрование #aes

Вопрос:

У меня есть файл, который был зашифрован на Android с помощью этого кода:

 import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class AESUtils {
    private static final String IV_STRING = "123456789876543";
    private String key = "mysecretkey12345";

    public static byte[] encryptData(String key, byte[] byteContent) {
        byte[] encryptedBytes = null;
        try {
            byte[] enCodeFormat = key.getBytes();
            SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES");
            byte[] initParam = IV_STRING.getBytes();
            IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
            encryptedBytes = cipher.doFinal(byteContent);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return encryptedBytes;
    }
    public static byte[] decryptData(String key, byte[] encryptedBytes) {
        byte[] result = null ;
        try {
            byte[] sEnCodeFormat = key.getBytes();
            SecretKeySpec secretKey = new SecretKeySpec(sEnCodeFormat, "AES");
            byte[] initParam = IV_STRING.getBytes();
            IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
            result = cipher.doFinal(encryptedBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resu<
    }
}
  

Я попытался перепроектировать дешифрование в Swift, используя CommonCrypto следующим образом:

 import CommonCrypto

let keyStr:String = "mysecretkey12345"
let ivStr:String = "123456789876543"    

func aesDecrypt(data:NSData) -> Data? {
        let k:NSData = keyStr.data(using: .utf8)! as NSData
        let dbytes = data.bytes
        let kbytes=k.bytes
    
        if let keyData = keyStr.data(using: .utf8),
           let cryptData    = NSMutableData(length: Int((data.length))   kCCBlockSizeAES128) {
                
                let keyLength              = size_t(kCCKeySizeAES128)
                let operation: CCOperation = UInt32(kCCDecrypt)
                let algoritm:  CCAlgorithm = UInt32(kCCAlgorithmAES128)
                let options:   CCOptions   = UInt32(kCCOptionPKCS7Padding)
                
                var numBytesEncrypted :size_t = 0
                
                let cryptStatus = CCCrypt(operation,
                    algoritm,
                    options,
                    kbytes, keyLength,
                    ivStr,
                    dbytes, data.length,
                    cryptData.mutableBytes, cryptData.length,
                    amp;numBytesEncrypted)
                
                if UInt32(cryptStatus) == UInt32(kCCSuccess) {
                    cryptData.length = Int(numBytesEncrypted)
                    return (cryptData.copy() as! Data)
                }
                else {
                    return nil
                }
        }
        return nil
    }
  

Я новичок в шифровании, но из своих исследований я обнаружил, что CC использует CBC по умолчанию, а PKCS7Padding в основном идентичен PKCS5Padding. Однако дешифрование не дает ожидаемых результатов!
Код swift был скопирован из разных источников, включая множество решений, предложенных здесь, в stackoverflow. Основная проблема заключается в том, что в большинстве примеров в качестве данных используются key и iv, тогда как у меня есть строки — не уверен, что мое преобразование вызывает проблемы. Во-вторых, многие просто преобразуют строковые сообщения, тогда как я конвертирую данные (из файлов) напрямую — это не должно слишком сильно влиять на это, на самом деле упрощает код, избегая преобразования данных-> строки.
Но поскольку это не работает, что я пропустил?

Ответ №1:

Хорошо, я думаю, я понял проблему. Очевидно, что существует проблема с NSData.bytes, и нужно работать с unsafebytes. Кроме того, моя проблема может заключаться в том, что IV не был частью данных, как предполагалось во многих примерах, поэтому я пропустил 16 байт при расшифровке. Следующий код работает для меня, надеюсь, он кому-нибудь поможет!

 func decrypt(data: Data) -> Data {
    let key: Data = keyStr.data(using: .utf8) ?? Data()
    let iv: Data = ivStr.data(using: .utf8) ?? Data()
        
    if(keyStr.count == kCCKeySizeAES128){print("Key OKAY")} else {print("Key NOT okay")}
    if(ivStr.count == kCCBlockSizeAES128){print("IV OKAY")} else {print("IV NOT okay")}
    
    var buffer = Data(count: data.count)

    var numberBytesDecrypted: Int = 0

    let cryptStatus: CCCryptorStatus = key.withUnsafeBytes {keyBytes in
        data.withUnsafeBytes {dataBytes in
            buffer.withUnsafeMutableBytes {bufferBytes in iv.withUnsafeBytes {ivBytes in
                CCCrypt(         // Stateless, one-shot encrypt operation
                    CCOperation(kCCDecrypt),                        // op: CCOperation
                    CCAlgorithm(kCCAlgorithmAES128),                // alg: CCAlgorithm
                    CCOptions(kCCOptionPKCS7Padding),                                        // options: CCOptions
                    keyBytes.baseAddress,                           // key: the "password"
                    key.count,                                      // keyLength: the "password" size
                    ivBytes.baseAddress,                          // iv: Initialization Vector
                    dataBytes.baseAddress!,    // dataIn: Data to decrypt bytes
                    data.count,                                     // dataInLength: Data to decrypt size
                    bufferBytes.baseAddress,                        // dataOut: decrypted Data buffer
                    data.count,                                     // dataOutAvailable: decrypted Data buffer size
                    amp;numberBytesDecrypted                           // dataOutMoved: the number of bytes written
                )}
            }
        }
    }
    
    if(cryptStatus == CCCryptorStatus(kCCSuccess)){
        return buffer[..<numberBytesDecrypted]
    } else {
        print("Decryption failed")
        return data
    }
}
  

Ответ №2:

Хорошо, оказалось, что один из моих тестовых файлов был поврежден, и моя расшифровка работала все время. Вот более простая версия без withUnsafeBytes:

 func decrypt(data: Data)  -> Data {
    let key:Data = keyStr.data(using: .utf8)!
    let iv:Data = ivStr.data(using: .utf8)!

    var buffer = [UInt8](repeating: 0, count: data.count   kCCBlockSizeAES128)
    var bufferLen: Int = 0

    let status = CCCrypt(
        CCOperation(kCCDecrypt),
        CCAlgorithm(kCCAlgorithmAES128),
        CCOptions(kCCOptionPKCS7Padding),
        [UInt8](key),
        kCCBlockSizeAES128,
        [UInt8](iv),
        [UInt8](data),
        data.count,
        amp;buffer,
        buffer.count,
        amp;bufferLen
    )

    if(status == kCCSuccess) {
        return Data(bytes: buffer, count: bufferLen)}
    else {
        print("Decryption failed")
        return data
    }
}