Проверка подписи DSA, сгенерированной PKCS#11 с OpenSSL
Я хочу подписать хэш SHA-256 с DSA, используя оболочку Java PKCS#11 для API PKCS#11 модуля аппаратной защиты. Для этого я выбрал Механизм CKM_DSA, загрузил соответствующий ключ DSA из токена и подписал данные (считанные как байтовый массив). Ключ, который я использую для тестирования, имеет длину 1024 бита.
Кажется, все работает нормально: ключ загружен, Session.sign() возвращает массив byte[] длиной 40. Это соответствует спецификации PKCS#11, которая гласит:
"Для целей этого механизма сигнатура DSA представляет собой 40-байтовую строку, соответствующую объединению значений r и s DSA, каждое из которых представляет собой старший значащий байт первым".
Теперь я хочу проверить эту подпись, используя openSSL, то есть, используя
openssl dgst -d -sha256 -verify ${PUBLIC_KEY} -signature signature.der <raw input file>
Это работает, если я
а) создал подпись с помощью OpenSSL
b) создал подпись с использованием bouncycastle и закодировал результат как последовательность DER в кодировке ASN1.
Теперь я хочу сделать то же самое с подписью PKCS#11. Мой вопрос: как отформатировать этот 40-байтовый массив? Я попробовал следующее:
//sign data
byte[] signedData = this.pkcs11Session.sign(dataToSign);
//convert result
byte[] r = new byte[20];
byte[] s = new byte[20];
System.arraycopy(signedData, 0, r, 0, 20);
System.arraycopy(signedData, 19, s, 0, 20);
//encode result
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(new ASN1Integer(r));
v.add(new ASN1Integer(s));
return new DERSequence(v).getEncoded(ASN1Encoding.DER);
Часть кодирования кажется правильной, потому что она работает, если я создаю r и s напрямую с bouncycastle и другим программным ключом. Кроме того, openssl принимает формат ввода, но проверка иногда завершается с ошибкой, иногда просто с "Ошибка проверки".
Таким образом, я предполагаю, что преобразование подписи PKCS#11 в r и s неверно. Может ли кто-нибудь помочь найти ошибку?
1 ответ
Вы, вероятно, должны конвертировать r
а также s
значения в BigInteger
класс, прежде чем сделать. Причина этого заключается в том, что ASN.1 использует кодирование со знаком, а DH приводит к кодированию без знака. Таким образом, у вас довольно высокий шанс получить отрицательное значение в ASN.1, что приведет к ошибке.
Чтобы выполнить преобразование, используйте new BigInteger(1, r)
а также new BigInteger(1, s)
и положить результат в ASN1Integer
экземпляров. Здесь 1 указывает, что значение необходимо преобразовать в положительное значение (т. Е. Вход является положительным без знака).