如何使用 System.Security.Cryptography 方法将 CryptAcquireContext 转换为 .NET 8

问题描述 投票:0回答:1

我需要解密使用 advapi32.dll 加密的文件。我需要使用 .NET 8 编写解密方法,并使用 C# 编写内置的 System.Security.Cryptography 方法,因为这需要经常使用。不幸的是,我们无法重写加密逻辑,因为这是由外部第 3 方完成的,我宁愿不依赖 advapi32.dll 继续前进。

这是我们当前使用 advapi32.dll 解密文件的代码

#Region "Constants"
    Private Const lngALG_CLASS_DATA_ENCRYPT As Integer = 24576
    Private Const lngALG_CLASS_HASH As Integer = 32768
    Private Const lngALG_SID_MD5 As Integer = 3
    Private Const lngALG_SID_RC4 As Integer = 1
    Private Const lngALG_TYPE_ANY As Integer = 0
    Private Const lngALG_TYPE_STREAM As Integer = 2048
    Private Const lngCALG_MD5 As Integer = ((lngALG_CLASS_HASH Or lngALG_TYPE_ANY) Or lngALG_SID_MD5)
    Private Const lngCALG_RC4 As Integer = ((lngALG_CLASS_DATA_ENCRYPT Or lngALG_TYPE_STREAM) Or lngALG_SID_RC4)
    Private Const lngCRYPT_NEWKEYSET As Integer = 8
    Private Const lngENCRYPT_ALGORITHM As Integer = lngCALG_RC4
    Private Const lngPROV_RSA_FULL As Integer = 1
    Private Const strKEY_CONTAINER As String = "TestString"
    Private Const strSERVICE_PROVIDER As String = "Microsoft Base Cryptographic Provider v1.0"
#End Region

#Region "Declarations"
    Private Declare Function CryptAcquireContext Lib "advapi32.dll" Alias "CryptAcquireContextA" (ByRef phProv As Integer, ByVal pszContainer As String, ByVal pszProvider As String, ByVal dwProvType As Integer, ByVal dwFlags As Integer) As Integer
    Private Declare Function CryptCreateHash Lib "advapi32.dll" (ByVal hProv As Integer, ByVal Algid As Integer, ByVal hKey As Integer, ByVal dwFlags As Integer, ByRef phHash As Integer) As Integer
    Private Declare Function CryptHashData Lib "advapi32.dll" (ByVal hHash As Integer, ByVal pbData As String, ByVal dwDataLen As Integer, ByVal dwFlags As Integer) As Integer
    Private Declare Function CryptDeriveKey Lib "advapi32.dll" (ByVal hProv As Integer, ByVal Algid As Integer, ByVal hBaseData As Integer, ByVal dwFlags As Integer, ByRef phKey As Integer) As Integer
    Private Declare Function CryptDestroyHash Lib "advapi32.dll" (ByVal hHash As Integer) As Integer
    Private Declare Function CryptEncrypt Lib "advapi32.dll" (ByVal hKey As Integer, ByVal hHash As Integer, ByVal Final As Integer, ByVal dwFlags As Integer, ByVal pbData As String, ByRef pdwDataLen As Integer, ByVal dwBufLen As Integer) As Integer
    Private Declare Function CryptDestroyKey Lib "advapi32.dll" (ByVal hKey As Integer) As Integer
    Private Declare Function CryptReleaseContext Lib "advapi32.dll" (ByVal hProv As Integer, ByVal dwFlags As Integer) As Integer
    Private Declare Function CryptDecrypt Lib "advapi32.dll" (ByVal hKey As Integer, ByVal hHash As Integer, ByVal Final As Integer, ByVal dwFlags As Integer, ByVal pbData As String, ByRef pdwDataLen As Integer) As Integer
#End Region

    Public Shared Function Encrypt(ByVal strValue As String, ByVal strPassword As String) As String
        Return Encrypt_Or_Decrypt(strValue, strPassword, True)
    End Function

    Public Shared Function Decrypt(ByVal strValue As String, ByVal strPassword As String) As String
        Return Encrypt_Or_Decrypt(strValue, strPassword, False)
    End Function

    Private Shared Function Encrypt_Or_Decrypt(ByVal strValue As String, ByVal strKey As String, ByVal blnEncrypt As Boolean) As String
        Dim lngCryptProv As Integer
        Dim lngHash As Integer
        Dim lngKey As Integer
        Dim lngLength As Integer

        Try
            If CryptAcquireContext(lngCryptProv, strKEY_CONTAINER, strSERVICE_PROVIDER, lngPROV_RSA_FULL, lngCRYPT_NEWKEYSET) = 0 Then
                If CryptAcquireContext(lngCryptProv, strKEY_CONTAINER, strSERVICE_PROVIDER, lngPROV_RSA_FULL, 0) = 0 Then
                    Throw New Exception("Error during CryptAcquireContext for a new key container." & vbCrLf & "A container with this name probably already exists.")
                End If
            End If

            If CryptCreateHash(lngCryptProv, lngCALG_MD5, 0, 0, lngHash) = 0 Then
                Throw New Exception("Could not create a Hash Object (CryptCreateHash API)")
            End If

            If CryptHashData(lngHash, strKey, strKey.Length, 0) = 0 Then
                Throw New Exception("Could not calculate a Hash Value (CryptHashData API)")
            End If

            If CryptDeriveKey(lngCryptProv, lngENCRYPT_ALGORITHM, lngHash, 0, lngKey) = 0 Then
                Throw New Exception("Could not create a session key (CryptDeriveKey API)")
            End If

            lngLength = strValue.Length

            If blnEncrypt Then
                If CryptEncrypt(lngKey, 0, 1, 0, strValue, lngLength, lngLength) = 0 Then
                    Throw New Exception("Error during CryptEncrypt.")
                End If
            Else
                If CryptDecrypt(lngKey, 0, 1, 0, strValue, lngLength) = 0 Then
                    Throw New Exception("Error during CryptDecrypt.")
                End If
            End If

            Return strValue.Substring(0, lngLength)

        Finally
            ' Ignore errors here
            Try
                If lngKey <> 0 Then
                    CryptDestroyKey(lngKey)
                End If

                If lngHash <> 0 Then
                    CryptDestroyHash(lngHash)
                End If

                If lngCryptProv <> 0 Then
                    CryptReleaseContext(lngCryptProv, 0)
                End If
            Catch
            End Try
        End Try
    End Function

我对逻辑的理解是,它使用MD5对密码进行哈希处理,然后使用RC4算法派生出密钥,然后使用该密钥来解密文件。我想我知道如何使用以下逻辑获得密码,但无法确定这是有效的

using (var md5 = MD5.Create())
{
    var hash = md5.ComputeHash(System.Text.Encoding.ASCII.GetBytes(key));
}

此时我有点困惑,因为在 System.Security.Cryptography 命名空间中看不到 RC4 派生密钥的任何内容,或者我如何使用它来解密文件

c# .net cryptography advapi32 cryptdecrypt
1个回答
0
投票

我认为这应该涵盖它

使用 MD5 对密码进行哈希处理。 .NET 不支持 RC4。这提供了 CryptoStream 所需的 CreateDecryptor 和 CreateEncryptor 方法。 使用密钥和 RC4 解密文件。 您只需在调用 DecryptFile 方法时将 filePath 和密码替换为您的即可。

使用系统; 使用系统.IO; 使用系统.安全.密码学; 使用 System.Text;

public class FileDecryptor
{
    private const string strKEY_CONTAINER = "TestString";
    private const string strSERVICE_PROVIDER = "Microsoft Base Cryptographic Provider v1.0";

    public static string DecryptFile(string filePath, string password)
    {
        using (var md5 = MD5.Create())
        {
            byte[] hash = md5.ComputeHash(Encoding.ASCII.GetBytes(password));
            
            using (var rc4 = new RC4())
            {
                rc4.Key = hash;
                rc4.IV = new byte[16]; // RC4 doesn't use IV, but we need to set it for compatibility

                using (var inputFile = new FileStream(filePath, FileMode.Open))
                using (var outputFile = new MemoryStream())
                using (var cryptoStream = new CryptoStream(inputFile, rc4.CreateDecryptor(), CryptoStreamMode.Read))
                {
                    cryptoStream.CopyTo(outputFile);
                    return Encoding.UTF8.GetString(outputFile.ToArray());
                }
            }
        }
    }
}

// Implementation of RC4 algorithm
public class RC4 : SymmetricAlgorithm
{
    public RC4()
    {
        KeySize = 128;
        BlockSize = 64;
        FeedbackSize = 64;
        LegalBlockSizesValue = new KeySizes[] { new KeySizes(64, 64, 0) };
        LegalKeySizesValue = new KeySizes[] { new KeySizes(40, 128, 8) };
    }

    public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[]? rgbIV)
    {
        return new RC4Transform(rgbKey);
    }

    public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[]? rgbIV)
    {
        return new RC4Transform(rgbKey);
    }

    public override void GenerateIV()
    {
        IV = new byte[8];
    }

    public override void GenerateKey()
    {
        Key = new byte[16];
    }
}

internal class RC4Transform : ICryptoTransform
{
    private byte[] s;
    private int x;
    private int y;

    public RC4Transform(byte[] key)
    {
        s = new byte[256];
        for (int i = 0; i < 256; i++)
        {
            s[i] = (byte)i;
        }

        int j = 0;
        for (int i = 0; i < 256; i++)
        {
            j = (j + key[i % key.Length] + s[i]) & 255;
            Swap(s, i, j);
        }

        x = 0;
        y = 0;
    }

    public int InputBlockSize => 1;

    public int OutputBlockSize => 1;

    public bool CanTransformMultipleBlocks => true;

    public bool CanReuseTransform => true;

    public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
    {
        for (int i = 0; i < inputCount; i++)
        {
            outputBuffer[outputOffset + i] = (byte)(inputBuffer[inputOffset + i] ^ GenerateNextKeystreamByte());
        }
        return inputCount;
    }

    public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
    {
        byte[] outputBuffer = new byte[inputCount];
        TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, 0);
        return outputBuffer;
    }

    public void Dispose()
    {
    }

    private byte GenerateNextKeystreamByte()
    {
        x = (x + 1) & 255;
        y = (y + s[x]) & 255;
        Swap(s, x, y);
        return s[(s[x] + s[y]) & 255];
    }

    private static void Swap(byte[] array, int i, int j)
    {
        byte temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.