I am trying to sign a remotely generated pdf hash data using the private key from my SafeNet token using c# and .Net core 3.1 framework. I can successfully sign the hash data using the key stored Windows store. When I select the certificate stored in my SafeNet to sign the data, it never prompts the SafeNet token password rather it throws an exception. But I can sign any XML data using the same certificate from Token.
I have checked some other examples and tutorials on the internet which are mainly based on RSA or RSACryptoServiceProvider which can not sign in case of private is non-exportable
Tried case 1: signing works only for the exportable private key
RSA rsa = x509Certificate2.GetRSAPrivateKey(); // or cert.GetRSAPublicKey() when need public key use the key
AsymmetricCipherKeyPair keypair = DotNetUtilities.GetRsaKeyPair(rsa);
// Hash data = hashByteArray
byte[] signedBytes = rsa.SignData(hashByteArray, System.Security.Cryptography.HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
Tried case 2: signing works only for the exportable private key
ICipherParameters Akp = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(x509Certificate2.PrivateKey).Private;
IExternalSignature iExternalSignature = new PrivateKeySignature(Akp, "SHA-256");
byte[] extSignature = iExternalSignature.Sign(<data array>);
Tried case 3: works for all keys (exportable or non-exportable) stored in windows store but not works using Smart card key and throws an exception
In my case, the private key is always RSACng.
if (x509Certificate2.PrivateKey is RSACryptoServiceProvider)
{
//never comes here
Log.log("Get the RSACryptoServiceProvider");
}
else if (x509Certificate2.PrivateKey is ECDsaCng)
{
//never comes here
Log.log("Get the ECDsaCng");
}
else if (x509Certificate2.PrivateKey is RSACng)
{
// always comes here
Log.log("HasPrivateKey: " + x509Certificate2.HasPrivateKey.ToString());
byte[] signedBytes = sign(selectedCert, hashByteArray);
return signedBytes;
}
Working Code
public static byte[] sign(X509Certificate2Collection selectedCert, byte[] hashByteArray)
{
X509Certificate2 x509Certificate2 = selectedCert[0];
// Coverting to bouncycastle X509Certificate for sign purpose
ICollection<Org.BouncyCastle.X509.X509Certificate> bouncyCertChains = ConvertX509Cert2ListToBouncyCastleX509CertList(selectedCert);
RSA rsa = (RSA)x509Certificate2.PrivateKey;
(x509Certificate2.PrivateKey as RSACng).Key.SetProperty(
new CngProperty(
"Export Policy",
BitConverter.GetBytes((int)CngExportPolicies.AllowPlaintextExport),
CngPropertyOptions.Persist));
RSAParameters RSAParameters = rsa.ExportParameters(true);
Log.log("Get the GetRsaKeyPair");
AsymmetricCipherKeyPair keypair = DotNetUtilities.GetRsaKeyPair(RSAParameters);
Log.log(keypair.ToString());
IExternalSignature iExternalSignature = new PrivateKeySignature(keypair.Private, DigestAlgorithms.SHA256);
PdfPKCS7 pdfpkcs7 = new PdfPKCS7(null, bouncyCertChains.ToArray(), DigestAlgorithms.SHA256, false);
byte[] computedSh = pdfpkcs7.GetAuthenticatedAttributeBytes(hashByteArray, PdfSigner.CryptoStandard.CMS, null, null);
Log.log("calculated SH: " + System.Convert.ToBase64String(computedSh));
byte[] extSignature = iExternalSignature.Sign(computedSh);
pdfpkcs7.SetExternalDigest(extSignature, null, iExternalSignature.GetEncryptionAlgorithm());
byte[] signedBytes = pdfpkcs7.GetEncodedPKCS7(hashByteArray, null, null, null, PdfSigner.CryptoStandard.CMS);
return signedBytes;
}
Exception message for SmartCard Token:
Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Key not valid for use in specified state.
XML Signing: works perfectly for any certificate
SignedXml doc= new SignedXml(xmlDocument);
doc.SigningKey = x509Certificate2.PrivateKey;
doc.ComputeSignature();
Now, my question is why it does not work for SmartCard Token? What am I missing here?