Исключение плохого заполнения при расшифровке с использованием заполнения AES CB C PCKS7 - PullRequest
0 голосов
/ 01 мая 2020

Итак, у меня есть некоторый код в Java для шифрования и дешифрования объектов, и я пытался сделать то же самое в C#, чтобы мой сервер и клиент могли расшифровывать и шифровать сообщения. На данный момент последняя часть головоломки, которую мне нужно исправить, это моя расшифровка C#. Когда я пытаюсь расшифровать, я обычно получаю плохое исключение заполнения. Однако Java использует отступы PCKS5, а C# использует отступы PCKS7, которые, если я правильно понимаю, практически неразличимы.

@RequiredArgsConstructor
@Getter
public class EncryptedBytes {

    private final byte[] data;
    private final byte[] params;
    private final String paramAlgorithm;

}

    /**
     * Encrypt object with password
     *
     * @param data   Object to be encrypted
     * @param secret Password to use for encryption
     * @return Encrypted version of object
     */
    public static EncryptedBytes encrypt(String data, SecretKey secret) throws InvalidKeyException {

        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secret);

            // properly encode the complete ciphertext
            //logEncrypt(password, object);

            byte[] encodedData = cipher.doFinal(data.getBytes(CharsetUtil.UTF_8));
            byte[] params = cipher.getParameters().getEncoded();
            String paramAlgorithm = cipher.getParameters().getAlgorithm();

            return new EncryptedBytes(encodedData, params, paramAlgorithm);
        } catch (InvalidKeyException e) {
            throw e;
        } catch (NoSuchAlgorithmException | IllegalBlockSizeException | NoSuchPaddingException | BadPaddingException | IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Decrypt data with secret
     *
     * @param encryptedBytes Object to be decrypted
     * @param secret         Password to use for decryption
     * @return Decrypted version of object
     */
    public static String decrypt(EncryptedBytes encryptedBytes, @NonNull SecretKey secret) throws InvalidKeyException {
        try {

            // get parameter object for password-based encryption
            AlgorithmParameters algParams = AlgorithmParameters.getInstance(encryptedBytes.getParamAlgorithm()); // getParamAlgorithm usually just returns "AES"


            if (algParams == null) throw new IllegalArgumentException("EncryptedBytes.Parameters are not valid");

            // initialize with parameter encoding from above
            algParams.init(encryptedBytes.getParams());

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secret, algParams);

            return new String(cipher.doFinal(encryptedBytes.getData()), CharsetUtil.UTF_8);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | IOException | InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }
        return null;
    }

Теперь в C# у меня есть очень близкая реализация к приведенному выше коду Java.

    public class EncryptedBytes
    {
        [NotNull]
        public List<sbyte> data { get; }

        [NotNull]
        [JsonProperty("params", Required = Required.Always)]
        [JsonPropertyName("params")]
        public List<sbyte> keyParams { get;  }

        [NotNull]
        public string paramAlgorithm { get; }

        public EncryptedBytes(IEnumerable<sbyte> data, [JsonProperty(propertyName: "params")] IEnumerable<sbyte> keyParams, string paramAlgorithm)
        {
            this.data = data.ToList();
            this.keyParams = keyParams.ToList();
            this.paramAlgorithm = paramAlgorithm;
        }
    }

       public static EncryptedBytes encrypt(AesCryptoServiceProvider aesCryptoServiceProvider, string plainText,
            Encoding encoding)
        {
            if (encoding == null) encoding = StaticHandler.encoding; // UTF_8

            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");
            if (aesCryptoServiceProvider.Key == null || aesCryptoServiceProvider.Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (aesCryptoServiceProvider.IV == null || aesCryptoServiceProvider.IV.Length <= 0)
                throw new ArgumentNullException("IV");


            ICryptoTransform transform = aesCryptoServiceProvider.CreateEncryptor();

            var encodedText = encoding.GetBytes(plainText);
            var encryptedText =
                transform.TransformFinalBlock(encodedText, 0, encodedText.Length).Select(Convert.ToSByte);

            var derOctetString = new DerOctetString(aesCryptoServiceProvider.IV);


            return new EncryptedBytes(encryptedText, derOctetString.GetEncoded().Select(Convert.ToSByte), "AES");
        }

        public static string decrypt([NotNull] EncryptedBytes encryptedBytes, AesCryptoServiceProvider aesCryptoServiceProvider, Encoding? encoding)
        {
            if (encoding == null) encoding = StaticHandler.encoding;

            var cipherText = encryptedBytes.data.Select(arg => (byte) arg).ToArray();

            // Check arguments.
            if (cipherText == null || cipherText.Length <= 0)
                throw new ArgumentNullException("cipherText");
            if (aesCryptoServiceProvider.Key == null || aesCryptoServiceProvider.Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (aesCryptoServiceProvider.IV == null || aesCryptoServiceProvider.IV.Length <= 0)
                throw new ArgumentNullException("IV");

            // Declare the string used to hold
            // the decrypted text.

            aesCryptoServiceProvider.Padding = PaddingMode.PKCS7;

            var transform = aesCryptoServiceProvider.CreateDecryptor();
            string plaintext;

            var keyParams = encryptedBytes.keyParams.Select(b => (byte) b).ToArray();

            var octetString = (DerOctetString) Asn1Object.FromByteArray(keyParams);

            aesCryptoServiceProvider.IV = octetString.GetOctets();
            aesCryptoServiceProvider.Padding = PaddingMode.PKCS7;

            plaintext = encoding.GetString(transform.TransformFinalBlock(cipherText, 0, cipherText.Length));

            return plaintext;

        }

Основной код, о котором идет речь, такой:

            var transform = aesCryptoServiceProvider.CreateDecryptor();
            string plaintext;

            var keyParams = encryptedBytes.keyParams.Select(b => (byte) b).ToArray();

            var octetString = (DerOctetString) Asn1Object.FromByteArray(keyParams);

            aesCryptoServiceProvider.IV = octetString.GetOctets();
            aesCryptoServiceProvider.Padding = PaddingMode.PKCS7;

            plaintext = encoding.GetString(transform.TransformFinalBlock(cipherText, 0, cipherText.Length));

В целях тестирования вы можете использовать это значение в качестве примера анализируемых данных (класс EncryptedBytes преобразован в JSON)

{"data":[-91,-105,-48,-75,18,-64,-12,-122,56,76,17,101,54,65,53,-10],"params":[4,16,81,-32,-95,-80,-81,100,-81,58,49,-36,35,21,-115,-110,111,-2],"paramAlgorithm":"AES"}

Это должно просто возвращать пустые скобки в виде открытого текста. Ожидаемый результат:

{}

Вы можете использовать этот ключ: (оба значения являются одним и тем же ключом, написанным по-разному)

Byte array: 15,215,133,73,75,92,34,113,122,135,36,209,240,159,54,165,30,23,86,73,166,7,166,192,209,145,68,167,19,205,105,55
Base64: D9eFSUtcInF6hyTR8J82pR4XVkmmB6bA0ZFEpxPNaTc=
...