Reputation: 21
I need to send data to a webservice everyday so I made a scheduled task that runs my code. The problem is the webservice requires a certificate with a PIN code. I have attached the certificate but I can't find a way to set the PIN to it, therefor it shows a popup everytime to enter it manually.
Here is my code for the certificate:
private void SendData(string data)
{
using (SerWSService webService = new SerWSService())
{
string certificateSN = "serial number for the certificate";
webService.ClientCertificates.Add(FindCertificate(certificateSN));
webService.SendData(data);
}
}
private X509Certificate2 FindCertificate(string certserial)
{
X509Certificate2 WPE_UserCert = null;
X509Store wstore = default(X509Store);
wstore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
wstore.Open(OpenFlags.ReadOnly);
var wcerts = wstore.Certificates;
foreach (var wcert in wcerts)
{
if (wcert.SerialNumber.ToUpper() == certserial.Replace(" ", "").ToUpper())
{
WPE_UserCert = wcert;
break;
}
}
wstore.Close();
if (WPE_UserCert != null)
{
//TO DO: add PIN code to certificate
}
return WPE_UserCert;
}
Is there any way I can set the PIN to the certificate?
Upvotes: 2
Views: 3970
Reputation: 2171
In the case of a CSP private key use CryptAcquireCertificatePrivateKey
to acquire CryptoProv handle and then use CryptSetProvParam(h,PP_SIGNATURE_PIN,pin,0)
to set the PIN.
Upvotes: 0
Reputation: 33098
No, because certificates don't have PINs; (private) keys do.
If you are finding a certificate with a private key and you pass that certificate to a class that expects the unified pair (e.g. SslStream, HttpClient) then there's no real/good solution.
If you are using the private key yourself, you have some leeway:
using (RSA rsa = cert.GetRSAPrivateKey())
{
RSACng rsaCng = rsa as RSACng;
RSACryptoServiceProvider rsaCsp = rsa as RSACryptoServiceProvider;
if (rsaCng != null)
{
// Set the PIN, an explicit null terminator is required to this Unicode/UCS-2 string.
byte[] propertyBytes;
if (pin[pin.Length - 1] == '\0')
{
propertyBytes = Encoding.Unicode.GetBytes(pin);
}
else
{
propertyBytes = new byte[Encoding.Unicode.GetByteCount(pin) + 2];
Encoding.Unicode.GetBytes(pin, 0, pin.Length, propertyBytes, 0);
}
const string NCRYPT_PIN_PROPERTY = "SmartCardPin";
CngProperty pinProperty = new CngProperty(
NCRYPT_PIN_PROPERTY,
propertyBytes,
CngPropertyOptions.None);
rsaCng.Key.SetProperty(pinProperty);
}
else if (rsaCsp != null)
{
// This is possible, but painful.
// Copy out the CspKeyContainerInfo data into a new CspParameters,
// build the KeyPassword value on CspParameters,
// Dispose() the existing instance,
// and open a new one to replace it.
//
// But if you really called GetRSAPrivateKey() and it has returned an RSACng for
// your device once, it pretty much will forever (until the next
// new RSA type for Windows is invented... at which point it still
// won't return an RSACryptoServiceProvider).
}
else
{
// No other built-in RSA types support setting a PIN programmatically.
}
// Use the key here.
// If you needed to return it, don't put it in a using :)
}
Upvotes: 4