CspParameter and CryptSetProvParam(PP_KEYEXCHANGE_PIN)

J

John Allberg

Hi!



I recently had to create an application using smart card certificates, but
since this was going to be a server application we had to insert the smart
card pin from the software.



So, I embarked on a mission to use P/Invoke but realized there is a
CspParameter constructor that takes a password, CspParameters (Int32,
String, String, CryptoKeySecurity, SecureString).



Happy as a bird I created a test app, but it didn't work, throwing an
System.Security.Cryptography.CryptographicException ("Cannot create a file
when that file already exists.") . (Thats obviously a generic "Access
Denied"-message)



When investigating, I saw in the CSP log that it receives only one character
of the password in the native CryptSetProvParam(PP_KEYEXCHANGE_PIN) and
therefore returns an error. It turns out that the password that I supplied
in the CspParameter constructor is formatted with unicode when arriving to
the CSP which in turn thinks it should be a null-terminated ascii.string and
since it found a null byte in position 2 it ended there, receiving only the
first character.



I throw that info at the CSP manufacturer and what I got back was this
reference to MSDN:

http://msdn2.microsoft.com/en-us/library/aa380276.aspx



That clearly states (for the PP_KEYEXCHANGE_PIN parameter) "The PIN is
represented as a null-terminated ASCII string." so giving that in Unicode as
CspParameter does is right out wrong.



Anyone else that can confirm this bad behaviour of CspParameter with
password?



Regards,



John Allberg



The exception:

System.Security.Cryptography.CryptographicException: Cannot create a file
when that file already exists.

at
System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32
hr)

at
System.Security.Cryptography.Utils._SetProviderParameter(SafeProvHandle
hProv, Int32 keyNumber, UInt32 paramID, IntPtr pbData)

at System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType
keyType, CspParameters parameters, Boolean randomKeyContainer, Int32
dwKeySize, SafeProvHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle)

at System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair()

at System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32
dwKeySize, CspParameters parameters, Boolean useDefaultKeySize)

at
System.Security.Cryptography.RSACryptoServiceProvider..ctor(CspParameters
parameters)

at ConsoleApplication1.UseCapiCom.Run() in C:\Visual Studio
Projects\TestSmartCardPIN\ConsoleApplication1\UseCapiCom.cs



The code:

CAPICOM.StoreClass store = new CAPICOM.StoreClass();
store.Open(CAPICOM.CAPICOM_STORE_LOCATION.CAPICOM_CURRENT_USER_STORE, "MY",

CAPICOM.CAPICOM_STORE_OPEN_MODE.CAPICOM_STORE_OPEN_EXISTING_ONLY |

CAPICOM.CAPICOM_STORE_OPEN_MODE.CAPICOM_STORE_OPEN_READ_ONLY);

CAPICOM.ICertificates2 storeCerts =
(CAPICOM.ICertificates2)store.Certificates;

CAPICOM.ICertificates2 certs =

storeCerts.Find(CAPICOM.CAPICOM_CERTIFICATE_FIND_TYPE.CAPICOM_CERTIFICATE_FIND_SHA1_HASH,

"a7 0d e7 7a 97 c3 24 8f 8b 80 17 d6 23 9a 43 0d 8c 8c 06 b1", false);

CAPICOM.ICertificate2 cert = (CAPICOM.ICertificate2)certs[1];
Console.WriteLine("Found cert with following CSP data: ");

Console.WriteLine("ProviderType: " + (int)cert.PrivateKey.ProviderType);

Console.WriteLine("ProviderName: " + cert.PrivateKey.ProviderName);

Console.WriteLine("ContainerName: " + cert.PrivateKey.ContainerName);



char[] pwdChars = "123456".ToCharArray(); System.Security.SecureString pwd =
new System.Security.SecureString(); pwd.Clear(); foreach (char chr in
pwdChars) pwd.AppendChar(chr);



System.Security.AccessControl.CryptoKeySecurity keySec =

new System.Security.AccessControl.CryptoKeySecurity();

int provType = (int)cert.PrivateKey.ProviderType;

CspParameters csp = new CspParameters(provType,
cert.PrivateKey.ProviderName, cert.PrivateKey.ContainerName, new
System.Security.AccessControl.CryptoKeySecurity(), pwd); try {

Console.WriteLine("Creating RSACryptoServiceProvider()...");

RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp); //
this is what throws the exc

Console.WriteLine("Signing RSACryptoServiceProvider()...");

byte[] signature = rsa.SignData(

System.Text.UnicodeEncoding.Unicode.GetBytes("DataToBeSigned"),

new SHA1Managed());

Console.WriteLine(Convert.ToBase64String(signature));

}

catch (Exception exc)

{

Console.WriteLine(exc.ToString());

}
 
W

Walter Wang [MSFT]

Hi John,

Welcome to MSDN Managed Newsgroup!

Please feel free to correct me if I've misunderstood anything.

To convert the SecureString in CspParameters to ANSI and pass to
CryptSetProvParam, you can use Marshal.SecureStringToCoTaskMemAnsi:

http://msdn2.microsoft.com/en-us/library/system.runtime.interopservices.mars
hal.securestringtocotaskmemansi.aspx

It will return an IntPtr, you need to declare CryptSetProvParam's third
parameter as IntPtr and pass this in.

Enclose the call within try/finally block, in finally block, call
Marshal.ZeroFreeCoTaskMemAnsi to release the IntPtr.

Please post your complete code listing if this suggestion doesn't help.
Thanks.

Sincerely,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
For MSDN subscribers whose posts are left unanswered, please check this
document: http://blogs.msdn.com/msdnts/pages/postingAlias.aspx

Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications. If you are using Outlook Express/Windows Mail, please make sure
you clear the check box "Tools/Options/Read: Get 300 headers at a time" to
see your reply promptly.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
J

John Allberg

Hi Walter!

I think you misunderstood what I was trying to say. I boldy state that the
framwork class "CspParameters" with the constructor

CspParameters (Int32, String, String, CryptoKeySecurity, SecureString)

is flawed since it calls the CryptSetProvParam function with the content of
the above SecureString encoded in unicode instead of ascii as the MSDN docs
of CryptSetProvParam states it should. Could you please verify this?

I couldn't even get it to work with MS CSP, but I'm not as familiar with
reading any logs from MS CSP as the one I'm using ("Net iD - CSP").

I have managed to work around this problem with importing CryptSetProvParam,
much the way you suggested, but I thought I call out if somebody else could
verify my finding and perhaps even get MS to fix the problem...

Regards,

John

"Walter Wang [MSFT]" said:
Hi John,

Welcome to MSDN Managed Newsgroup!

Please feel free to correct me if I've misunderstood anything.

To convert the SecureString in CspParameters to ANSI and pass to
CryptSetProvParam, you can use Marshal.SecureStringToCoTaskMemAnsi:

http://msdn2.microsoft.com/en-us/library/system.runtime.interopservices.mars
hal.securestringtocotaskmemansi.aspx

It will return an IntPtr, you need to declare CryptSetProvParam's third
parameter as IntPtr and pass this in.

Enclose the call within try/finally block, in finally block, call
Marshal.ZeroFreeCoTaskMemAnsi to release the IntPtr.

Please post your complete code listing if this suggestion doesn't help.
Thanks.

Sincerely,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
For MSDN subscribers whose posts are left unanswered, please check this
document: http://blogs.msdn.com/msdnts/pages/postingAlias.aspx

Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications. If you are using Outlook Express/Windows Mail, please make sure
you clear the check box "Tools/Options/Read: Get 300 headers at a time" to
see your reply promptly.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no
rights.
 
W

Walter Wang [MSFT]

Hi John,

Thanks for your quick update and sorry for my misunderstanding.

Well, I searched our internal database and did find some fixed bug that was
using Unicode instead of ANSI for the password. However, I just verified
using Reflector (http://www.aisto.com/roeder/dotnet/) that my .NET 2.0 on
Vista has already incorporated the fix.

My %windir%\microsoft.net\framework\v2.0.50727\mscorlib.dll has version
2.0.50727.1378, size 4,399,104 bytes. Would you please check yours and see
if it has the same version?

If you're already having newest version, would you please post your code
which could reproduce the issue you mentioned? Thanks.


Regards,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
G

Guest

Hi Walter,

thanks for your quick reply.

My version was 2.0.50727.832 so I'm obiously some steps behind. It seems the
2.0.50727.832 came from MS07-040.

I checked with MicrosoftUpdate but couldn't find an update so I went to MSDN
and couldn't find one there either.

According to
http://blogs.msdn.com/dougste/archive/2007/07/31/summary-of-fixes-available-for-asp-net-2-0.aspx
the 2.0.50727.1378 version isn't even a released hotfix. I don't like the
thought of installing .Net framework 3.5 beta, do you know if there are any
fixes to download separatly?

Regards,

John
 
W

Walter Wang [MSFT]

Hi John,

I've just verified that the issue does get fixed in 2.0.50727.1378 but not
in 2.0.50727.832. Here's the relevant code extracted using Reflector
(http://www.aisto.com/roeder/dotnet/):

mscorlib.dll

System.Security.Cryptography.Utils

internal static void GetKeyPairHelper(CspAlgorithmType keyType,
CspParameters parameters, bool randomKeyContainer, int dwKeySize, ref
SafeProvHandle safeProvHandle, ref SafeKeyHandle safeKeyHandle)

2.0.50727.832:

else if (parameters.KeyPassword != null)
{
IntPtr pbData =
X509Utils.PasswordToCoTaskMemUni(parameters.KeyPassword);
try
{
_SetProviderParameter(hProv, parameters.KeyNumber, 11, pbData);
}
finally
{
if (pbData != IntPtr.Zero)
{
Marshal.ZeroFreeCoTaskMemUnicode(pbData);
}
}
}


2.0.50727.1378

else if (parameters.KeyPassword != null)
{
IntPtr pbData =
Marshal.SecureStringToCoTaskMemAnsi(parameters.KeyPassword);
try
{
_SetProviderParameter(hProv, parameters.KeyNumber, 11, pbData);
}
finally
{
if (pbData != IntPtr.Zero)
{
Marshal.ZeroFreeCoTaskMemAnsi(pbData);
}
}
}

Sorry for the inconvenience caused.

I cannot seem to find dedicated kb or hotfix to describe this change. As
far as I know we don't have a separate update to update the mscorlib.dll to
version 1378, but I will ask around to see if there's anything I missed.
I'll keep you posted.

Regards,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
G

Guest

Hi Walter,

thank you for your answer, reflector is very usable... :)

Do you think I should open a support issue for this to get a packaged hotfix?

Regards,

John
 
W

Walter Wang [MSFT]

Hi John,

So far I haven't found an existing hotfix for this to upgrade standalone
.NET 2.0 framework. Please go ahead to open a support incident to request
the hotfix. Our CSS (Customer Support and Service) will decide whether or
not to provide a hotfix for you based on your business impact. (For such
confirmed issue of our product, the support incident will be free). You can
refer to this newsgroup thread or me when contacting CSS.

Please feel free to let me know if there's anything else I can help. Thanks.


Regards,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
W

Walter Wang [MSFT]

Hi John,

Just after I posted my last reply, I got updated information that we're
actually having a hotfix for this issue and preparing to publish a KB.
Please email me and I will let you know the KB ID so that you could more
easily request the hotfix. (Sorry I cannot state the KB ID here publicly
before it's published to avoid confusion).

Regards,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top