1

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?

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
sharif2008
  • 2,716
  • 3
  • 20
  • 34
  • *"why it does not work for SmartCard Token?"* - SmartCards (usually) are made to keep the private key inside. If that could be changed by setting some `CngProperty`, hardware tokens would be superfluous, wouldn't they? Consider using classes like the [X509Certificate2Signature](https://github.com/mkl-public/itext-signing/blob/master/simple.Net/X509Certificate2Signature.cs) or [X509Certificate2SignatureContainer](https://github.com/mkl-public/itext-signing/blob/master/simple.Net/X509Certificate2SignatureContainer.cs) instead. – mkl Oct 06 '21 at 13:39
  • @sharif2008, BTW, you may use free Signer.Digital Browser extension on browser side if it suits you. This extension has been demonstrated to bcc-ca.gov.bd authorities and they have evaluated it. Refer https://stackoverflow.com/a/63173083/9659885 and https://security.stackexchange.com/a/252318/206413 – Bharat Vasant Oct 07 '21 at 06:07
  • @mkl I do agree with your points. My Problem is solved by exploring your code samples. Thanks a lot – sharif2008 Oct 11 '21 at 15:14
  • @BharatVasant Of course, your solution is in our minds. We will let you know if needed. Thanks for your support. – sharif2008 Oct 11 '21 at 15:16
  • @sharif2008 Can you please share your code ? I have similar requirement in which I have to sign a PDF remotely – Gaurav May 15 '22 at 21:58

0 Answers0