Преобразование Java в подпись Python DSA
Кто-нибудь знает, как я буду конвертировать этот Java-код в Python?
/**
* signs the data for the account account
*/
private byte[] sign(String pkStr,byte[] data, String keyType) throws Exception {
BASE64Decoder decoder = new BASE64Decoder();
KeyFactory keyFac = null;
//instantiate the key factory based on the key alg type
if(keyType.equals("DSA")){
keyFac = KeyFactory.getInstance("DSA");
}else if(keyType.equals("RSA")){
keyFac = KeyFactory.getInstance("RSA");
}
//generate the public key
PKCS8EncodedKeySpec dprks = new PKCS8EncodedKeySpec(decoder.decodeBuffer(pkStr));
PrivateKey pk = keyFac.generatePrivate(dprks);
return(signBytes(data,pk,keyType));
}
/**
* sign the data with the key
*/
private byte[] signBytes(byte [] data,
PrivateKey signingPrivateKey, String keyType)throws Exception {
Signature dsa = null;
//instantiate the signature alg based on the key type
if(keyType.equals("DSA")){
dsa = Signature.getInstance("SHA1withDSA");
}else if(keyType.equals("RSA")){
dsa = Signature.getInstance("SHA1withRSA");
}
/* Initializing the object with a private key */
dsa.initSign(signingPrivateKey);
/* Update and sign the data */
dsa.update(data);
byte[] sig = dsa.sign();
return sig;
}
"Тип ключа", кажется, всегда передается как "DSA", поэтому я посмотрел на M2Crypto.DSA, и это выглядит многообещающе. Однако функция DSA.sign возвращает кортеж из 2-байтовых строк, с которыми я не совсем уверен, что делать.
2 ответа
Сигнатура DSA определяется как пара целых чисел (называемых r и s соответственно). Стандарт DSA не предписывает конкретное кодирование такой подписи в последовательность байтов. Таким образом, каждый протокол, использующий подписи DSA, должен определять свою собственную кодировку.
Существует два обычно используемых кодировки подписи DSA; один - это прямая конкатенация беззнаковых кодировок с прямым порядком байтов значений r и s, оба из которых нормализуются к длине (в байтах) параметра q в открытом ключе ("размер подгруппы", обычно 160-битное простое число) целое число, что дает 40-байтовую подпись). Документация к M2Crypto.DSA довольно краткая, но я предполагаю, что она возвращает r и s отдельно, но уже в этом формате.
Java использует другую кодировку, основанную на ASN.1. Эта кодировка используется в X.509 и во всем, что на ней основано (включая подписи в SSL/TLS). ASN.1 является общим стандартом для представления и сериализации структурированных данных. В этом случае подпись должна быть сериализацией ASN.1 SEQUENCE
содержащий два INTEGER
значения (r и s, в этом порядке). Согласно правилам кодирования ASN.1 и DER подпись должна иметь следующий формат:
0x30 A 0x02 BR 0x02 CS
где:
R - кодирование со знаком с прямым порядком байтов r минимальной длины: это означает, что первый байт должен иметь значение от 0 до 127, и он должен иметь значение 0, только если второй байт имеет значение от 128 до 255. В Другими словами, закодируйте r как последовательность байтов с соглашением с прямым порядком байтов (старший значащий байт идет первым), убедившись, что у вас как можно меньше начальных нулевых битов, при условии, что вы сохраняете хотя бы один (это то, что "подписано" "кодирование означает: так как r положительно, его старший значащий бит должен быть 0). Поскольку r является целым числом от 0 до q-1, длина R будет не более чем на один байт больше длины q, но она может быть меньше.
S - это кодировка со знаком с прямым порядком байтов s (такой же, как и для r; примечание: R и S могут иметь различную длину).
B - это один байт, содержащий длину R (в байтах).
C - это один байт, содержащий длину S (в байтах).
A - это один байт, содержащий B + C + 2 (то есть длину в байтах того, что следует за байтом A).
Написание специализированных функций кодирования и декодирования для подписей DSA на основе ASN.1 немного утомительно, но не сложно; просто позаботьтесь о получении последовательностей R и S нужного размера. В качестве альтернативы вы можете использовать существующую библиотеку кодирования / декодирования ASN.1, которая излишня, но может быть проще, в зависимости от вашей ситуации.
Согласно http://download.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html (по какой-то странной причине, у которой есть два Приложения B, и вам нужно прокрутить вниз до второго) Java использует кодировку ASN.1 SEQUENCE ::= { r INTEGER, s INTEGER }
Вы должны быть в состоянии генерировать (и анализировать) это в Python, используя pyasn1 - http://pyasn1.sourceforge.net/
ASN.1 является стандартом для кодирования двоичных данных. Таким образом, приведенная выше информация указывает, как код Java объединяет два значения, которые возвращает код Python. Поэтому вы можете сделать то же самое, и, таким образом, поддерживать тот же формат байтов для подписи.