<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title type="html"><![CDATA[Форум Рутокен &mdash; Проверка подписи в Java]]></title>
	<link rel="self" href="https://forum.rutoken.ru/feed/atom/topic/2103/" />
	<updated>2014-08-06T08:55:43Z</updated>
	<generator>PunBB</generator>
	<id>https://forum.rutoken.ru/topic/2103/</id>
		<entry>
			<title type="html"><![CDATA[Re: Проверка подписи в Java]]></title>
			<link rel="alternate" href="https://forum.rutoken.ru/post/7437/#p7437" />
			<content type="html"><![CDATA[<p>Вы правы, в предложенном коде не была учтена необходимость экспорта публичного ключа. Провайдер JRT11 разрабатывался в первую очередь для работы с ГОСТами и сейчас не имеет нужной вам функциональности.<br />У разных провайдеров будут отличаться форматы и механизмы, от этого никак не уйдешь. Пытаться эмпирическим путем нащупать лазейку мимо интерфейсов сквозь внутренние API, чтобы привести все к единому знаменателю -- не самый простой, красивый и безопасный путь.</p><div class="quotebox"><blockquote><p>Кроме того, мне не нравится сам принцип построения JCA API, ибо по моим представлением, работа с токеном идет по алгоритму:<br />1) Нашли все токены в системе<br />2) По какому-то признаку (серийник, метка, или токен всего один подключен) выбрали нужный<br />3) Залогинились на токен с PIN-кодом<br />4) Начали выполнять криптографические операции</p></blockquote></div><p> Тяжело представить другой алгоритм работы в принципе для токена в PKCS11, так как он заложен стандартом. Единственное, при отсутствии признаков подключение будет происходить к первому подключенному токену.</p><p>Работа напрямую с PKCS11 библиотекой через связку JNI + sun.wrapper подошла бы лучше. Собственно, вы уже так работаете, только через внутренности нашего провайдера. Из необходимых требований -- наличие нашей библиотеки (устанавливается вместе с драйверами).</p><p>Так или иначе -- благодарим вас за фидбек, посыл пойман, будем стараться делать наш продукт лучше.</p>]]></content>
			<author>
				<name><![CDATA[Olga Dagaeva]]></name>
				<uri>https://forum.rutoken.ru/user/9333/</uri>
			</author>
			<updated>2014-08-06T08:55:43Z</updated>
			<id>https://forum.rutoken.ru/post/7437/#p7437</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: Проверка подписи в Java]]></title>
			<link rel="alternate" href="https://forum.rutoken.ru/post/7436/#p7436" />
			<content type="html"><![CDATA[<p>JCA через sunPKCS11 я не использовал. Проверка подписи через Cipher - оффлайн операция по отношению к токену, и она использует стандартный провайдер JVM. Собственно, ради это все и затевалось - необходимо проверить подпись, сделанную токеном на другой машине, которая токена не имеет. И для этого надо экспортировать публичный ключ в стандартном формате, и проверять при помощи других инструментов. Ибо, как я видел по коду, ваш класс Signature делает ровно то же самое - создает класс MessageDigest, извлекает TokenObj из ключа, который ему дали и в процессе подписи вызывает TokenObj.sign(). Что, кстати, не совместимо с форматом подписи в Signature, ибо там подписывается ASN.1 объект. В этом же приложении надо было расшифровывать RSA через токен. Судя по исходному коду класса Cipher, он умеет только ГОСТ-овые алгоритмы, а через TokenSession.decryptSingle - получилось.</p><p>Кроме того, мне не нравится сам принцип построения JCA API, ибо по моим представлением, работа с токеном идет по алгоритму:</p><p>1) Нашли все токены в системе<br />2) По какому-то признаку (серийник, метка, или токен всего один подключен) выбрали нужный<br />3) Залогинились на токен с PIN-кодом<br />4) Начали выполнять криптографические операции</p><p>Единственное, что более-менее удовлетворяет таким критериям - класс SessionFactory, который описан у вас в документации. Собственно, от него я и начинал изучение внутреннего API и обнаружил, что SessionFactory корнями уходит в JRT11. Остальные же классы - Cipher, Signature, KeyPairGenerator нуждаются, как минимум, в указании PIN-кода каждый раз, а по-хорошему - в задании серийника, чтобы не получилось так, что ты сгенерировал ключ на одном токене, а попытался подписать на другом. И, как я уже сказал, имеются проблемы совместимости. Подпись, сгенерированная вашим классом Signature, только на нем и проверяется, методы SHA256withRSA и SHA256withrtRSA несовместимы. Ключи возвращают null для getEncoded(), и прочие существенные недостатки.</p>]]></content>
			<author>
				<name><![CDATA[makkarpov]]></name>
				<uri>https://forum.rutoken.ru/user/9245/</uri>
			</author>
			<updated>2014-08-06T07:24:41Z</updated>
			<id>https://forum.rutoken.ru/post/7436/#p7436</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: Проверка подписи в Java]]></title>
			<link rel="alternate" href="https://forum.rutoken.ru/post/7435/#p7435" />
			<content type="html"><![CDATA[<p>Судя по коду, вы используете микс из внутренней реализации JRT11 провайдера (к слову, не предназначенной для использования извне) и интерфейса JCA c sunPKCS11 провайдером. Да, у вас получилось найти ключи на токене, извлечь их и корректно сконвертировать для JCA, получить хеш сторонней утилитой и проверить подпись через интерфейс расшифрования. Вероятно, в итоге вы получили верный результат, а вместе с тем тяжелый код и использование нескольких технологий одновременно, в том числе и не предназначенных для вашей целей.</p><p>Можно было бы использовать JRT провайдер через предназначенный для его использования JCA интерфейс. Тогда бы код сократился до:<br /></p><div class="codebox"><pre><code>Config config = new Config(&quot;C:\\Windows\\System32\\rtPKCS11ECP.dll&quot;, &quot;&quot;, &quot;12345678&quot;);
int pos = Security.addProvider(new ru.rutoken.jrt11.JRT11Provider(config));

KeyStore keyStore = KeyStore.getInstance(&quot;rtStore&quot;, &quot;JRT11&quot;);
keyStore.load(null, &quot;12345678&quot;.toCharArray());

PrivateKey privateKey = (PrivateKey)keyStore.getKey(&quot;Sample RSA Private Key&quot;, null);
KeyStore.Entry entry = keyStore.getEntry(&quot;Sample RSA Public Key&quot;, null);
PublicKey publicKey = ((ru.rutoken.security.KeyContainer)entry).getPublicKey();
byte[] data = &quot;Message&quot;.getBytes();

Signature signature = Signature.getInstance(JRT11Provider.MD5_RSA_SIGNATURE_ALGORITHM, &quot;JRT11&quot;);
signature.initSign(privateKey);
signature.update(data);
byte[] signatureValue = signature.sign();

signature.initVerify(publicKey);
signature.update(data);
boolean result = signature.verify(signatureValue);</code></pre></div><p>На сегодня JRT11 провайдер умеет работать с подписью с RSA только со связкой MD5. Полное руководство по провайдеру можно найти тут: <a href="http://dev.rutoken.ru/pages/viewpage.action?pageId=4227190">http://dev.rutoken.ru/pages/viewpage.ac … Id=4227190</a></p><p>Можно было бы использовать JCA интерфейс через sunPKCS11 провайдер полностью. Единственное -- для работы с ключами на токене необходимо создать и записать на токен сертификат для ключевой пары.</p><p>Можно было бы использовать функции PKCS11 библиотеки rtPKCS11.dll/rtPKCS11ECP.dll напрямую через JNI и sun.security.pkcs11.wrapper, через наш враппер или IAIK PKCS#11 Wrapper. Тут код полностью аналогичен сишному (присутствует в Рутокен SDK), абсолютно корректен и не имеет никаких ограничений в реализациях.</p>]]></content>
			<author>
				<name><![CDATA[Olga Dagaeva]]></name>
				<uri>https://forum.rutoken.ru/user/9333/</uri>
			</author>
			<updated>2014-08-05T23:40:57Z</updated>
			<id>https://forum.rutoken.ru/post/7435/#p7435</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: Проверка подписи в Java]]></title>
			<link rel="alternate" href="https://forum.rutoken.ru/post/7430/#p7430" />
			<content type="html"><![CDATA[<p>Глянув на отрицательный modulus после конвертирования в BigInteger, понял, что он должен быть конвертирован без учета знака. Исправленный вариант для тех, кто будет ковыряться:</p><p>1) Создавать числа не через <strong>new BigInteger(byte[])</strong>, а через <strong>new BigInteger(1, byte[])</strong><br />2) Подпись представляет из себя аргумент функции <strong>sign</strong>, зашифрованный в режиме <strong>RSA/ECB/PKCS1Padding</strong> с ключом на токене. Соответственно, обратное преобразование - расшифровать подпись в таком режиме и проверить на совпадение с тем, что передавалось в функцию sign().</p><p>Код: <br /></p><div class="codebox"><pre><code>Session tokenSession = token.createSession();
tokenSession.login(pin);
            
TokenObj privKey = tokenSession.findObjects(new AttributeList(new Attribute[]{
    Attribute.CLASS_PRIVATE_KEY, Attribute.PRIVATE_TRUE, Attribute.TOKEN_TRUE,
    new Attribute(PKCS11Constants.CKA_LABEL, ruToken.KEY_LABEL)
}))[0];
            
byte[] signature = privKey.sign(CryptoUtils.sha256(contents), Mechanism.SIGN_RSA);
    
BigInteger modulus = new BigInteger(1, (byte[]) privKey.getAttributeValue(PKCS11Constants.CKA_MODULUS));
BigInteger pubExp  = new BigInteger(1, (byte[]) privKey.getAttributeValue(PKCS11Constants.CKA_PUBLIC_EXPONENT));
        
PublicKey localKey = KeyFactory.getInstance(&quot;RSA&quot;).generatePublic(new RSAPublicKeySpec(modulus, pubExp));
            
Cipher c = Cipher.getInstance(&quot;RSA/ECB/PKCS1Padding&quot;);
c.init(Cipher.DECRYPT_MODE, localKey);
System.out.println(Arrays.equals(c.doFinal(signature), CryptoUtils.sha256(contents)));</code></pre></div>]]></content>
			<author>
				<name><![CDATA[makkarpov]]></name>
				<uri>https://forum.rutoken.ru/user/9245/</uri>
			</author>
			<updated>2014-08-02T14:03:30Z</updated>
			<id>https://forum.rutoken.ru/post/7430/#p7430</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Проверка подписи в Java]]></title>
			<link rel="alternate" href="https://forum.rutoken.ru/post/7429/#p7429" />
			<content type="html"><![CDATA[<p>Здравствуйте.</p><p>Имеется токен и RSA-ключи на нем. Нужно подписать данные при помощи токена и потом проверить подпись на удаленной машине, где токена нет. Мой порядок действий:</p><p>1) Извлекается ключ при помощи session.findObjects()<br />2) Данные для подписи хешируются и подписываются через object.sign(data, Mechanism.SIGN_RSA)<br />3) Через findObjects() извлекается публичный ключ, из него при помощи getAttributeValue() извлекаются значения аттрибутов CKA_PUBLIC_EXPONENT и CKA_MODULUS<br />4) Из модуля и публичной экспоненты собирается публичный ключ через KeyFactory.generatePublic()</p><p>Дальше возникает проблема. Как я понял, в функцию sign передаются уже хешированные данные, т.к. никаких намеков на выбор алгоритма хеширования там нету. Однако, как проверить подпись, я не знаю. Попытка сделать это через класс Signature проваливается с возвратом false на методе verify(), попытка &quot;заглянуть внутрь&quot; при помощи класса Cipher, расшифровав подпись при помощи публичного ключа, проваливается с исключением &quot;Message is larger than modulus&quot;.</p><p>Код, делающий описанные выше вещи: <a href="https://pastebin.com/sdCnhADC">https://pastebin.com/sdCnhADC</a></p>]]></content>
			<author>
				<name><![CDATA[makkarpov]]></name>
				<uri>https://forum.rutoken.ru/user/9245/</uri>
			</author>
			<updated>2014-08-02T12:48:54Z</updated>
			<id>https://forum.rutoken.ru/post/7429/#p7429</id>
		</entry>
</feed>
