title: Translating Win32 Crypto API calls to C# with System.Security.Cryptography
body: <p>I have been given an assignment to drop one of our product's dll and replace it with a pure C# one. The old DLL is a .NET 2.0 Managed C++ (C++\CLI) which wraps calls to the Win32 native Crypto API. The new DLL should expose a new object with the same name &amp; methods, but should be written with C# (.NET 4.0). Of course, the new DLL should encrypt that same way (and decrypt) as the old one - otherwise, all saved encrypted passwords in a persistent storage like in a DB or in a File - will not be resolved!.</p>
  
  <p>This is the (pseudo) code of the native (Win32) API calls (note the the input is alway Unicode encoded):</p>
  
  <pre><code>//buffer_to_encrypt - Is the input to the following procedure and is the buffer
  // to be encrypted using 3DES and the below password to generate a valid 3DES key
  // The buffer is Unicode encoded!!!
  
  
  HCRYPTPROV m_provider = NULL;
  HCRYPTHASH m_hash = NULL;
  HCRYPTKEY m_key = NULL;
  
  static const unsigned char password[] = { 
                                                  0xF1, 0x49, 0x4C, 0xD0, 0xC1, 
                                                  0xE2, 0x1A, 0xEA, 0xFB, 0x34, 
                                                  0x25, 0x5A, 0x63, 0xA5, 0x29, 
                                                  0x09, 0x8E, 0xB6, 0x7B, 0x75 
                                              }; //20 BYTES password
  
  
  CryptAcquireContextW( &amp;m_provider, NULL, NULL, PROV_DH_SCHANNEL, CRYPT_MACHINE_KEYSET | CRYPT_VERIFYCONTEXT);
  
  CryptCreateHash( m_provider, CALG_SHA1, NULL, 0, &amp;m_hash );
  
  CryptHashData( m_hash, password, (DWORD)20, 0 ); //password is a 20Bytes buffer 
  
  CryptDeriveKey(m_provider, CALG_3DES, m_hash, CRYPT_EXPORTABLE, &amp;m_key);
  
  CryptEncrypt( m_key.handle(), NULL, TRUE, 0, buffer_to_encrypt, &amp;dwFilled, (DWORD)total );
  
  return buffer_to_encrypt;
  </code></pre>
  
  <p>Now, I'm trying to write the same procedure using C# (System.Security.Cryptography namespace) with the new Crypto objects exposed by the .NET API:</p>
  
  <pre><code>class Encryptor
  {
  
           private static byte[] password = { 
                                              0xF1, 0x49, 0x4C, 0xD0, 0xC1, 
                                              0xE2, 0x1A, 0xEA, 0xFB, 0x34, 
                                              0x25, 0x5A, 0x63, 0xA5, 0x29, 
                                              0x09, 0x8E, 0xB6, 0x7B, 0x75 
                                          }; //20 BYTES password, same as the above native code
  
  
  
  
          private static byte[] EncryptInternal(string source)
          {
              byte[] resultArray = null;
              byte[] streamToEncrypt = Encoding.Unicode.GetBytes(source);
  
              using (TripleDESCryptoServiceProvider prov3des = new TripleDESCryptoServiceProvider())
              {
  
                  prov3des.Mode = CipherMode.ECB;
                  prov3des.Padding = PaddingMode.PKCS7;
  
                  using (PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, null)) //No slat needed here
                  {
                      prov3des.Key = pdb.CryptDeriveKey("TripleDES", "SHA1", prov3des.KeySize, ZeroIV);
                  }
  
                  ICryptoTransform cTransform = prov3des.CreateEncryptor();
  
                  resultArray = cTransform.TransformFinalBlock(streamToEncrypt, 0, streamToEncrypt.Length);
  
              }
  
              return resultArray;
          }
  }
  </code></pre>
  
  <p>Here I'm facing an <strong>annoying problem</strong> - The encrypted array (result encrypted buffer) is not the same using both methods! The first 8 bytes (64 bits) of each array are identical, but the next bytes are not. This causes short strings (max 3 characters) to be encrypted identically using both methods, but longer strings are resulted in different encrypted data.</p>
  
  <p>How can I force the two methods to be equivalent? That is - To encrypt &amp; decrypt that same way so that the output would be the same?
  What am I missing here? Is there a change of default values\behavior between the .NET &amp; Native (Win32) APIs? (I think the default 3DES cypher mode in Win32 Crypto API is EBC while the default using C# is CBC - please correct me if I'm wrong).</p>
  
  <p>Thanks!</p>
  
  <p>Omri</p>