NsisCrypt returns "Error while acquiring Windows CryptoAPI context" on all but development machine

-1

I'm trying to use the NsisCrypt plugin to encrypt a couple of passwords before writing them to a config file.

I have the following code in my installer script:

StrCpy $algorithm "3des"

NsisCrypt::Base64Encode "<key string>"
Pop $key

NsisCrypt::Base64Encode "<iv string>"
Pop $iv

NsisCrypt::EncryptSymmetric $teststring $algorithm $key $iv
Pop $encryptedString

This works fine on my development machine. The string is encrypted and when I call the following code in my program the correct clear text string is returned:

private string DecryptInternal(string source, string key, string iv)
{
    byte[] resultArray = null;

    using (var prov3des = new TripleDESCryptoServiceProvider())
    {
        prov3des.Key = Encoding.UTF8.GetBytes(key);
        prov3des.Mode = CipherMode.CBC;
        prov3des.Padding = PaddingMode.PKCS7;
        prov3des.IV = Encoding.UTF8.GetBytes(iv);

        using (var cTransform = prov3des.CreateDecryptor())
        {
            byte[] stringToDecrypt = Convert.FromBase64String(source);
            resultArray = cTransform.TransformFinalBlock(stringToDecrypt, 0, stringToDecrypt.Length);
        }
    }

    return Encoding.UTF8.GetString(resultArray);
}

However, when I run the installer on any other machine I get the following written into the config file:

Error while acquiring Windows CryptoAPI context

The NSIS plugin page has the following in the description of the EncryptSymmetric method:

As always you have to pop the result, which may be the return value or the error message.

So why am I getting this error on some machines, but not my development machine.

My development machine specs are:

  • Windows 7 Enterprise 64 bit with .NET 4.0 installed

The machines where I'm seeing the failure are:

  • Windows 7 Enterprise 64 bit with .NET 4 Extended installed
  • Windows Vista Enterprise 32 bit with .NET 4 Extended installed
  • Windows XP 32 bit with .NET 4 Extended installed

Any clues about what I'm doing wrong?

Right - after running the code provided by Anders I can see that the error code being returned is

0x80090016 (-2146893802)

Which according to this page is

NTE_BAD_KEYSET
0x80090016L

The key container could not be opened. A common cause of this error is that the key container does not exist. To create a key container, call CryptAcquireContext using the CRYPT_NEWKEYSET flag. This error code can also indicate that access to an existing key container is denied. Access rights to the container can be granted by the key set creator by using CryptSetProvParam.

So I just need to find out whether it's due to the key container not existing (unlikely I would have thought) or due to access being denied (more likely I think).

encryption
nsis
cryptoapi
asked on Stack Overflow Jul 12, 2013 by ChrisF • edited Jul 12, 2013 by ChrisF

2 Answers

0

You can try calling the API directly, the GetLastError() value will hopefully help us narrow down the problem...

!define MS_DEF_PROV "Microsoft Base Cryptographic Provider v1.0"
!define MS_ENHANCED_PROV "Microsoft Enhanced Cryptographic Provider v1.0"
!define PROV_RSA_FULL 1

System::Call 'advapi32::CryptAcquireContext(*i.r0,i0,t "${MS_ENHANCED_PROV}",i${PROV_RSA_FULL},i0)i.r1 ?e' # The plugin uses these parameters
pop $3
detailprint ret=$1,getlasterror=$3,hContext=$0

System::Call 'advapi32::CryptAcquireContext(*i.r0,i0,t "${MS_DEF_PROV}",i${PROV_RSA_FULL},i0)i.r1 ?e'
pop $3
detailprint ret=$1,getlasterror=$3,hContext=$0

System::Call 'advapi32::CryptAcquireContext(*i.r0,i0,i0,i${PROV_RSA_FULL},i0)i.r1 ?e'
pop $3
detailprint ret=$1,getlasterror=$3,hContext=$0
answered on Stack Overflow Jul 12, 2013 by Anders
0

The information provided by Anders showed that my problem was the same as in the other NsisCrypt bug report. I implemented that fix and the passwords were encrypted correctly. Where the code just does this:

if (!CryptAcquireContext(&hProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, 0))
{
    pushstring("Error while acquiring Windows CryptoAPI context");
    return;
}

It should do this:

if (!CryptAcquireContext(&hProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, 0))
{
    if (GetLastError() == NTE_BAD_KEYSET)
    {
        // No default container was found. Attempt to create it.
        if(!CryptAcquireContext(&hProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET)) 
        {
            pushstring("Could not create key for Windows CryptoAPI context");
            return;
        }
    }
    else
    {
        pushstring("Error while acquiring Windows CryptoAPI context");
        return;
    }
}

This is in several places so will need to be refactored into its own method.

So, until a new version of NsisCrypt is released we've had to take a copy of the source (NsisCrypt.cpp) and modify it in the three places where CryptAcquireContext is called with this fix. These are

  • Hash
  • EncryptSymmetric
  • DecrypySymmetric

We didn't modify

  • EncryptAsymmetric
  • DecryptAsymmetric

as we're not using those methods.

answered on Stack Overflow Jul 15, 2013 by ChrisF • edited Jan 26, 2016 by ChrisF

User contributions licensed under CC BY-SA 3.0