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());
}
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());
}