简体   繁体   中英

Translate advapi32.dll crypto calls to .NET

I need to transfer data from a legacy system to the new 3rd party systems.

Some of the data is encrypted, and I need to decrypt it for the new system that will have their own, modern, encryption.

The legacy decryption code was written in VB6. I'm trying to convert that to .NET code and I have a hard time making it work.

The old, simplified code is like this:

'Const ENCRYPT_BLOCK_SIZE as Integer = 8

Dim hCSP As Long
Dim strContexte As String
Dim Password as String
Dim lngPassword  as Long
Dim strEncrypted as String
Dim lngEncrypted as Long
Dim strBuffer as String
Dim lngBuffer as Long
Dim hHash as Long
Dim lngRC as Long

strContexte = "Some Context"
Password = "SomeString"
lngPassword = Len(Password)

strEncrypted = "String to decrypt"
lngEncrypted = Len(strEncrypted)
lngBuffer = lngEncrypted + ENCRYPT_BLOCK_SIZE 
LSet strBuffer = strEncrypted 


lngRC = CryptAcquireContext(hCSP, strContext, "Microsoft Base Cryptographic Provider v1.0", PROV_RSA_FULL, CRYPT_MACHINE_KEYSET)
  If Not CBool(lngRC) Then
    If Err.LastDllError = NTE_BAD_KEYSET Then
      lngRC = CryptAcquireContext(hCSP, strContext, "Microsoft Base Cryptographic Provider v1.0", PROV_RSA_FULL, CRYPT_NEWKEYSET + CRYPT_MACHINE_KEYSET)
  End If

  lngRC = CryptCreateHash(hCSP, CALG_SHA1, 0, 0, hHash)
  lngRC = CryptHashData(hHash, Password, lngPassword, 0)
  lngRC = CryptDeriveKey(hCSP, CALG_RC4, hHash, 0, hHash)
  lngRC = CryptDecrypt(hHash, 0, 1, 0, strBuffer, lngBuffer)

Since this code snippet is part of a code that compares passwords at login, it is not easily accessible, and runs on old servers.

So I'm trying to reproduce the decryption using newer code, so that I can call it during an ETL session.

My C# code looks like this:

        string Password = "SomeString";
        string info = "String to decrypt";
        byte[] value = Convert.ToByte(info, 16)

        long lngInfo = info.Length;
        long lngBuffer = lngInfo + 8;
        byte[] Buffer = new byte[lngBuffer];

        value.CopyTo(Buffer, 0); 


        byte[] key;
        using (PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password, null))
        {
            key = pdb.CryptDeriveKey("TripleDES", "SHA1", 0, new byte[8]);
        }


        byte[] decrypted;
        using (var prov = new TripleDESCryptoServiceProvider())
        {   
            using (var decryptor = prov.CreateDecryptor(key, new byte[8]))
            {
                decrypted = decryptor.TransformFinalBlock(Buffer, 0, lngBuffer);
            }
        }

I'm not confidant with this code at all, and I get an error running it:

System.Security.Cryptography.CryptographicException: 'Length of the data to decrypt is invalid.'

The old code has PROV_RSA_FULL, SHA1 and RC4, how do I plug RC4 in the newer code?

Newer environment is: Windows 2016

I know that ciphers might cause an issue, but first I want to fix the .net code, I'll focus on other problems later!

Thank you for your help!

Ok, since this is a one-time-ish job and not a permanent solution, what I ended up doing was reproduce the same Win32 api calls in my C# code.

After a few tweaks regarding byte arrays and values, it works flawlessly.

The most fun I had was with the key building, but that's another topic.

So first I created a small class to encapsulate all the DLL imports:

public static class ADVAPI32
{
    [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 CryptHashData(IntPtr hHash, byte[] pbData, uint dataLen, uint flags);
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool CryptCreateHash(IntPtr hProv, uint algId, IntPtr hKey, uint dwFlags, ref IntPtr phHash);
    [DllImport("advapi32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool CryptDeriveKey(IntPtr hProv, int Algid, IntPtr hBaseData, int flags, ref IntPtr phKey);

    [DllImport("advapi32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool CryptDecrypt(IntPtr hKey, IntPtr hHash, int Final, uint dwFlags, byte[] pbData, ref uint pdwDataLen);

    [DllImport("Advapi32.dll", EntryPoint = "CryptReleaseContext", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern bool CryptReleaseContext(IntPtr hProv, int dwFlags);
    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool CryptDestroyHash(IntPtr hHash);

    public static string GetAPIErrorMessageDescription()
    {
        return new Win32Exception(Marshal.GetLastWin32Error()).Message;
    }
}

in the main class, created a few constants:

    byte[] Pass { get; set; }
    static readonly string Context = "Context";
    static readonly string Name_CSP = "Microsoft Base Cryptographic Provider v1.0";

    const uint PROV_RSA_FULL = 1;
    const uint CRYPT_MACHINE_KEYSET = 0x20;

    const int ALG_CLASS_DATA_ENCRYPT = 0x6000;
    const int ALG_CLASS_HASH = 0x8000;
    const int ALG_TYPE_ANY = 0;
    const int ALG_TYPE_STREAM = 0x800;

    const int ALG_SID_RC4 = 1;
    const int ALG_SID_SHA1 = 4;

    const int CALG_RC4 = ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_STREAM | ALG_SID_RC4;
    const int CALG_SHA1 = ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA1;

And the Decrypt method:

public string Decrypt(byte[] info)
    {
        IntPtr href = IntPtr.Zero;
        IntPtr hCSP = IntPtr.Zero;

        try
        {

            var success = ADVAPI32.CryptAcquireContext(out hCSP, Context, NAME_CSP, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET);
            if (!success)
            {
                throw new ApplicationException("Error with CryptAcquireContext: " + ADVAPI32.GetAPIErrorMessageDescription());
            }

            success = ADVAPI32.CryptCreateHash(hCSP, CALG_SHA1, IntPtr.Zero, 0, ref href);
            if (!success)
            {
                throw new ApplicationException("Error with CryptCreateHash: " + ADVAPI32.GetAPIErrorMessageDescription());
            }

            var dataLength = Convert.ToUInt32(Pass.Length);

            success = ADVAPI32.CryptHashData(href, MDP, dataLength, 0);
            if (!success)
            {
                throw new ApplicationException("Error with CryptHashData: " + ADVAPI32.GetAPIErrorMessageDescription());
            }

            success = ADVAPI32.CryptDeriveKey(hCSP, CALG_RC4, href, 0, ref href);
            if (!success)
            {
                throw new ApplicationException("Error with CryptDeriveKey: " + ADVAPI32.GetAPIErrorMessageDescription());
            }

            var bufferLen = Convert.ToUInt32(info.Length);

            success = ADVAPI32.CryptDecrypt(href, IntPtr.Zero, 1, 0, info, ref bufferLen);
            if (!success)
            {
                throw new ApplicationException("Error with CryptDecrypt: " + ADVAPI32.GetAPIErrorMessageDescription());
            }

            var strTxt = Encoding.ASCII.GetString(info, 0, Convert.ToInt32(bufferLen));
            strTxt = strTxt.Substring(0, strTxt.Length / 2 - 4);

            return strTxt;
        }
        finally
        {
            if (href != IntPtr.Zero)
            {
                ADVAPI32.CryptDestroyHash(href);
            }
            if (hCSP != IntPtr.Zero)
            {
                ADVAPI32.CryptReleaseContext(hCSP, 0);
            }
        }
    }

Thanks to jdweng for the idea.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM