Crypt in C++ and Decrypt in C# (and C++)

You may also like...

22 Responses

  1. útitárs says:

    This website is mostly a walk-through for all of the info you wished about this and didn’t know who to ask. Glimpse here, and you’ll positively uncover it.

  2. Kendall Mcandrews says:

    Wow a really useful article on BW , Whatever next

  3. Taki Waston says:

    Great Article! Extremely helpful. Please keep sharing!

  4. Drew says:

    I know the article is a year old, but I’m hoping you still check the comments. Is there any chance you could show me how to export a public key from C# and use it in C++?

    • riccardotramma says:

      Hi Drew,
      Yes I still check the comments :).
      I will need to setup the environment to try it properly (it has been some time since I have played with it). I will try to have a go asap.

    • riccardotramma says:

      Hi Drew,
      I manage to find the time to have a go with it.
      Imagining that you have your keys generated in C#, already described in the article, just be sure to remove the “CspProviderFlags.UseExistingKey” flag from the line:

      cspParams.Flags = CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore;

      if you are creating the keys.

      You can easily export the public key (the one you want to share) communicating exponent and modulus to the client. The exponent and modulus are retrieved using something like this:

      RSAParameters par = m_RSACryptoServiceProvider.ExportParameters(false); // export the public key

      Exponent is here: par.Exponent
      Modulus is here: par.Modulus

      I noticed that you need to reverse the modulus when passing this to the C++ client.

      On the client side (C++), once you received those info (and acquired the key context as already explained in the article), you can import the public key using a code similar to this (I assume that the exponent is stored in “dwExponent” and the modulus array is pointed by “pModulus”, with “modulusSize” representing the number of bytes in the “pModulus” array):

      DWORD dwKeyBlobSize = modulusSize + sizeof(PUBLICKEYSTRUC) + sizeof(RSAPUBKEY);
      BYTE *pBlob = new BYTE[dwKeyBlobSize];

      PUBLICKEYSTRUC *pPublicKey = (PUBLICKEYSTRUC *) pBlob;
      pPublicKey->bType = PUBLICKEYBLOB;
      pPublicKey->bVersion = CUR_BLOB_VERSION;
      pPublicKey->reserved = 0;
      pPublicKey->aiKeyAlg = CALG_RSA_KEYX; // RSA public-key key exchange

      RSAPUBKEY *pRsaPubKey = (RSAPUBKEY *) (pBlob + sizeof(PUBLICKEYSTRUC));
      pRsaPubKey-> magic = 0x31415352; // Set to RSA1 (0x31415352) for public keys and to RSA2 (0x32415352) for private keys
      pRsaPubKey-> bitlen = modulusSize * 8; //Number of bits in the modulus
      pRsaPubKey-> pubexp = dwExponent; //Exponent

      BYTE *pKey = (BYTE *) ((BYTE *)pRsaPubKey + sizeof (RSAPUBKEY));
      memcpy(pKey, pModulus, modulusSize);

      At this point you can use the same method in the article (ClientImportServerPublicKey) to import the public key. The parameters will be “pBlob” and “dwKeyBlobSize” of course :).

      Note that if you want to you can avoid to export the exponent and use directly the value: “0x10001” that is the RSA Exponent.

      Hope this helps.

      • Uwe says:

        Refering to your remark

        “I noticed that you need to reverse the modulus when passing this to the C++ client.”

        Currently I tried to reverse the complete byte array and still am not able to encrypt via CryptEncrypt.

        Could you explain what you mean by “reversing” the modulus? Maybe I just have to swap every two bytes (Big Endian vs. Little Endian)?

        • riccardotramma says:

          Hi Uwe,
          I actually mean reverse the whole byte array:
          So if the original array is for example: 1, 2, 3, 4, 10, 20, …
          Import is in C++ as: … 20, 10, 4, 3, 2, 1
          (each element is a single byte).
          What error do you have and when (what is your scenario)?

          • Uwe says:

            Thanks, Riccardo

            Actual, my scenario is a bit “unusual”; I want to encrypt in C++ and decrypt in C# without transfering anything but the encrypted data.

            I.e. I do not want to use session keys, or transfer keys between the encryption and decryption part. I’ve tried this for hours and I doubt whether this is actually possible to solve with the Crypto API.

            (I’m aware that this is not the highest security, I could get; it is sufficient, though…)

            Do you think this is possible to acchieve?

            • riccardotramma says:

              Hi Uwe,
              If I understand correctly you are trying to use the RSA key to directly encrypt your message and you receive an error when you are trying to execute the CryptEncrypt function.

              The key specified in the CryptEncrypt method should be a handle to the encryption key (obtained by using either the CryptGenKey or the CryptImportKey function).

              You can still use the public key directly to encrypt the data, but there are some problems and limitations:
              One is the security issue as you were saying, the other is the performance, but the third one is that you have some (annoying) limitations with the data you are trying to encrypt.
              One of the error you are most likely to obtain is:

              errorCode = 0x80090004 – NTE_BAD_LEN (Bad Length: The size of the output buffer is too small to hold the generated ciphertext)

              or:

              errorCode = 0x000000EA – ERROR_MORE_DATA (More data is available: the buffer allocated is not large enough to hold the encrypted data)

              The problem is that you can encrypt a buffer no longer than the length of the key modulus, minus eleven bytes (the eleven bytes is the chosen minimum for PKCS #1 padding).
              In this case (modulus 128), you can then encrypt max 117 bytes.

              So, in order to do this kind of encryption you can use a code similar to this:

              DWORD dwLength = 128 - 11;
              res = ::CryptEncrypt( hClientImportedPublicKey, NULL, 1, 0,
              NULL, &dwLength, 0 );

              You can verify that “res” now should be TRUE and “dwLength” should be 128.
              If this is correct you can then allocate your 128 bytes buffer and encrypt your message:

              res = ::CryptEncrypt( hClientImportedPublicKey, NULL, 1, 0,
              (BYTE *)DataToEncrypt, &dwDataBufferLength, dwLength );

              Where “hClientImportedPublicKey” is your RSA (public) key, “DataToEncrypt” is the buffer to encrypt (allocated with the extra space to contain the final encrypted test (then 128 bytes in this case) , “dwDataBufferLength” is the length of the data to encrypt in that buffer (117 in the example), and “dwLength” is total length of the buffer (128 of course).

              To help you in the debugging remember to use the function “GetLastError()” if any of these methods do not work. The error code (even if not very descriptive in some occasions) should tell you what is the main cause of the problem ;).

              Hope this helps. Let me know how it goes.

              • Uwe says:

                Awesome, Riccardo! I’m trying this ASAP to get it working. Thanks a lot for your assistance! If I’m having a working version, I’ll write an article on my blog or on Code Project about it, since it might help others, too.

                • riccardotramma says:

                  Sure. Remember to link back to this article and let me know the link so that I can add it here :).

                  • elmadj says:

                    Hi thanks for your very well written article !
                    I am having the exact same problem Uwe was describing. I have a public/private key pair with a 512 byte modulus. I am allocating a 512 bytes buffer and Encrypting with CryptEncrypt.
                    Then I am using RSACryptoServiceProvider to decrypt the encrypted data which was written in a file in binary mode meanwhile. I made sure to reverse the bytes in my c# code (with Array.Reverse)before trying to decrypt. I am still having a “bad data” error message in a CryptographicException.

                    Any hint on what might be missing ?

                    Here is the exception call stack :

                    à System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
                    à System.Security.Cryptography.RSACryptoServiceProvider.DecryptKey(SafeKeyHandle pKeyContext, Byte[] pbEncryptedKey, Int32 cbEncryptedKey, Boolean fOAEP, ObjectHandleOnStack ohRetDec
                    ryptedKey)
                    à System.Security.Cryptography.RSACryptoServiceProvider.Decrypt(Byte[] rgb, Boolean fOAEP)
                    à GIRO.Licensor.EncryptionContext.Decrypt(Byte[] iData, Byte[]& oData) dans c:\sessions\V2015\V2015_HTOK\src\ogl\prog\GIRO.Licensor.exe\EncryptionContext.cs:ligne 56

  5. sarah says:

    maybe you can help me,
    I have a C++ code for encrypting and decrypting with hard coded password.
    When i use c++ code to ancrypt and then decrypt it frm another code (C# which calls C++ code) it works just fine.
    But when i try to decrypt from a third application (also C# calling the c++ code) the result decrypted text comes as garbage.
    what can be the reason? all projects are defined to work with unicode.

    • riccardotramma says:

      Hi Sarah,
      My suggestion is to try to make sure that the Key Container name you are using is the same (correct) one. It sounds like something related to a small error like that one.
      I hope this helps.

  6. Larry Smith says:

    Terrific article. Kudos for your efforts! I’ve now spent several painful weeks wrapping my head around all this stuff and largely understand it now (no thanks to MSFT, whose docs on the subject are woeful). I do have one issue maybe you’ld care to weigh in on though. Before you can even begin to decrypt a session key, you need to know the provider that was used for the encryption in the first place (in order to call “CryptAcquireContext()”). It would be much better however to extract that info from the encrypted data itself. You could then decrypt the data from any arbitrary provider without knowing the provider ahead of time. The problem is that to do this, you would need to write the provider name (and its type) to the encrypted file itself. But you would want to encrypt this as well, so that the name of the provider isn’t stored as plain text (since you don’t want anyone to know anything about how the file is encrypted). But how to do this? You can’t encrypt the provider name using the session key itself, since the server would then be facing a chicken/egg situation, i.e., it needs the provider name before it can get access to the session key, but can’t access the provider name until it knows the session key. So maybe the best thing to do is encrypt the provider name using the public key itself, and always with MSFT’s basic or enhanced provider (which may be different than that used by the session key and its data but both client and server need to agree on a common provider used to encrypt the provider name itself). Even if this will work still pondering the situation), the resulting encrypted file will now (usually) contain at least 1 kb or more of overhead (typically 500+ bytes or more for the encrypted session key itself plus another 500+ bytes or more for the encrypted provider name). I find this all problematic, in particular when when a lot of small files (or other sources of data) need to be encrypted (too much overhead involved). Any suggestions or comments? Thanks.

    • riccardotramma says:

      Hi Larry,
      I am glad that you found the article useful and interesting and thanks for the original question.
      While reading your question I thought that the most natural solution is to have both client and server agree upon a common provider to use to encrypt the “main” provider name.
      For the size-overhead problem I was thinking about using a sort of lookup system. You could add a single extra byte to each file. In that way you can identify up to 256 possible alternatives. You can then use an additional set of files that contains (only) the encrypted provider name (one for each file).
      So for example you could name your file in this way:
      provider_000
      provider_001
      provider_002

      provider_255
      When the client receives a file (message.txt) with, for example, the provider byte set to “4”, it will request the file “provider_004” from the server, decrypt it (with the common provider name if this is the solution you go for) and use the decrypted provider name to perform the decryption of the original file (message.txt).
      This will definitely eliminate the overhead for the encrypted provider name.

      You can decide to adopt the same solution for the session key (1, 2 or 4 bytes in this case that reference a set of files with an encrypted key in each one), but this is not really in line with the concept of “session key”, that should not be relative to a file (or a set of files), but should be valid for only a specific session with one client.
      In this case a better solution is therefore to encrypt the files on-the-fly from the server, using a single specific session key (valid only for this session, as it should), and transmitting such key only once to the client before initiating the transmission of the data.
      In this way you will have only one single initial overhead for the encrypted session key and the rest would be pure data.

      Let me know if this helps and covers the scenario you are trying to solve.

      • Larry Smith says:

        HI Riccardo (is that your first name?)

        (Sorry, I know the following is a little long :)

        Thanks for the feedback. It’s appreciated. I’ll give your suggestion some thought. It may work or perhaps some hybrid system but I’ll have to think about it. In the general case though, it would obviously be cleaner for everything to be self-contained in a single file, but it’s likely not doable without incurring the overhead. I had already given some thought however (before you responded), about adding a single byte to the file to identify the provider name. However, I was thinking about mapping it back to the provider name using a hard-coded table in the program itself. It’s hardly ideal, and not nearly as clean as grabbing the provider name from the file (and I’d also be restricted to a predefined collection of providers – not good), but at least no extraneous files would be required. The technique could also be applied to any scenario, not just client/server. I’ve already spent the past several weeks writing some very generic (application independent) classes for encrypting/decrypting any file (or other arbitrary source of data), and it’s not restricted to a client/server scenario (since almost everything I write has a very heavy emphasis on re-use). Clients of my classes can therefore use it in any type of application. I’ll give your idea some thought though and perhaps put something generic together specifically for a client/server environment (built on-top of my other generic routines but leveraging your idea when requested by users of my class). Thanks for the suggestion (didn’t think it).

        On another unrelated issue (while I got you), would you happen to know if it’s safe to open the container for a standard public/private key pair (AT_KEYEXCHANGE) that was originally created using one provider, but using another provider instead. IOW, when calling “CryptAcquireContext()” to access a public/private key pair, I want to pass the container name originally used to create that key, but use a different provider name instead. I know it’s irregular, but the reason I ask is that my generic decryption classes can also handle “.pfx” files meaning they can obtain the private key for decrypting the session key by invoking MSFT’s “PFXImportCertStore()” function. Unfortunately, this function seems to be hardwired to work with MSFT’s enhanced provider, meaning that the private key you end up with is in a container created by the enhanced provider (with no way to change it I could find). If the session key and its encrypted data uses another provider however (say, MSFT’s AES provider), you’re forced to pass the container name created by “PFXImportCertStore()” to “CryptAcquireContext()”, but pass the session key’s provider name instead. IOW, you’re accessing a container originally created by the enhanced provider but using another provider instead (such as the AES provider). This actually works with the AES provider and probably all other mainstream MSFT providers (since public/private key pairs are likely always handled the same way), but I don’t know if it’s safe for a 3rd-party provider. I’m assuming it’s not, since presumably a 3rd-party provider would expect the public/private key pair to be in its own container, not MSFT’s (which it would know nothing about). I know you might just be speculating on this yourself, but any ideas? If not, don’t worry, I’ll just have to live with it (or figure out if it’s possible to safely copy the key created by “PFXImportCertStore()” into a new container under the provider I want).

        Thanks again for your assistance.

        • riccardotramma says:

          Hi Larry,
          (Yep, my name is Riccardo :))

          When you call CryptAcquireContext() and the container doesn’t exist, then you create it adding the flag CRYPT_NEWKEYSET, and it is now generated with that specific provider (PROV_RSA_FULL in the example).
          At this point I would expect that the method returns an error if you try to acquire it with a different provider name.
          This is what should happen (assuming that that’s the case):

          res = ::CryptAcquireContext( &hCryptProvServer, KEY_CONTAINER_SERVER, SERVICE_PROVIDER, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET );

          will probably return false and the same should happen for the following call, when it tries to create a container that exists already:

          res = ::CryptAcquireContext( &hCryptProvServer, KEY_CONTAINER_SERVER, SERVICE_PROVIDER, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET );

          Specifically, the error that I would expect from the first call should be:

          NTE_PROV_TYPE_NO_MATCH (0x8009001BL):
          The provider type specified by dwProvType does not match the provider type found. Note that this error can only occur when pszProvider specifies an actual CSP name.

          If they seems to work as you are saying than it probably means that they are compatible somehow, but I agree that this could not always be the case.

          One thing I can think of is that, in order to transform the provider used for the container created by “PFXImportCertStore()” (that is the MSFT’s enhanced provider), you could try to acquire the context using the same (original) MSFT’s enhanced provider, and then extract the keys and recreate the same container (the same name, removing the previous one), but with the custom provider type you want to use.

          Let me know how it goes with it or if you find an alternative solution :).

  7. zkheraj says:

    I am wondering if the code sample would work for the reverse scenario i.e. Server sending an encrypted message to the Client. The Server sends the public key, with the encrypted session key to the client.
    Thanks

  8. Zul Kheraj says:

    I’m a bit new to CryptoAPI and I need some help…I suspect I’m approaching things incorrectly.

    What I want to do is:
    – to generate a Session key on the SERVER
    – Encrypt the Session Key

    I’d like to then send this encrypted session key to a client who will then decrypt it using the Public Exchange key previously sent from the server. The conversation will then continue using the session key.
    Does this seem sensible?

    What I am trying to do is reverse scenario of what you have described. Any help ?

    Thanks

  9. Kiran Chavan says:

    Hi Riccardo,

    I am facing a difficulty while verifying a digital signature in C# application.

    The signature is created using Win32 Crypto APIs.

    Would you please help me what could be wrong in below snippet.

    RSACryptoServiceProvider rsa;

    const int PROVIDER_RSA_FULL = 1;
    const string SERVICE_PROVIDER = “Microsoft Enhanced Cryptographic Provider v1.0″; //128 bit encryption

    CspParameters cspParams;
    cspParams = new CspParameters(PROVIDER_RSA_FULL);
    //cspParams.KeyContainerName = ” “;

    cspParams.Flags = CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore;
    cspParams.ProviderName = SERVICE_PROVIDER;

    rsa = new RSACryptoServiceProvider(cspParams);

    byte[] PublicKeyBlob;
    using (var fsKeyFile = new FileStream(“..\..\..\Files\pubKey.txt”, FileMode.Open))
    {
    PublicKeyBlob = new byte[fsKeyFile.Length];
    fsKeyFile.Read(PublicKeyBlob, 0, PublicKeyBlob.Length);
    }

    // RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
    // //RSA.FromXmlString(ReadFile(“..\..\..\Files\pub.txt”));
    rsa.ImportCspBlob(PublicKeyBlob);
    RSAPKCS1SignatureDeformatter RSADeformatter = new RSAPKCS1SignatureDeformatter(rsa);
    RSADeformatter.SetHashAlgorithm(“SHA1”);
    SHA1CryptoServiceProvider SHhash = new SHA1CryptoServiceProvider();
    //string signature = ReadFile(“..\..\..\Files\DigiSign.sig”);
    byte[] digitalSign;
    using (var fsSignFile = new FileStream(“..\..\..\Files\DigiSign.sig”, FileMode.Open))
    {
    digitalSign = new byte[fsSignFile.Length];
    fsSignFile.Read(digitalSign, 0, digitalSign.Length);

    // digitalSign = digitalSign.Reverse().ToArray();
    }

    var bytes = new ASCIIEncoding().GetBytes(“HelloWorld”);

    var returnVal = RSADeformatter.VerifySignature(SHhash.ComputeHash(bytes),
    digitalSign);
    if (returnVal)
    {
    Console.WriteLine(“Valid signature”);
    }
    else
    {
    Console.WriteLine(“Wrong sign”);
    }
    }

    The output is: “Wrong sign”

    Whereas the signature is successfully validated in C++ application (where it is generated).

    Would you please help me out in this?

Leave a Reply

Your email address will not be published.

To avoid spam, comments will be verified before being published

This site uses Akismet to reduce spam. Learn how your comment data is processed.