Я создал пару ключей в python, используя pycrypto
key=RSA.generate(bit_size,os.urandom)
exportedPrivateKey = key.exportKey('PEM', None, pkcs=1).decode("utf-8")
exportedPublicKey = key.publickey().exportKey('PEM', None, pkcs=1).decode("utf-8")
Я написал крошечную утилиту, которая берет хэш сообщения и подписывает хеш...
hash = MD5.new(json_info.encode("utf-8")).digest()
privateKey = RSA.importKey(USER_TOKEN_PRIVATE_KEY)
signature = privateKey.sign(hash,'')
Затем я написал что-то, что использовало открытый ключ, чтобы убедиться, что он прошел проверку в порядке... подпись в моих токенах работает нормально...
hash = MD5.new(packet.encode("utf-8")).digest()
publicKey = RSA.importKey(tokenPublicKey)
if publicKey.verify(hash, signature):
return json.loads(packet)
else:
return None
Теперь, поскольку мне нужно было использовать это как в Java, так и в python, я переносил аналогичную библиотеку на java, но начал сталкиваться с проблемами. А именно, моя проверка всегда будет терпеть неудачу...
Я начинаю с создания объекта PublicKey из экспортированного PEM...
byte[] encoded = Base64.decodeBase64(USER_TOKEN_PUBLIC_KEY);
//decode the encoded RSA public key
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pubKey = kf.generatePublic(keySpec);
Я могу получить подпись, и это точно такая же подпись, и значение хэширует точно такое же значение (ну, похоже; java представляет байты как целые числа со знаком, тогда как python представляет их как беззнаковые, но они одинаковы бинарное представление). Но, похоже, мне всегда не удается проверить мою подпись. Вот что я использую для этого:
byte[] hash = hasher.digest(packet.getBytes("UTF-8"));
InputStream hashStream = new ByteArrayInputStream(hash);
final Signature sign = Signature.getInstance("MD5withRSA");
sign.initVerify(pubKey);
byte[] buffer = new byte[256];
int length;
while ((length = hashStream.read (buffer)) != -1)
sign.update (buffer, 0, length);
hashStream.close();
System.out.println(sign.verify(signature.getBytes("UTF-8")));
К сожалению, это печатает только ложь.
Единственная разница, которую я действительно вижу, заключается в том, что когда я передаю его для проверки в Java, он запрашивает массив длинных чисел, тогда как в python требуется последовательность байтов. Моей лучшей догадкой было взять строковое представление этого длинного и преобразовать его в набор байтов, но это не удалось. Все мои другие попытки также не увенчались успехом (посмотрите на байтовое представление основного большого целого числа, посмотрите на байтовое представление массива и т. д.). Я чувствую, что упускаю что-то ДЕЙСТВИТЕЛЬНО простое, но хоть убей, я не могу понять, что это такое...
Для примера того, как выглядит подпись на питоне, мне дано:
[68830459489863257411523011520104203035626147084548742757940226446079486348431212041096334237130703774949375015187747280487790006116898192460644067270457728626039524097117092304115366780581423597886886987279231850120937691165013216970647150989646220735762034864029622135210042186666476516651349805320771941650]