locked
Using Win32 Crypto API to decrypt NET app RijndaelManaged RRS feed

  • Question

  • I cannot find a solution to this problem anywhere so far.  I'm hoping that someone has successfully tackled this problem.

    I have a .NET library that generates encrypted strings using 256-bit RijndaelManaged.

    I am trying to write a Win32 app that can decrypt that string using the
    Crypto APIs.

    I can get both to encrypt and decrypt successfully in their own projects,
    but I have been unable to get the Win32 based CryptDecrypt algorithm to
    successfully decrypt the RijndaelManaged encrypted string.

    I am using the same Key and IV for each one, the same string, etc.  As far
    as I can tell, I am using the same Padding, bit size, algorithm.

    This whole post boils down to 2 questions that someone may have already
    resolved in a similar scenario:

    1)  Is it easier to write one unmanaged C++ library source that both
    applications share, with a managed wrapper in the .NET assembly that all use
    the Win32 Crypto library, or:
    2)  Is there some tweak that I am missing that will enable me to decrypt my
    .NET encrypted strings in CryptDecrypt?

    I have listed code from apps to illustrate where I am.  The resultant byte
    arrays from both encryptions are different, and plugging in the byte array
    from one into the other will not decrypt - NTE_BAD_DATA

    Here is my .NET code:

    /////////////////////////////////////////////////
    Byte[] key = { 0x74, 0x6c, 0x9b, 0xf2, 0x24, 0xce, 0xdd, 0x55, 0x23, 0xba,
    0x64, 0xfb, 0xe9, 0xbe, 0x17, 0x32, 0xcf, 0xf5, 0xe3, 0xf6, 0x18, 0xc0, 0x1f,
    0xd5, 0x64, 0x5d, 0x84, 0xc1, 0x1a, 0xc8, 0x71, 0x7c };

    Byte[] IV = { 0x18, 0x13, 0xb1, 0xb3, 0x26, 0x2b, 0x80, 0xd1, 0xe, 0x80,
    0x9f, 0x47, 0xa0, 0xd9, 0x6c, 0x1f, 0xcd, 0x42, 0x1f, 0x33, 0xa9, 0x8e, 0x2f,
    0x66, 0x68, 0xcf, 0x7d, 0xd6, 0x58, 0x57, 0xf2, 0xaa };

    System.Security.Cryptography.RijndaelManaged rm = new
    System.Security.Cryptography.RijndaelManaged();

    rm.KeySize = 256;
    rm.BlockSize = 256;
    rm.Padding = PaddingMode.PKCS7; // default
    rm.Key = key;
    rm.IV = IV;

    string toEncrypt = "String To Encrypt";

    byte []plainTextBytes = Encoding.UTF8.GetBytes(toEncrypt);
    ICryptoTransform ict = rm.CreateEncryptor();

    byte []encrBytes = ict.TransformFinalBlock(plainTextBytes, 0,
    plainTextBytes.Length);

    string result = Convert.ToBase64String(encrBytes);

    byte []dBytes = Convert.FromBase64String(result);
    ICryptoTransform ict2 = rm.CreateDecryptor();
    byte[] decryptedBytes = ict2.TransformFinalBlock(encrBytes, 0,
    encrBytes.Length);
    string plainText  = Encoding.UTF8.GetString(decryptedBytes);

    ///////////////////////////////////////////

    Here is my C++ code:

    struct CryptoBlob {
        BLOBHEADER header;
        DWORD cbKeySize;
        BYTE rgbKeyData [32];
    } keyBlob;

    ..... <skipping _tmain initialization>

    keyBlob.header.bType = PLAINTEXTKEYBLOB;
    keyBlob.header.bVersion = CUR_BLOB_VERSION;
    keyBlob.header.reserved = 0;
    keyBlob.header.aiKeyAlg = CALG_AES_256;
    keyBlob.cbKeySize = 32;  // has to be in bytes, not bits

    BYTE keyData[32] = { 0x74, 0x6c, 0x9b, 0xf2, 0x24, 0xce, 0xdd, 0x55, 0x23,
    0xba, 0x64, 0xfb, 0xe9, 0xbe, 0x17, 0x32, 0xcf, 0xf5, 0xe3, 0xf6, 0x18, 0xc0,
    0x1f, 0xd5, 0x64, 0x5d, 0x84, 0xc1, 0x1a, 0xc8, 0x71, 0x7c };

    BYTE ivData[32] = { 0x18, 0x13, 0xb1, 0xb3, 0x26, 0x2b, 0x80, 0xd1, 0xe,
    0x80, 0x9f, 0x47, 0xa0, 0xd9, 0x6c, 0x1f, 0xcd, 0x42, 0x1f, 0x33, 0xa9, 0x8e,
    0x2f, 0x66, 0x68, 0xcf, 0x7d, 0xd6, 0x58, 0x57, 0xf2, 0xaa };

    for (int idx = 0; idx < 32; idx++)
    {
     keyBlob.rgbKeyData[idx] = keyData[idx];
    }

    HCRYPTKEY hPubKey;
    HCRYPTPROV hProv;
    DWORD dwBlobLen;
    DWORD errVal;

    if (!CryptAcquireContext(
      &hProv,
      NULL,
      NULL,
      PROV_RSA_AES,
      CRYPT_VERIFYCONTEXT))
     {
      errVal = GetLastError();
      ErrLookup(errVal);
      if (NTE_BAD_KEYSET == errVal)
      {
       if (!CryptAcquireContext(
        &hProv,
        _T("NVCryptoTestContainer"),
        MS_ENHANCED_PROV,
        PROV_RSA_AES,
        CRYPT_NEWKEYSET |
                                                                   
    CRYPT_VERIFYCONTEXT))
       {
        errVal = GetLastError();
        ErrLookup(errVal);
        printf("Error in AcquireContext 0x%08x \n", errVal);
        return 1;
       }

      }
     }
     else
     {
      if (CryptImportKey(hProv, (const LPBYTE)&keyBlob, sizeof(keyBlob), 0, 0,
    &hPubKey))
      {
       if (!CryptSetKeyParam(hPubKey, KP_IV, ivData, 0))
       {
        ErrLookup(GetLastError());
       }
       else
       {
        char szClearText[32];
       strcpy(szClearText, "String To Encrypt");

                     DWORD dwDataLen = (DWORD)strlen(szClearText);
                     DWORD dwBufferSize = sizeof(szClearText);

                    if(!CryptEncrypt(hPubKey, 0, TRUE, 0, (LPBYTE)szClearText,
    &dwDataLen, dwBufferSize))
     {
      ErrLookup(GetLastError());
     }
     else
     {
      printf("encrypted looks like:  %s", szClearText);
      dwBufferSize = sizeof(szClearText);
      if(!CryptDecrypt(hPubKey, 0, TRUE, 0, (LPBYTE)zClearText, &dwBufferSize))
          {
           ErrLookup(GetLastError());
          }
          else
          {
           printf("decrypted looks like: %s", szClearText);
          }
         }
        }

        if(!CryptDestroyKey(hPubKey))
        {
         ErrLookup(GetLastError());
        }
       }
       else
       {
        ErrLookup(GetLastError());
       }
    }

    Any help is GREATLY appreciated.
      

    • Moved by nobugz Tuesday, June 30, 2009 1:02 AM not a clr q (From:Common Language Runtime)
    Monday, June 29, 2009 10:59 PM

Answers

  • Hello Clayton,

    RijndaelManaged is an implementation of Rijndael algorithm and allows you to select the block size. If the block size is not set to 128 bits, RijndaelManaged will not be able to interoperate with an AES implementation. The detailed information can be found in .NET security blog(http://blogs.msdn.com/shawnfa/archive/2006/10/09/The-Differences-Between-Rijndael-and-AES.aspx ):

    <quote>
    Since RijndaelManaged is an implementation of Rijndael, it will allow you to select different block sizes (although both block and key sizes must be either 128, 192, or 256 bits.  160 and 224 bit are not supported).  By selecting a block size which is not 128 bits however, RijndaelManaged will not be able to interoperate with an AES implementation ... since the block sizes will not match on either end of the communication.

    One other interesting quirk of the RijndaelManaged implementation is that it will adjust block size to match the feedback size in CFB mode.  This means that if you use CFB and a block size of 128 bits, but a feedback size which is not 128 bits you again will not be compatible with AES.  Generally this does not affect many people, since the most common cipher mode to use is CBC.

    Essentially, if you want to use RijndaelManaged as AES you need to make sure that:

       1. The block size is set to 128 bits
       2. You are not using CFB mode, or if you are the feedback size is also 128 bits
    </quote>

    One way to workaround the problem is to write a C++/CLI wrapper class to interact with the RijndaelManaged class, and use the wrapper class to do the decryption. But this requires a prerequisite, which is your application needs be run with CLR(.NET Framework need to be installed on the target machine). The following sample shows the creation of a wrapper class to interact with .NET objects:

    http://msdn.microsoft.com/en-us/library/ms235259.aspx

    Thanks,
    Rong-Chun Zhang
    Please mark the replies as answers if they help and unmark if they don't.
    Welcome to the All-In-One Code Framework, a sample code project owned by the MSDN Forum Support team!
    Tuesday, June 30, 2009 9:32 AM

All replies

  • Hello Clayton,

    RijndaelManaged is an implementation of Rijndael algorithm and allows you to select the block size. If the block size is not set to 128 bits, RijndaelManaged will not be able to interoperate with an AES implementation. The detailed information can be found in .NET security blog(http://blogs.msdn.com/shawnfa/archive/2006/10/09/The-Differences-Between-Rijndael-and-AES.aspx ):

    <quote>
    Since RijndaelManaged is an implementation of Rijndael, it will allow you to select different block sizes (although both block and key sizes must be either 128, 192, or 256 bits.  160 and 224 bit are not supported).  By selecting a block size which is not 128 bits however, RijndaelManaged will not be able to interoperate with an AES implementation ... since the block sizes will not match on either end of the communication.

    One other interesting quirk of the RijndaelManaged implementation is that it will adjust block size to match the feedback size in CFB mode.  This means that if you use CFB and a block size of 128 bits, but a feedback size which is not 128 bits you again will not be compatible with AES.  Generally this does not affect many people, since the most common cipher mode to use is CBC.

    Essentially, if you want to use RijndaelManaged as AES you need to make sure that:

       1. The block size is set to 128 bits
       2. You are not using CFB mode, or if you are the feedback size is also 128 bits
    </quote>

    One way to workaround the problem is to write a C++/CLI wrapper class to interact with the RijndaelManaged class, and use the wrapper class to do the decryption. But this requires a prerequisite, which is your application needs be run with CLR(.NET Framework need to be installed on the target machine). The following sample shows the creation of a wrapper class to interact with .NET objects:

    http://msdn.microsoft.com/en-us/library/ms235259.aspx

    Thanks,
    Rong-Chun Zhang
    Please mark the replies as answers if they help and unmark if they don't.
    Welcome to the All-In-One Code Framework, a sample code project owned by the MSDN Forum Support team!
    Tuesday, June 30, 2009 9:32 AM
  • Thank you!  That was extremely helpful to learn.  Now we have the information we need to move forward.  Thank you again!

    Clayton
    Tuesday, June 30, 2009 2:36 PM