Итак, у меня есть некоторый код в 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=