为什么只有一半的加密字符串没有正确解密?

时间:2017-06-09 18:44:04

标签: c# des

似乎无法解决这个问题......我正在使用DESCryptoServiceProvider进行快速的双向加密(与安全无关,安全性不是此问题的目的)。

无论如何它很奇怪,因为进入然后返回的字符串只能正确解密字符串的一半。我似乎无法注意到这个错误,所以也许有人会对此有所了解......

enter image description here

我将两个字符串与冒号组合作为分隔符,因此' abc12345:xyz56789'是输入。然后在输出中注意只有字符串的第一部分被搞砸了,而不是第二部分。我希望如果我完全错了,那么整个事情都不会正确解密。

以下是所有代码:

class Program
{
    static void Main(string[] args)
    {
        var userId = "abc12345";
        var appId = "xyz56789";

        Console.WriteLine($"UserId: {userId}, AppId: {appId}");

        var code = QuickEncode(userId, appId);

        Console.WriteLine(code);

        var result = QuickDecode(code);

        var uId = result.Item1;
        var aId = result.Item2;

        Console.WriteLine($"UserId: {uId}, AppId: {aId}");

        Console.ReadKey();
    }

    private static string QuickEncode(string userId, string appId)
    {
        DESCryptoServiceProvider des = new DESCryptoServiceProvider();

        var desKey = StringToByteArray("437459133faf42cb");

        des.Key = desKey;

        ICryptoTransform encryptor = des.CreateEncryptor();

        var encryptMe = $"{userId}:{appId}";

        Console.WriteLine($"Input String: {encryptMe}");

        byte[] stringBytes = System.Text.Encoding.UTF8.GetBytes(encryptMe);

        byte[] enc = encryptor.TransformFinalBlock(stringBytes, 0, stringBytes.Length);

        var encryptedBytesString = Convert.ToBase64String(enc);

        return encryptedBytesString;
    }

    private static Tuple<string, string> QuickDecode(string code)
    {
        DESCryptoServiceProvider des = new DESCryptoServiceProvider();

        var desKey = StringToByteArray("437459133faf42cb");

        des.Key = desKey;

        ICryptoTransform decryptor = des.CreateDecryptor();

        var codeBytes = Convert.FromBase64String(code);

        byte[] originalAgain = decryptor.TransformFinalBlock(codeBytes, 0, codeBytes.Length);

        var decryptMe = System.Text.Encoding.UTF8.GetString(originalAgain);

        Console.WriteLine($"Output String: {decryptMe}");

        var ids = decryptMe.Split(':');

        return new Tuple<string, string>(ids[0], ids[1]);
    }

    public static string ByteArrayToString(byte[] ba)
    {
        StringBuilder hex = new StringBuilder(ba.Length * 2);
        foreach (byte b in ba)
            hex.AppendFormat("{0:x2}", b);
        return hex.ToString();
    }

    public static byte[] StringToByteArray(String hex)
    {
        int NumberChars = hex.Length;
        byte[] bytes = new byte[NumberChars / 2];
        for (int i = 0; i < NumberChars; i += 2)
            bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
        return bytes;
    }
}

1 个答案:

答案 0 :(得分:3)

您必须将initialization vector(IV)设置为相同的加密值和解密值。因为为每个新的DESCryptoServiceProvider实例自动生成了新的IV,所以您的IV不同并且解密不成功。

正确解密一半邮件的原因是使用CBC mode(默认模式),这有一个非常讨厌的属性,只有第一块加密邮件实际上取决于IV的值,所以潜在的攻击者可以解码除第一个块之外的所有消息,而不知道正确的IV(当然,仍然需要正确的密钥)。因此不建议使用此模式。有关详细信息,请参阅Block cipher mode of operation

因此解决方案很容易 - 存储在用于加密的IV处,并使用相同的IV进行解密。如果可能,也使用另一个密码模式。像这样的事情:

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

class Program
{
    static void Main(string[] args)
    {
        var userId = "abc12345";
        var appId = "xyz56789";

        Console.WriteLine($"UserId: {userId}, AppId: {appId}");

        byte[] IV;
        var code = QuickEncode(userId, appId, out IV);

        Console.WriteLine(code);

        var result = QuickDecode(code, IV);

        var uId = result.Item1;
        var aId = result.Item2;

        Console.WriteLine($"UserId: {uId}, AppId: {aId}");

        Console.ReadKey();
    }

    private static string QuickEncode(string userId, string appId, out byte[] IV)
    {
        DESCryptoServiceProvider des = new DESCryptoServiceProvider();

        var desKey = StringToByteArray("437459133faf42cb");

        des.Key = desKey;
        des.GenerateIV();
        IV = des.IV;

        ICryptoTransform encryptor = des.CreateEncryptor();

        var encryptMe = $"{userId}:{appId}";

        Console.WriteLine($"Input String: {encryptMe}");

        byte[] stringBytes = System.Text.Encoding.UTF8.GetBytes(encryptMe);

        byte[] enc = encryptor.TransformFinalBlock(stringBytes, 0, stringBytes.Length);

        var encryptedBytesString = Convert.ToBase64String(enc);

        return encryptedBytesString;
    }

    private static Tuple<string, string> QuickDecode(string code, byte[] IV)
    {
        DESCryptoServiceProvider des = new DESCryptoServiceProvider();

        var desKey = StringToByteArray("437459133faf42cb");

        des.Key = desKey;
        des.IV = IV;

        ICryptoTransform decryptor = des.CreateDecryptor();

        var codeBytes = Convert.FromBase64String(code);

        byte[] originalAgain = decryptor.TransformFinalBlock(codeBytes, 0, codeBytes.Length);

        var decryptMe = System.Text.Encoding.UTF8.GetString(originalAgain);

        Console.WriteLine($"Output String: {decryptMe}");

        var ids = decryptMe.Split(':');

        return new Tuple<string, string>(ids[0], ids[1]);
    }

    public static string ByteArrayToString(byte[] ba)
    {
        StringBuilder hex = new StringBuilder(ba.Length * 2);
        foreach (byte b in ba)
            hex.AppendFormat("{0:x2}", b);
        return hex.ToString();
    }

    public static byte[] StringToByteArray(String hex)
    {
        int NumberChars = hex.Length;
        byte[] bytes = new byte[NumberChars / 2];
        for (int i = 0; i < NumberChars; i += 2)
            bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
        return bytes;
    }
}