.NET Forum / .NET Framework / New Users / October 2007
CspParameter and CryptSetProvParam(PP_KEYEXCHANGE_PIN)
|
|
Thread rating:  |
John Allberg - 19 Sep 2007 08:38 GMT 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());
}
Walter Wang [MSFT] - 19 Sep 2007 11:35 GMT 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 (wawang@online.microsoft.com, 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.
John Allberg - 20 Sep 2007 08:58 GMT 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
> Hi John, > [quoted text clipped - 46 lines] > This posting is provided "AS IS" with no warranties, and confers no > rights. Walter Wang [MSFT] - 20 Sep 2007 12:21 GMT 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 (wawang@online.microsoft.com, 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.
John Allberg - 20 Sep 2007 14:10 GMT 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
Walter Wang [MSFT] - 21 Sep 2007 04:26 GMT 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 (wawang@online.microsoft.com, 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.
John Allberg - 21 Sep 2007 08:46 GMT 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
Walter Wang [MSFT] - 21 Sep 2007 09:36 GMT 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 (wawang@online.microsoft.com, 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.
Walter Wang [MSFT] - 21 Sep 2007 10:18 GMT 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 (wawang@online.microsoft.com, 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.
Dan_Phillips - 13 Oct 2007 14:07 GMT Walter, We are experiencing a similar problem with the cspparameters method and the keypassword parameter. We are using a smartcard and trying to read the cert from the card and the pass the password to the card thus avoiding having the login dialog appear prompting for the password.
We are using c# to pass a secure string to the csppararameters keypassword parameter and are are getting a mesage back that our password is incorrect. We are sure the password is correct and have found other developers who have found the same problem.
// To idendify the Smart Card CryptoGraphic Providers on your // computer, use the Microsoft Registry Editor (Regedit.exe). // The available Smart Card CryptoGraphic Providers are listed // in HKEY_LOCAL_MACHINE\Software\Microsoft\Cryptography\Defaults\ Provider.
// Create a new CspParameters object that identifies a // Smart Card CryptoGraphic Provider.
CspParameters cspp = new CspParameters(1, "Datakey RSA CSP");
System.Security.SecureString sss = new System.Security. SecureString(); sss.AppendChar('1'); sss.AppendChar('2'); sss.AppendChar('3'); sss.AppendChar('4');
cspp.KeyPassword = sss; cspp.Flags = CspProviderFlags.UseDefaultKeyContainer;
RSACryptoServiceProvider rsaa = new RSACryptoServiceProvider(cspp) ;
Error occurs here. If we remove the cspp.KeyPassword = sss; line the code prompts us for the password and the rest of the code works fine.
From the sounds of this thread John is experiencing the same problem. Can we ge the hot fix also as we have the 2.0.50727.832 version of the mscorlib.dll file.
Your help would be greatly appreciated as we have been trying to get this fixed for sometime.
Thanks in advance
Dan Phillips dphillips210 (at) aol.com
>Hi John, > [quoted text clipped - 14 lines] > >This posting is provided "AS IS" with no warranties, and confers no rights.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|