将WinApi Crypt转换为.NET Framework System.Security.Cryptography

时间:2017-03-28 09:21:49

标签: c# winapi encryption

我想请求帮助重构C#代码。

目标是删除所有外部WinApi调用,并使用System.Security.Cryptography命名空间中的方法替换它们。

private static IntPtr GenerateKey(IntPtr hCryptProv, byte[] keyData)
{
    var hHash = IntPtr.Zero;
    Win32.CryptCreateHash(hCryptProv, Win32.CALG_MD5, IntPtr.Zero, 0, ref hHash);
    var len = (uint)keyData.Length;
    Win32.CryptHashData(hHash, keyData, len, 0);
    var hKey = IntPtr.Zero;
    Win32.CryptDeriveKey(hCryptProv, Win32.CALG_3DES, hHash, 0, ref hKey);
    if (hHash != IntPtr.Zero) Win32.CryptDestroyHash(hHash);
    return hKey;
}

public static byte[] Encrypt(byte[] dataToEncrypt)
{                        
    var keyData = Encoding.ASCII.GetBytes("{2B9B4443-74CE-42A8-8803-076B136B5967}");
    var size = (uint)dataToEncrypt.Length;                
    var buffer = new byte[size * 2];
    Array.Copy(dataToEncrypt, 0, buffer, 0, size);
    var hCryptProv = IntPtr.Zero;
    bool gotcsp = Win32.CryptAcquireContext(ref hCryptProv, null, null, Win32.PROV_RSA_FULL, Win32.CRYPT_VERIFYCONTEXT | Win32.CRYPT_MACHINE_KEYSET);
    if (!gotcsp)
    {
        Win32.CryptAcquireContext(ref hCryptProv, null, null, Win32.PROV_RSA_FULL, Win32.CRYPT_NEWKEYSET | Win32.CRYPT_VERIFYCONTEXT | Win32.CRYPT_MACHINE_KEYSET);
    }                        

    if (hCryptProv == IntPtr.Zero) return null;

    var hKey = GenerateKey(hCryptProv, keyData);
    Win32.CryptEncrypt(hKey, IntPtr.Zero, 1, 0, buffer, ref size, size*2);
    var encryptedData = new byte[size];
    Array.Copy(buffer, 0, encryptedData, 0, size);
    if (hKey != IntPtr.Zero) Win32.CryptDestroyKey(hKey);
    if (hCryptProv != IntPtr.Zero) Win32.CryptReleaseContext(hCryptProv, 0);            
    return encryptedData;
}
/// <summary>
/// WinAPI Imports
/// </summary>
internal class Win32
{
    public const uint PROV_RSA_FULL = 1;
    public const uint NTE_BAD_KEYSET = 0x80090016;
    public const uint CRYPT_NEWKEYSET = 0x00000008;
    public const uint CRYPT_VERIFYCONTEXT = 0xF0000000;
    public const uint CRYPT_MACHINE_KEYSET = 0x00000020;

    public const uint ALG_CLASS_HASH = (4 << 13);
    public const uint ALG_SID_MD5 = 3;
    public const uint CALG_MD5 = (ALG_CLASS_HASH | ALG_SID_MD5);
    public const uint ALG_CLASS_DATA_ENCRYPT = (3 << 13);
    public const uint ALG_TYPE_BLOCK = (3 << 9);
    public const uint ALG_SID_3DES = 3;
    public const uint CALG_3DES = (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_3DES);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool CryptAcquireContext(ref IntPtr hProv, string pszContainer, string pszProvider, uint dwProvType, uint dwFlags);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CryptReleaseContext(IntPtr hProv, uint dwFlags);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CryptDestroyKey(IntPtr hKey);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CryptCreateHash(IntPtr hProv, uint Algid, IntPtr hKey, uint dwFlags, ref IntPtr hHash);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CryptHashData(IntPtr hHash, [In, Out] byte[] pbData, uint dwDataLen, uint dwSize);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CryptDestroyHash(IntPtr hHash);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CryptDeriveKey(IntPtr hProv, uint Algid, IntPtr hHash, uint dwFlags, ref IntPtr hKey);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CryptEncrypt(IntPtr hKey, IntPtr hHash, int Final, uint dwFlags, [In, Out] byte[] pbData, ref uint pdwDataLen, uint dwBufLen);
}

我尝试过不同的组合,选项和加密提供程序,使用System.Security.Cryptography创建类似的方法没有问题,但问题是我需要一个替换代码的方法。 这意味着,为了加密传递相同的数据,我必须得到相同的结果。这是一个问题。我对加密的了解肯定不是很深入,不能考虑到这种方法的所有细微差别。

你能帮我解决这个问题吗?我不是要给我一个加密教程的链接,但要告诉我应该使用哪些选项的方法。

[2017-03-28 11:27GMT]其他信息

我真的不认为它会有所帮助,但我完成了一个实验性代码:

public static List<byte> Encrypt(byte[] toEncrypt)
{
    var databytes = Encoding.ASCII.GetBytes("{2B9B4443-74CE-42A8-8803-076B136B5967}");
    var hashmd5 = new MD5CryptoServiceProvider();
    var keyArray = hashmd5.ComputeHash(databytes);
    hashmd5.Clear();            
    var pdb = new PasswordDeriveBytes(keyArray, new byte[0]);
    var hashKey = pdb.CryptDeriveKey("TripleDES", "MD5", 0, new byte[8]);

    var tdes = new TripleDESCryptoServiceProvider();
    tdes.Key = hashKey;
    tdes.Mode = CipherMode.ECB;
    tdes.Padding = PaddingMode.PKCS7;
    var cTransform = tdes.CreateEncryptor();            
    var resultArray = cTransform.TransformFinalBlock(toEncrypt, 0, toEncrypt.Length);
    tdes.Clear();
    return resultArray.ToList();
}

还有许多其他变种,但没有任何能给我正确的结果:

来源数据:

private byte[] dataToEncrypt = {224,111,176,138,238,238,238,239,115,109,201,144,89,58,161,0,0,0,0,0,0,0,0};

原始功能重新出现:

private byte[] originalResult = {31,173,65,161,199,249,73,200,210,74,156,21,36,160,94,137,71,205,15,206,99,105,40,83};

示例函数返回:

private byte[] sampleResult = {211,29,187,125,82,9,240,177,199,133,135,7,132,166,166,164,189,36,126,186,104,79,53,159};

1 个答案:

答案 0 :(得分:0)

我至少丢失了一个小时,因为我认为您的代码使用了RSA(我被PROV_RSA_FULL误导了)......实际上它只是在进行3DES加密......

var keyData = Encoding.ASCII.GetBytes("{2B9B4443-74CE-42A8-8803-076B136B5967}");
byte[] key;

using (PasswordDeriveBytes pdb = new PasswordDeriveBytes(keyData, null))
{
    key = pdb.CryptDeriveKey("TripleDES", "MD5", 0, new byte[8]);
    //Debug.WriteLine(BitConverter.ToString(key));
}

using (var prov = new TripleDESCryptoServiceProvider())
{
    using (var encryptor = prov.CreateEncryptor(key, new byte[8]))
    {
        byte[] encrypted = encryptor.TransformFinalBlock(bytes2, 0, bytes2.Length);
        //Debug.WriteLine(BitConverter.ToString(encrypted));
    }
}

出于好奇,我会发布原始C#代码的修改版本...我想调试一下生成的密钥,但是在CryptoAPI中导出它很复杂,然后原始代码不是就像我会写的那样: - )

private static IntPtr GenerateKey(IntPtr hProv, byte[] keyData)
{
    var hHash = IntPtr.Zero;

    try
    {
        bool check = Win32.CryptCreateHash(hProv, Win32.CALG_MD5, IntPtr.Zero, 0, out hHash);

        if (!check)
        {
            throw new Win32Exception();
        }

        check = Win32.CryptHashData(hHash, keyData, keyData.Length, 0);

        if (!check)
        {
            throw new Win32Exception();
        }

        IntPtr hKey;

        check = Win32.CryptDeriveKey(hProv, Win32.CALG_3DES, hHash, Win32.CRYPT_EXPORTABLE, out hKey);

        if (!check)
        {
            throw new Win32Exception();
        }

        return hKey;
    }
    finally
    {
        if (hHash != IntPtr.Zero)
        {
            Win32.CryptDestroyHash(hHash);
        }
    }
}

public static byte[] Encrypt(byte[] plainText)
{
    var keyData = Encoding.ASCII.GetBytes("{2B9B4443-74CE-42A8-8803-076B136B5967}");

    IntPtr hCryptProv = IntPtr.Zero;
    IntPtr hKey = IntPtr.Zero;

    try
    {
        bool check = Win32.CryptAcquireContext(out hCryptProv, null, null, Win32.PROV_RSA_FULL, Win32.CRYPT_VERIFYCONTEXT | Win32.CRYPT_MACHINE_KEYSET);

        if (!check)
        {
            check = Win32.CryptAcquireContext(out hCryptProv, null, null, Win32.PROV_RSA_FULL, Win32.CRYPT_NEWKEYSET | Win32.CRYPT_VERIFYCONTEXT | Win32.CRYPT_MACHINE_KEYSET);

            if (!check)
            {
                throw new Win32Exception();
            }
        }

        hKey = GenerateKey(hCryptProv, keyData);

        //byte[] key = ExportSymmetricKey(hCryptProv, hKey);
        //Debug.WriteLine(BitConverter.ToString(key));

        var size = plainText.Length;
        check = Win32.CryptEncrypt(hKey, IntPtr.Zero, 1, 0, null, ref size, 0);

        if (!check)
        {
            throw new Win32Exception();
        }

        var cypherText = new byte[size];

        Array.Copy(plainText, cypherText, plainText.Length);

        size = plainText.Length;

        check = Win32.CryptEncrypt(hKey, IntPtr.Zero, 1, 0, cypherText, ref size, cypherText.Length);

        if (!check)
        {
            throw new Win32Exception();
        }

        return cypherText;
    }
    finally
    {
        if (hKey != IntPtr.Zero)
        {
            Win32.CryptDestroyKey(hKey);
        }

        if (hCryptProv != IntPtr.Zero)
        {
            Win32.CryptReleaseContext(hCryptProv, 0);
        }
    }
}

// Based on https://books.google.it/books?id=aL3P3eJdiREC&pg=PT271&lpg=PT271&dq=PROV_RSA_FULL+CryptEncrypt&source=bl&ots=STsuConTHr&sig=W-BWwch8aZ-RqFb8N67rMHTrqYc&hl=it&sa=X&ved=0ahUKEwit2qKnlfvSAhWCtRQKHbL9BbQQ6AEIQzAF#v=onepage&q=PROV_RSA_FULL%20CryptEncrypt&f=false
// Page 248
private static byte[] ExportSymmetricKey(IntPtr hProv, IntPtr hKey)
{
    IntPtr hExpKey = IntPtr.Zero;

    try
    {
        bool check = Win32.CryptGenKey(hProv, 1 /* AT_KEYEXCHANGE */, 1024 << 16, out hExpKey);

        if (!check)
        {
            throw new Win32Exception();
        }

        int size = 0;
        check = Win32.CryptExportKey(hKey, hExpKey, 1 /* SIMPLEBLOB */, 0, null, ref size);

        if (!check)
        {
            throw new Win32Exception();
        }

        var bytes = new byte[size];

        check = Win32.CryptExportKey(hKey, hExpKey, 1 /* SIMPLEBLOB */, 0, bytes, ref size);

        if (!check)
        {
            throw new Win32Exception();
        }

        // The next lines could be optimized by using a CryptDecrypt 
        // that accepts a IntPtr and adding directly 12 to the ref bytes
        // instead of copying around the byte array

        // 12 == sizeof(BLOBHEADER) + sizeof(ALG_ID)
        var bytes2 = new byte[size - 12];

        Array.Copy(bytes, 12, bytes2, 0, bytes2.Length);

        bytes = bytes2;
        bytes2 = null;

        check = Win32.CryptDecrypt(hExpKey, IntPtr.Zero, true, 0, bytes, ref size);

        if (!check)
        {
            throw new Win32Exception();
        }

        Array.Resize(ref bytes, size);

        return bytes;
    }
    finally
    {
        if (hExpKey != IntPtr.Zero)
        {
            Win32.CryptDestroyKey(hExpKey);
        }
    }
}

/// <summary>
/// WinAPI Imports
/// </summary>
internal class Win32
{
    public const uint PROV_RSA_FULL = 1;
    public const uint NTE_BAD_KEYSET = 0x80090016;
    public const uint CRYPT_NEWKEYSET = 0x00000008;
    public const uint CRYPT_VERIFYCONTEXT = 0xF0000000;
    public const uint CRYPT_MACHINE_KEYSET = 0x00000020;

    public const uint CRYPT_EXPORTABLE = 1;

    public const uint ALG_CLASS_HASH = (4 << 13);
    public const uint ALG_SID_MD5 = 3;
    public const uint CALG_MD5 = (ALG_CLASS_HASH | ALG_SID_MD5);
    public const uint ALG_CLASS_DATA_ENCRYPT = (3 << 13);
    public const uint ALG_TYPE_BLOCK = (3 << 9);
    public const uint ALG_SID_3DES = 3;
    public const uint CALG_3DES = (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_3DES);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool CryptAcquireContext(out IntPtr hProv, string pszContainer, string pszProvider, uint dwProvType, uint dwFlags);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CryptReleaseContext(IntPtr hProv, uint dwFlags);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CryptDestroyKey(IntPtr hKey);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CryptCreateHash(IntPtr hProv, uint Algid, IntPtr hKey, uint dwFlags, out IntPtr hHash);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CryptHashData(IntPtr hHash, [In, Out] byte[] pbData, int dwDataLen, uint dwSize);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CryptDestroyHash(IntPtr hHash);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CryptDeriveKey(IntPtr hProv, uint Algid, IntPtr hHash, uint dwFlags, out IntPtr hKey);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CryptGenKey(IntPtr hProv, uint Algid, uint dwFlags, out IntPtr hKey);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CryptEncrypt(IntPtr hKey, IntPtr hHash, int final, uint dwFlags, [In, Out] byte[] pbData, ref int pdwDataLen, int dwBufLen);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CryptExportKey(IntPtr hKey, IntPtr hExpKey, uint dwBlobType, uint dwFlags, [Out] byte[] pbData, ref int pdwDataLen);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CryptGetKeyParam(IntPtr hKey, uint dwParam, [Out] byte[] pbData, ref int pdwDataLen, uint dwFlags);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CryptDecrypt(IntPtr hKey, IntPtr hHash, bool final, uint dwFlags, [In, Out] byte[] pbData, ref int pdwDataLen);
}