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:
The machines where I'm seeing the failure are:
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
0x80090016LThe 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).
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
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
We didn't modify
as we're not using those methods.
User contributions licensed under CC BY-SA 3.0