Лучший способ подписать данные в веб-форме с помощью сертификата пользователя

У нас есть веб-приложение C#, к которому пользователи будут подключаться с помощью цифрового сертификата, хранящегося в их браузерах.

Из примеров, которые мы видели, проверка их подлинности будет легкой, если мы включим SSL, поскольку мы можем получить доступ к полям в сертификате, используя Request.ClientCertificate, чтобы проверить имя пользователя.

Однако нас также попросили подписать данные, отправленные пользователем (несколько простых полей и двоичный файл), чтобы мы могли без сомнений доказать, какой пользователь ввел каждую запись в нашу базу данных.

Нашей первой мыслью было создать небольшую текстовую подпись, включающую поля (и, если возможно, md5 файла) и зашифровать ее приватным ключом сертификата, но...

Насколько я знаю, мы не можем получить доступ к закрытому ключу сертификата для подписи данных, и я не знаю, есть ли способ подписать поля в браузере, или у нас нет другого выбора, кроме как использовать Java апплет. И если последнее, то как бы мы это сделали (есть ли какой-нибудь апплет с открытым исходным кодом, который мы могли бы использовать? Было бы лучше, если бы мы создали его сами?)

Конечно, было бы лучше, если бы был какой-то способ «подписать» поля, полученные на сервере, используя данные, к которым мы можем получить доступ из сертификата пользователя. Но если нет, любая информация о лучшем способе решения проблемы будет оценена по достоинству.


person salgiza    schedule 15.06.2010    source источник


Ответы (4)


Я предлагаю вам использовать java-апплет. Этот подход един для всех браузеров. Мы используем его в наших проектах. Чтобы подписать пользовательские данные, вам потребуется использовать JCA API (http://download.oracle.com/javase/1.4.2/docs/guide/security/CryptoSpec.html#Signature). Также вам нужно будет решить проблему с доступом к хранилищу сертификатов Windows из java.

Вы можете решить это следующим образом:

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());
}
person Sasha    schedule 11.10.2010
comment
Вау! Я не проверял это, но это определенно выглядит хорошо. До тех пор, пока crypto.* API не будет реализован всеми поставщиками браузеров, я думаю, придется использовать Java Applet. - person salgiza; 11.10.2010

Вам нужно будет спросить свой юридический отдел, является ли это достаточно сильным «подписанием», но при каждой публикации полей сервер должен записывать значения полей и отпечаток сертификата из рукопожатия сертификата клиента. Да, это «подделка», поскольку любой, кто знает отпечаток пальца, может создать фальшивую запись в базе данных, но при надлежащей безопасности базы данных вы можете удалить этот вектор атаки и получить запись о том, кто что опубликовал (поскольку вы не можете подделать отпечаток пальца). сертификата клиента при двусторонней аутентификации SSL)

person Scott Chamberlain    schedule 05.10.2010
comment
К сожалению, нам нужно подписать, используя сертификат пользователя. Дело не только в том, что мы (клиент) можем проверить, что данные не были изменены, но и в том, чтобы доказать пользователю, отправившему данные, что мы их не изменили (так что, насколько я знаю, подпись должно быть сделано до того, как данные поступят на сервер). - person salgiza; 07.10.2010

мы не можем получить доступ к закрытому ключу сертификата для подписи данных,

Если бы это было не так, то сервер мог бы запомнить сам сертификат и использовать его, чтобы казаться настоящим клиентом при использовании других служб.

т.е. полностью сделает недействительным использование клиентских сертификатов для аутентификации клиента.

и я не знаю, есть ли способ подписать поля в браузере,

Как насчет того, чтобы задать этот вопрос с подходящими тегами на стороне клиента (например, JavaScript, Browser), чтобы получить нужную аудиторию. (Я уверен, что это можно сделать с помощью расширения браузера или объекта ActiveX, но это создает свои проблемы.)

person Richard    schedule 05.10.2010
comment
Я добавил теги, чтобы посмотреть, поможет ли это. P.S. Я знаю последствия получения сервером закрытого ключа, но, похоже, некоторые государственные учреждения делают это (!!!!), и в этом случае клиент примет его. - person salgiza; 07.10.2010
comment
В своем комментарии выше вы сказали, что клиент должен иметь возможность доказать пользователю, что данные не были изменены. Наличие закрытого ключа сделает невозможным доказательство того, что вы не меняли данные. Также крайне маловероятно, что вы все равно сможете получить закрытый ключ, так что это спорный вопрос. - person Andrew Barber; 10.10.2010
comment
@salgiza нет, государственные учреждения не получают закрытые ключи клиентов. Вот почему он называется закрытым ключом. Аутентификация клиента аналогична аутентификации сервера — то есть, когда вы посещаете защищенный веб-сайт, ваш браузер, конечно же, не получает закрытый ключ сайта при проверке действительности сертификата сервера. - person Pointy; 10.10.2010
comment
@pointy Я бы, конечно, ожидал, что это так. Но в прошлом году предупреждение безопасности, исходящее от Java-апплета, установленного на определенной странице от правительства Испании, сообщило мне, что мой закрытый ключ будет отправлен на сервер на сервер, чтобы подписать данные, которые я отправлял (и отмечая насколько это было небезопасно). Озадаченный (и раздраженный), я отменил подписку, так что не могу точно сказать, было ли это плохо написанное предупреждение, но похоже, что это не так. - person salgiza; 11.10.2010
comment
@Андрей Да, ты прав. Думаю, я немного отчаялся из-за отсутствия ответов, когда писал этот комментарий. - person salgiza; 11.10.2010

Существует метод JavaScript crypto.signText, который позволяет подписывать произвольный текст клиентским сертификатом. Вот некоторые документы и пример подписанной формы. Кажется, в настоящее время это поддерживает только Firefox.

Для IE есть объект CAPICOM ActiveX (доступен для загрузки с сайта MS). Вот пример его использования.

Я не уверен в поддержке этих функций в разных браузерах и версиях ОС, вам, вероятно, придется проверить это для вашей конфигурации.

person VladV    schedule 10.10.2010
comment
Приятно знать, что по крайней мере некоторые поставщики браузеров подумали, что кто-то может захотеть подписывать формы своими сертификатами! Жаль, что разработчики webkit не думали так же :( CAPICOM, безусловно, будет работать для IE (мы действительно использовали его много лет назад), но почему-то я думал, что к настоящему времени появится что-то лучше/более стандартное Тем не менее, спасибо за пример!(И будем надеяться, что crypto.signText будет реализован в других браузерах, так как он выглядит весьма полезным) - person salgiza; 11.10.2010
comment
Ага. Я наткнулся на несколько жалоб на отсутствие этой функции в Chrome. - person VladV; 11.10.2010
comment
@Vlav Я проверил консоль Safari, и криптообъект даже не существует. Я только что проверил, нет ли ошибки в webkit, но это ближайшее совпадение, которое я нашел: bugs.webkit.org/show_bug.cgi?id=24077 О, хорошо :S PS Я ненавижу тот факт, что интро теперь публикует комментарии! (Я продолжаю оставлять комментарии до того, как закончу их писать ;)) - person salgiza; 11.10.2010