views:

165

answers:

4

We have a C# web app where users will connect using a digital certificate stored in their browsers.

From the examples that we have seen, verifying their identity will be easy once we enable SSL, as we can access the fields in the certificate, using Request.ClientCertificate, to check the user's name.

We have also been requested, however, to sign the data sent by the user (a few simple fields and a binary file) so that we can prove, without doubt, which user entered each record in our database.

Our first thought was creating a small text signature including the fields (and, if possible, the md5 of the file) and encrypt it with the private key of the certificate, but...

As far as I know we can't access the private key of the certificate to sign the data, and I don't know if there is any way to sign the fields in the browser, or we have no other option than using a Java applet. And if it's the latter, how we would do it (Is there any open source applet we can use? Would it be better if we create one ourselves?)

Of course, it would be better if there was any way to "sign" the fields received in the server, using the data that we can access from the user's certificate. But if not, any info on the best way to solve the problem would be appreciated.

+2  A: 
Richard
I've added the tags to see if it helps. P.S. I know the implications of the server receiving the private key, but it seems there are some public institutions doing that (!!!!) and the customer would accept it in this case.
salgiza
In your above comment, you said that the customer needs to be able to prove to the user that the data has not been modified. Being in possession of the private key would make it impossible to prove you did not change the data. It's also highly unlikely you'll be able to get the private key anyway, so it's a moot point.
Andrew Barber
@salgiza no, public institutions are **not** getting client private keys. That's why it's called a "private key". Client authentication is the same as server authentication - that is, when you visit a secure website, your browser certainly does not get the site's private key when it checks the server certificate validity.
Pointy
@pointy I would certainly expect that to be the case. But last year a security warning, coming from a Java applet installed in certain page from the Spanish Government, informed me that my private key was going to be sent to the server to the server in order to sign the data I was submitting (and remarking how insecure that was). Puzzled (and annoyed) I cancelled, so I can't tell for sure if it was a badly written warning, but it didn't seem so.
salgiza
@Andrew Yeah, you are right. I guess I was getting a bit desperate due to the lack of responses when I wrote that comment.
salgiza
+2  A: 

You will need to ask your legal department if this is a strong enough "signing" but with every posting of the fields have the server record the field values and the fingerprint of the cert from the handshake of the client certificate. Yes this is "forgeable" as anyone with knowledge of the fingerprint could create a fake record in the database, but with proper database security you can remove that vector of attack and have a record of who posted what (as you can not fake the fingerprint of the client certificate in a two way authentication of SSL)

Scott Chamberlain
Unfortunately, we need to sign using the user certificate. It's not just a matter of us (the customer) being able to check that the data hasn't been modified, but a matter of proving to the user that sent the data that we didn't modify it (so, AFAIK, the signature should be done before the data arrives to the server).
salgiza
+1  A: 

There is a JavaScript method crypto.signText that allows to sign arbitrary text with client cerificate. Here are some docs and a signed form example. It seems, currently only Firefox supports it.

For IE there is CAPICOM ActiveX object (available as a download from MS). Here is an example of its usage.

I'm not sure about support for these features in different browser and OS versions, you'll probably have to check it for your configuration.

VladV
It's nice knowing that at least some browser vendor thought someone might want to sign forms with their certificates! It's a shame that it looks like webkit developers didn't thought the same thing, though :( CAPICOM would certainly work for IE (we actually used it many years ago), but somehow I thought something better/more standard would have appeared by now. Nevertheless, thanks for the example! (And let's hope that crypto.signText gets implemented in other browsers, as it looks quite useful)
salgiza
Yep. I've stumbled upon a few complaints about this functionality missing from Chrome.
VladV
@Vlav I checked on Safari's console and the crypto object doesn't even exist. I just checked to see if there's a bug for it in webkit, but this is the nearest match I've found: https://bugs.webkit.org/show_bug.cgi?id=24077 Oh, well :S P.S. I hate the fact that intro now submits comments! (I keep submitting comments before I finish writing them ;))
salgiza
+2  A: 

I suggest you to use java-applet. This approach is unified for all browsers. We use it in our projects. To sign the user-data you will need to use JCA API (http://download.oracle.com/javase/1.4.2/docs/guide/security/CryptoSpec.html#Signature). Also you will need to solve problem with accessing windows certificate store from java.

You can solve it in this way:

KeyStore keystore = KeyStore.getInstance("Windows-MY", "SunMSCAPI");

keystore.load(null, null);

HashMap<String,String> userPublicKeys = new HashMap<String,String>();

Enumeration<String> aliasesEnum = keystore.aliases();

if (!aliasesEnum.hasMoreElements())
    throw new Exception("No certificate!");

// list through windows personal store
while (aliasesEnum.hasMoreElements()) 
{
   String alias = aliasesEnum.nextElement();

   boolean isKey = keystore.isKeyEntry(alias);

   if (isKey)
   {
       BASE64Encoder encoder = new BASE64Encoder();
       encoder.encode(keystore.getCertificate(alias).getEncoded());

       userPublicKeys.put(alias, encoder.encode(keystore.getCertificate(alias).getEncoded()));
       System.out.println("Entry alias: " + alias); 
   }

   // sign
   PrivateKey privateKey = (PrivateKey) keystore.getKey(alias,null);           

   Provider provider = keystore.getProvider();
   // data to signed
   byte[] data ="test data".getBytes();
   // Signing the data
   Signature sig = Signature.getInstance("SHA1withRSA", provider);
   sig.initSign(privateKey);

   sig.update(data);
   byte[] signature = sig.sign();

   System.out.println(ByteArrayToFromHexDigits.bytesToHexString(signature).toUpperCase());
}
Sasha
Wow! I haven't tested it, but it certainly looks good. Until the crypto.* API gets implemented by all browser vendors, I guess going with a Java Applet will have to do.
salgiza