11

I need to enable my applications' users to sign their approvals with their personal USB security token.

I've managed to sign data but I haven't been able to get the information of who's token has been used to do so.

Here is the code I have so far:

CspParameters csp = new CspParameters(1, "SafeNet RSA CSP");
csp.Flags = CspProviderFlags.UseDefaultKeyContainer;            
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);
// Create some data to sign. 
byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 };
Console.WriteLine("Data         : " + BitConverter.ToString(data));
// Sign the data using the Smart Card CryptoGraphic Provider.            
byte[] sig = rsa.SignData(data, "SHA1");            
Console.WriteLine("Signature    : " + BitConverter.ToString(sig));

There is a field in token's information called "Token Name". How can I access that field in order to validate which token has been used to sign the approval?

enter image description here

Additional info and update:

  • "Token name" always match owner's name (the user who owns the usb token)
  • It seems like it can't be done, maybe there is a web service or something I need to call in order to get the information directly from the cert authority.
Jason Aller
  • 3,541
  • 28
  • 38
  • 38
daniloquio
  • 3,822
  • 2
  • 36
  • 56
  • Maybe this is more of a question for security.stachexchange.com ? – MiMo Feb 28 '13 at 14:37
  • Looks like you would need to extract a [Modulus](http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsaparameters.modulus.aspx) as a certificate fingerprint and then compare to the database of all the available certificates' fingerprints. Probably something like [`byte[] modulus = rsa.ExportParameters(false).Modulus;`](http://attercop.googlecode.com/svn-history/r8/trunk/AttercopClient/SettingsWindow.cs) – oleksii Feb 28 '13 at 14:55
  • @oleksii I don't have a modulus database. I need to get the "Token name" and compare it to the current AD user name. Thanks for your comment. – daniloquio Feb 28 '13 at 15:02
  • I am not too sure what's a token, is it a private RSA certificate? I don't think you can embed such information at all in the certificate. You need a 3-rd party you can trust, i.e. your database, the third party will either acknowledge that this fingerprint is for Bob's private certificate, or it will reject it. If you embed, say, user name into the private cert, then anyone can create a new private certificate and claim to be Bob. How can you trust that? – oleksii Feb 28 '13 at 15:14
  • 1
    Is Token Name something unique to the hardware or part of the standard? It's quite possible that it's stored in such a way that you can't get at it via standard libraries. – Bobson Feb 28 '13 at 15:15
  • @oleksii each user in the company has a USB device that allows them to, for example, access the company's private network from their homes. They also can sign PDF files or emails. When they sign a PDF, it gets a mark indicating the user's name and other info. – daniloquio Feb 28 '13 at 15:23
  • @Bobson Token Name is standard, always have the user Name in the exact format of the user's name in active directory. I'm starting to think what you mentioned: there is no way to get that information with the .NET framework – daniloquio Feb 28 '13 at 15:31
  • @daniloquio - Standard is different from "a standard". It could be something that's unique to SafeNet (but set in a standard manner) in which case .NET has no way to know about it. Or it could be part of the RSA standard, in which case there should be *some* way to access it. My guess is the former. – Bobson Feb 28 '13 at 15:48

1 Answers1

8

When I had originally asked the question my understanding of digital certificates was very basic, so the question wasn't properly asked. Now I understand that I needed to access a certificate from a smart card device, query its attributes and test if the user could enter the right PIN for it.

Here is the code I used to do so:

//Prompt the user with the list of certificates on the local store.
//The user have to select the certificate he wants to use for signing.
//Note: All certificates form the USB device are automatically copied to the local store as soon the device is plugged in.
X509Store store = new X509Store(StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509CertificateCollection certificates = X509Certificate2UI.SelectFromCollection(store.Certificates,
                                                                                "Certificados conocidos",
                                                                                "Por favor seleccione el certificado con el cual desea firmar",
                                                                                X509SelectionFlag.SingleSelection
                                                                                );
store.Close();
X509Certificate2 certificate = null;
if (certificates.Count != 0)
{
    //The selected certificate
    certificate = (X509Certificate2)certificates[0];
}
else
{
    //The user didn't select a certificate
    return "El usuario canceló la selección de un certificado";
}
//Check certificate's atributes to identify the type of certificate (censored)
if (certificate.Issuer != "CN=............................., OU=................., O=..., C=US")
{
    //The selected certificate is not of the needed type
    return "El certificado seleccionado no corresponde a un token ...";
}
//Check if the certificate is issued to the current user
if (!certificate.Subject.ToUpper().Contains(("E=" + pUserADLogin + "@censoreddomain.com").ToUpper()))
{
    return "El certificado seleccionado no corresponde al usuario actual";
}
//Check if the token is currently plugged in
XmlDocument xmlDoc = new XmlDocument();
XmlElement element = xmlDoc.CreateElement("Content", SignedXml.XmlDsigNamespaceUrl.ToString());
element.InnerText = "comodin";
xmlDoc.AppendChild(element);
SignedXml signedXml = new SignedXml();
try
{
    signedXml.SigningKey = certificate.PrivateKey;
}
catch
{
    //USB Token is not plugged in
    return "El token no se encuentra conectado al equipo";
}
DataObject dataObject = new DataObject();
dataObject.Data = xmlDoc.ChildNodes;
dataObject.Id = "CONTENT";
signedXml.AddObject(dataObject);
Reference reference = new Reference();
reference.Uri = "#CONTENT";
signedXml.AddReference(reference);
//Attempt to sign the data. The user will be prompted to enter his PIN
try
{
    signedXml.ComputeSignature();
}
catch
{
    //User didn't enter the correct PIN
    return "Hubo un error confirmando la identidad del usuario";
}
//The user has signed with the correct token
return String.Format("El usuario {0} ha firmado exitosamente usando el token con serial {1}", pUserADLogin, certificate.SerialNumber);

Sources:

http://stormimon.developpez.com/dotnet/signature-electronique/ (en Francais) https://www.simple-talk.com/content/print.aspx?article=1713 (in English)

Kush
  • 438
  • 1
  • 7
  • 20
daniloquio
  • 3,822
  • 2
  • 36
  • 56
  • There is an alternative approach to get the token name. You will have to use either PKCS#11 or CryptoAPI interface to detect which tokane has been inserted. This is different from your approach to deteting the token name based on certificates subject name. – Raj May 13 '14 at 16:15