c# – Error signing xml passing PIN automatically

Question:

I have this code that selects the certificate according to the serial passed:

public static X509Certificate2Collection SelecionarCertificado(string serial)
{
   X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
   var Key = new RSACryptoServiceProvider();
   store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
   X509Certificate2Collection collection = store.Certificates;
   X509Certificate2Collection fcollection = collection.Find(X509FindType.FindBySerialNumber, serial, true);

  return fcollection;
}

And this part here in the signature, to pass the password automatically:

SignedXml signedXml = new SignedXml(doc);

KeyInfo keyInfo = new KeyInfo();
//signedXml.SigningKey = x509Cert.PrivateKey;
keyInfo.AddClause(new KeyInfoX509Data(x509Cert));
RSACryptoServiceProvider Key = new RSACryptoServiceProvider();
Key = (System.Security.Cryptography.RSACryptoServiceProvider)x509Cert.PrivateKey;
signedXml.SigningKey = Key;
signedXml.KeyInfo = keyInfo;
signedXml.SigningKey = Certificados.LerDispositivo(Key, PIN);
signedXml.SignedInfo.SignatureMethod = SignedXml.XmlDsigRSASHA1Url;
reference.DigestMethod = SignedXml.XmlDsigSHA1Url;

But what happens, when I have two certificates on my machine, it returns invalid password. I changed the passwords to be the same, but in this case it does not return an invalid PIN error, but it signs with the wrong certificate, which returns another error. It's as if the query I make isn't worth anything.

This is the LerDispositivo() function

public static RSACryptoServiceProvider LerDispositivo(RSACryptoServiceProvider key, string PIN)
{
    CspParameters csp = new CspParameters(key.CspKeyContainerInfo.ProviderType, key.CspKeyContainerInfo.ProviderName);
    SecureString ss = new SecureString();

    foreach (char a in PIN)
    {
        ss.AppendChar(a);
    }

    csp.ProviderName = key.CspKeyContainerInfo.ProviderName;
    csp.ProviderType = key.CspKeyContainerInfo.ProviderType;
    csp.KeyNumber = key.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2;
    csp.KeyContainerName = key.CspKeyContainerInfo.KeyContainerName;
    csp.KeyPassword = ss;
    csp.Flags = CspProviderFlags.NoPrompt | CspProviderFlags.UseDefaultKeyContainer;

    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);

    return rsa;
}

I've tried line by line, changed a few things, and it still happens. If you have only one certificate connected, it works perfectly, everything indicates that it takes the first certificate in alphabetical order, because when I'm signing the other, the problem doesn't return.

EDIT

Analyzing the code, I came to the conclusion that the two connected certificates have the same name/ ProviderName :

csp.ProviderName = key.CspKeyContainerInfo.ProviderName;
csp.ProviderType = key.CspKeyContainerInfo.ProviderType;

That's why it's confusing, is there any way to differentiate, so he knows the correct certificate?

Answer:

In the LerDispositivo() function change the line:

csp.Flags = CspProviderFlags.NoPrompt | CspProviderFlags.UseDefaultKeyContainer;

for:

csp.Flags = CspProviderFlags.NoPrompt | CspProviderFlags.UseExistingKey;

See what the CspProviderFlags enum documentation says (used in the CspParameters.Flags property):

UseDefaultKeyContainer => Use key information from the default key container.
UseExistingKey => Use key information from the current key.

It is also worth remembering that the certificate's serial number is not entirely unique, it may be better to use the thumbprint to uniquely identify each certificate, using the X509FindType.FindByThumbprint option instead of the X509FindType.FindBySerialNumber option:

What is the difference between serial number and thumbprint? – Information Security Stack Exchange

Although I don't think this affects it in your current context, as the two certificates are apparently from the same vendor.

Scroll to Top