Search code examples
c#digital-signaturex509certificatex509certificate2

Override X509Certificate2 PIN behaviour


I am trying to perform 3 types of digital signature (SHA256) at once, XML, PDF and Text using smart card device. All signatures are working perfectly, but problem is, it is asking for PIN when each signing takes place, but I need it to be asked only once. Can anybody suggest a better way for achieving the result?

What I am trying to implement is,

Ask pin -> Sign XML -> Sign PDF -> Sign TEXT

What was happening is,

Ask pin -> Sign XML -> Ask pin -> Sign PDF -> Ask pin -> Sign TEXT

Then I created a common cmssiger object for PDF and TEXT signing.

Now what happening is,

Ask pin -> Sign XML -> Ask pin -> Sign PDF -> Sign TEXT

I hope everyone understood what I am saying.

Code for each signing process is as follows, XML

XAdESSignedXml signer = new XAdESSignedXml(toSign);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa = cert.PrivateKey as RSACryptoServiceProvider;
signer.SigningKey = rsa;
/*.......Elements attached......*/
signer.ComputeSignature();

PDF

private byte[] SignMsg(Byte[] msg, bool detached)
{
    ContentInfo contentInfo = new ContentInfo(msg);
    SignedCms signedCms = new SignedCms(contentInfo, detached);
    _cmsSigner.IncludeOption = X509IncludeOption.EndCertOnly;//common cmssigner object
    _cmsSigner.DigestAlgorithm.FriendlyName = "SHA256";
    signedCms.ComputeSignature(_cmsSigner, false);
    byte[] bb = signedCms.Encode();
    CmsSignedData sd = new CmsSignedData(bb);
    SignerInformationStore signers = sd.GetSignerInfos();
    byte[] signature = null;
    SignerInformation signer = null;
    foreach (SignerInformation signer_ in signers.GetSigners())
    {
        signer = signer_;
        break;
    }
    signature = signer.GetSignature();
    signer = SignerInformation.ReplaceUnsignedAttributes(signer, null);
    IList signerInfos = new ArrayList();
    signerInfos.Add(signer);
    sd = CmsSignedData.ReplaceSigners(sd, new SignerInformationStore(signerInfos));
    bb = sd.GetEncoded();
    return bb;
}

TEXT

public static string Sign(string msg, CmsSigner cmsSigner) //common cmssigner object
{
    SHA256Managed crypt = new SHA256Managed();
    string hash = String.Empty;
    byte[] crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(msg), 0, Encoding.UTF8.GetByteCount(msg));
    foreach (byte theByte in crypto)
    {
        hash += theByte.ToString("x2");
    }
    ContentInfo contentInfo = new ContentInfo(Encoding.UTF8.GetBytes(hash));
    SignedCms cms = new SignedCms(contentInfo);
    cmsSigner.IncludeOption = X509IncludeOption.EndCertOnly;
    cmsSigner.DigestAlgorithm.FriendlyName = "SHA256";
    cms.ComputeSignature(cmsSigner, false);
    return Convert.ToBase64String(cms.Encode());
}

Thanks in advance.


Solution

  • Pin prompt behavior is a provider specific implementation detail. In other words, one cryptographic service provider / key storage provider will behave one way while another will behave another.

    To reduce chances of pin prompting you will want to use one session for all operations, it sounds like you are doing that though so were back to the provider-specific behavior.

    In the case of RSACryptoServiceProvider a key can be generated and stored in such a way it enforces an on-use pin prompt policy. This is really the only pin prompt behavior it exposes, as such your stuck with the observed behavior with such keys.