【问题标题】:AES Padding issue in iOS but works fine in AndroidiOS 中的 AES 填充问题,但在 Android 中运行良好
【发布时间】:2016-01-06 02:38:20
【问题描述】:

我正在尝试在 iOS 中使用 AES/CBC 加密。解密由 C# 完成。在 Android 中由 Java 完成的加密工作正常。我收到错误“填充无效,无法删除。”在尝试使用 iOS 代码时。请帮忙。

请在下面找到 C#、Java 和 Objective C 代码。

C#代码:

namespace InternetMobileService
{
    using System;
    using System.IO;
    using System.Security.Cryptography;
    using System.Text;
    using System.Xml;


    public class EncryptDecrypt
    {
        //// Replace me with a 16-byte key, share between Java and C#

        /// <summary>
        /// rawSecretKey values
        /// </summary>
        private static byte[] rawSecretKey = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

        /// <summary>
        /// variable field
        /// </summary>
        private ICryptoTransform rijndaelDecryptor;

        /// <summary>
        /// Initializes a new instance of the <see cref="EncryptDecrypt"/> class.
        /// </summary>
        /// <param name="passphrase">pass phrase</param>
        public EncryptDecrypt(string passphrase)
        {
            byte[] passwordKey = EncodeDigest(passphrase);
            RijndaelManaged rijndael = new RijndaelManaged();
            this.rijndaelDecryptor = rijndael.CreateDecryptor(passwordKey, rawSecretKey);
        }


        public static byte[] EncodeDigest(string text)
        {
            SHA256CryptoServiceProvider x = new System.Security.Cryptography.SHA256CryptoServiceProvider();
            byte[] data = Encoding.ASCII.GetBytes(text);
            return x.ComputeHash(data);
        }


        public static string Encrypt(string plainText)
        {
            using (RijndaelManaged myRijndael = new RijndaelManaged())
            {
                byte[] passwordKey = EncodeDigest(System.Configuration.ConfigurationManager.AppSettings["ConstructorKey"].ToString());
                byte[] encrypted = EncryptStringToBytes(plainText, passwordKey, rawSecretKey);
                return Convert.ToBase64String(encrypted);
            }
        }


        public static byte[] EncryptStringToBytes(string plainText, byte[] key, byte[] iV)
        {
            if (plainText == null || plainText.Length <= 0)
            {
                throw new ArgumentNullException("plainText");
            }

            if (key == null || key.Length <= 0)
            {
                throw new ArgumentNullException("key");
            }

            if (iV == null || iV.Length <= 0)
            {
                throw new ArgumentNullException("key");
            }

            byte[] encrypted;
            using (RijndaelManaged rijAlg = new RijndaelManaged())
            {
                rijAlg.Key = key;
                rijAlg.IV = iV;
                ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
                using (MemoryStream memorystreamEncrypt = new MemoryStream())
                {
                    using (CryptoStream cryptostreamEncrypt = new CryptoStream(memorystreamEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter streamwriterEncrypt = new StreamWriter(cryptostreamEncrypt))
                        {
                            streamwriterEncrypt.Write(plainText);
                        }

                        encrypted = memorystreamEncrypt.ToArray();
                    }
                }
            }

            return encrypted;
        }


        public string Decrypt(byte[] encryptedData)
        {            
            byte[] newClearData = this.rijndaelDecryptor.TransformFinalBlock(encryptedData, 0, encryptedData.Length);
            return Encoding.ASCII.GetString(newClearData);
        }


        public string DecryptFromBase64(string encryptedBase64)
        {
            return this.Decrypt(Convert.FromBase64String(encryptedBase64));
        }
    }
}

Java 代码:

public class Crypto {
    public static final String TAG = Crypto.class.getSimpleName();
    // Replace me with a 16-byte key, share between Java and C#
    private static Cipher aesCipher;
    private static SecretKey secretKey;
    private static IvParameterSpec ivParameterSpec;
    private static String CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding";
    private static String CIPHER_ALGORITHM = "AES";
    private static byte[] rawSecretKey = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

    private static String MESSAGEDIGEST_ALGORITHM = "SHA-256";

    public Crypto(String passphrase) {
        byte[] passwordKey = encodeDigest(passphrase);

        try {
            aesCipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
        } catch (NoSuchAlgorithmException e) {
            Log.e(TAG, "No such algorithm " + CIPHER_ALGORITHM, e);
        } catch (NoSuchPaddingException e) {
            Log.e(TAG, "No such padding PKCS5", e);
        }

        secretKey = new SecretKeySpec(passwordKey, CIPHER_ALGORITHM);
        ivParameterSpec = new IvParameterSpec(rawSecretKey);
    }

    public byte[] decrypt(byte[] clearData) {
        try {
            aesCipher.init(Cipher.DECRYPT_MODE, secretKey);
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        }

        byte[] decryptedData;

        try {
            decryptedData = aesCipher.doFinal(clearData);
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
            return null;
        } catch (BadPaddingException e) {
            e.printStackTrace();
            return null;
        }
        return decryptedData;

    }

    public String decryptAsBase64(byte[] clearData) throws IOException {
        byte[] decryptedData = decrypt(clearData);
        return new String(Base64New.decode(decryptedData));
    }

    public String encryptAsBase64(byte[] clearData) {
        byte[] encryptedData = encrypt(clearData);
        return Base64New.encodeBytes(encryptedData);
    }

    public byte[] encrypt(byte[] clearData) {
        try {
            aesCipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
        } catch (InvalidKeyException e) {
            Log.e(TAG, "Invalid key", e);
            return null;
        } catch (InvalidAlgorithmParameterException e) {
            Log.e(TAG, "Invalid algorithm " + CIPHER_ALGORITHM, e);
            return null;
        }

        byte[] encryptedData;
        try {
            encryptedData = aesCipher.doFinal(clearData);
        } catch (IllegalBlockSizeException e) {
            Log.e(TAG, "Illegal block size", e);
            return null;
        } catch (BadPaddingException e) {
            Log.e(TAG, "Bad padding", e);
            return null;
        }
        return encryptedData;
    }

    private byte[] encodeDigest(String text) {
        MessageDigest digest;
        try {
            digest = MessageDigest.getInstance(MESSAGEDIGEST_ALGORITHM);
            return digest.digest(text.getBytes());
        } catch (NoSuchAlgorithmException e) {
            Log.e(TAG, "No such algorithm " + MESSAGEDIGEST_ALGORITHM, e);
        }

        return null;
    }
}

Obj C 代码:

+ (NSString*)encryptBase64String:(NSString*)string keyString:(NSString*)keyString separateLines:(BOOL)separateLines
{

    const unsigned char rawSectret[] = {
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00};
    NSData* rawScretdata = [NSData dataWithBytes:rawSectret length:kCCBlockSizeAES128];

    NSString *shaKeyString = [self sha256HashFor:keyString];

    NSData *sourceData = [string dataUsingEncoding:NSUTF8StringEncoding];
    NSString *ivString = [[NSString alloc] initWithData:rawScretdata
                                               encoding:NSUTF8StringEncoding];

    NSData* Outdata = [sourceData AES128EncryptedDataWithKey:shaKeyString  iv:ivString];


    NSString *encodedString = [Outdata base64EncodedStringWithSeparateLines:separateLines];

    return encodedString;
}

- (NSData *)AES128Operation:(CCOperation)operation key:(NSString *)key iv:(NSString *)iv
{
    char keyPtr[kCCKeySizeAES128 + 1];
    bzero(keyPtr, sizeof(keyPtr));
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

    char ivPtr[kCCBlockSizeAES128 + 1];
    bzero(ivPtr, sizeof(ivPtr));
    if (iv) {
        [iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
    }

    NSUInteger dataLength = [self length];
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);

    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(operation,
                                          kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding,
                                          keyPtr,
                                          kCCBlockSizeAES128,
                                          ivPtr,
                                          [self bytes],
                                          dataLength,
                                          buffer,
                                          bufferSize,
                                          &numBytesEncrypted);
    if (cryptStatus == kCCSuccess) {
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }
    free(buffer);
    return nil;
}

【问题讨论】:

  • 您显然没有使用 AES-128 (obj-c),而是使用 AES-256,因为密钥是从带有 SH-A256 的密码中派生的。 rawSecretKey 是 IV 的字节数组,因此名称具有误导性。从密码派生的密钥需要比 PBKDF2 更强大,具有一百万次迭代和随机盐。此外,您没有使用 HMAC 验证您的密文。
  • 如果是填充问题,只有最后一个块是不正确的。本质上 PKCS5Padding 和 PKCS7Padding 是相同的,只是 PKCS7Padding 的文档允许更大的块大小。

标签: c# android ios encryption aes


【解决方案1】:

Common Crypto 使用显式密钥大小,许多其他库使用基于提供的密钥的密钥大小,因此您需要确保为 Common Crypto 指定正确的密钥大小,kCCKeySizeAES128kCCKeySizeAES192、@ 之一987654323@,而不是kCCBlockSizeAES128。在这种情况下,由于密钥是使用 SHA-256 派生的,kCCKeySizeAES256 是应该指定的密钥大小。

C# 使用 Rijndael,因此您必须确保指定的块大小为 128,这是 AES 唯一支持的块大小。

只要输入相同,输出就会匹配。它们是选项(模式和填充)、键、键大小、数据和 iv。用十六进制检查它们。

提供测试向量:所有三个十六进制的键、输入数据和输出数据,以便我们测试代码。

【讨论】:

  • 我应该在哪里改变?我可以看到多次出现 kCCBlockSizeAES128
  • 在需要块大小的地方使用kCCBlockSizeAES128,在需要密钥的地方使用kCCKeySizeAES256(或其他适当的大小)。请参阅 CCCrypt 文档(头文件)。
猜你喜欢
  • 2015-06-22
  • 1970-01-01
  • 1970-01-01
  • 2023-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-25
相关资源
最近更新 更多