【问题标题】:Encrypt / Decrypt data with AES between c# and PHP - decrypted data starts with 255,254在 c# 和 PHP 之间使用 AES 加密/解密数据 - 解密数据以 255,254 开头
【发布时间】:2015-11-20 19:46:10
【问题描述】:

我必须从用 C# 编写的外部现有 Web 服务请求数据。 这个webservice需要对部分数据进行加密(连接使用SSL连接,部分数据经过aes加密)

在 php 站点上,openssl 用于解密。

c#站点上使用了以下设置 (这是 AesCryptoServiceProvider 的默认值):

  • 算法:AES
  • 填充:PKCS7
  • 模式:CBC
  • 密钥大小:256

PKCS7 的填充工作如下: 01 如果缺少 1 个字节 02 02 如果缺少 2 个字节 等等

所以这个值不会被填充添加。

我做错了什么?

我用 c#、php 和 ruby​​ 对此进行了检查——解密后的数据以 255、254 开头

要重现,请使用以下参数:

数据:1234567890123456 关键:第一1 salt(iv):数据

using System;
using System.Security.Cryptography;
using System.Text;
using System.IO;

namespace crypto_test
{
    class MainClass
    {
        public static void Main(string[] args)
        {

                bool running = true;
                while (running)
                {
                    Console.WriteLine("Enter data:");
                    var data = Console.ReadLine();
                    Console.WriteLine("Enter key:");
                    var key = Console.ReadLine();
                    Console.WriteLine("Enter iv:");
                    var iv = Console.ReadLine();
                    Console.WriteLine("Enter d for decode");
                    var decode = (Console.ReadLine() == "d");

                    string encoded=Crypt(data, key, iv, decode);
                    Console.WriteLine(encoded);
                    if (!decode)
                    {
                        encoded=  Crypt(encoded, key, iv, true);
                        Console.WriteLine(encoded);
                    }

                    Console.WriteLine("quit to exit");
                    running = !(Console.ReadLine() == "quit");
                }

        }

        public static string Crypt(string value, string password, string salt, bool decrypt)
        {
            DeriveBytes rgb = new Rfc2898DeriveBytes(password, Encoding.Unicode.GetBytes(salt));
            SymmetricAlgorithm algorithm = new AesCryptoServiceProvider();
            byte[] rgbKey = rgb.GetBytes(algorithm.KeySize >> 3);
            byte[] rgbIV = rgb.GetBytes(algorithm.BlockSize >> 3);

            Console.WriteLine("rbKey: size:{0} key:{1}", (algorithm.KeySize >> 3), GetHex(rgbKey));
            Console.WriteLine("rgbIV: size:{0} key:{1}", (algorithm.BlockSize >> 3), GetHex(rgbIV));

            ICryptoTransform transform = decrypt ? algorithm.CreateDecryptor(rgbKey, rgbIV) : algorithm.CreateEncryptor(rgbKey, rgbIV);
            Console.WriteLine("Mode {0}", algorithm.Mode);
            Console.WriteLine("PAdding {0}", algorithm.Padding);

            using (MemoryStream buffer = new MemoryStream())
            {
                using (CryptoStream stream = new CryptoStream(buffer, transform, CryptoStreamMode.Write))
                {
                    try
                    {
                        if (decrypt)
                        {
                            byte[] data = Convert.FromBase64String(value);
                            stream.Write(data,0,data.Length);
                        }
                        else
                        {
                            using (StreamWriter writer = new StreamWriter(stream, Encoding.Unicode))
                            {
                                writer.Write(value);
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.ToString());
                    }
                }

                byte[] buff = buffer.ToArray();
                if (decrypt)
                {
                    return Encoding.Unicode.GetString(buff) + "\r\n" + GetHex(buff);
                }
                else
                    return Convert.ToBase64String(buff);
            }
        }

        public static string GetHex(byte[] data)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < data.Length; ++i)
                sb.Append(data[i].ToString("X2"));
            return sb.ToString();
        }
    }
}

直到现在我还没有找到与 Rfc2898DeriveBytes 等效的东西, 所以我复制了密钥和iv

php

<?php

$salt='Data';
$pass='First1';
$data='1234567890123456';

$encrypted_base64='VKNd9Pi+cttaM6ne8pzAuFbH1U0gJiJ2Wlbbr1rU5z8vbIfAS6nb0/5py4p54aK7';
$encrypted=base64_decode($encrypted_base64);
$key = pack('H*', "30EE7F95F0EF4835F048A481424F2F52EE21B7CEB97F8CC437E5949DB53797D9");
$iv = pack('H*', "B29F5ECF7057065758102385509F0637");
$cipher='AES-256-CBC';
$decrypted = openssl_decrypt($encrypted,$cipher, $key,true,$iv);
for($i =0; $i<strlen($decrypted);++$i)
{
    echo "char:" . ord($decrypted[$i])."<br/>";
}
echo $decrypted
?>

红宝石:

require ('openssl')
require ('base64')

while true


enc_data='VKNd9Pi+cttaM6ne8pzAuFbH1U0gJiJ2Wlbbr1rU5z8vbIfAS6nb0/5py4p54aK7'

data = Base64.decode64(enc_data)

key_hex='30EE7F95F0EF4835F048A481424F2F52EE21B7CEB97F8CC437E5949DB53797D9'
iv_hex='B29F5ECF7057065758102385509F0637'


key = [key_hex].pack('H*')
iv = [iv_hex].pack('H*')

decipher = OpenSSL::Cipher::AES.new(256, :CBC)
decipher.decrypt
decipher.key = key
decipher.iv = iv
plain = decipher.update(data) + decipher.final

puts plain
puts plain.bytes

end

【问题讨论】:

  • 问题是,如果我再次解码数据,它以 255、254 开头,我不知道为什么。我添加了代码来重现它。

标签: c# php encryption cryptography


【解决方案1】:

好消息,您的解密似乎工作正常。

您在解密的密文中看到的是 UTF-16 LE 的字节顺序标记,Microsoft 将其(错误地)指示为 Encoding.Unicode。您需要做两件事中的一件:

  1. 使用可理解 UTF-16 LE 的解码器对文本进行解码,包括字节顺序标记;
  2. 使用更合理的UTF-8 编码(在C# 代码中)进行编码。

就我个人而言,我会强烈倾向于 (2)。

【讨论】:

    猜你喜欢
    • 2023-03-23
    • 2016-04-25
    • 1970-01-01
    • 1970-01-01
    • 2018-09-28
    • 2011-03-06
    • 1970-01-01
    • 1970-01-01
    • 2019-12-01
    相关资源
    最近更新 更多