#java #c# #rsa #digital-signature
#java #c# #rsa #цифровая подпись
Вопрос:
Мне нужно проверить подпись, созданную на C #, используя алгоритм «SHA1» в моей JAVA-программе, которая использует «SHA1withRSA».Байты подписи не совпадают. Я использую открытый ключ, сгенерированный программой C #, для проверки подписи, которая хранится в файле. Я новичок в криптографии. Ниже приведен код C # для создания подписи :
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSA.FromXmlString(privateKey);
var encoder = new UTF8Encoding();
byte[] originalData = encoder.GetBytes(message);
SHA1 sha1 = SHA1.Create();
byte[] signedBytes = RSA.SignData(originalData, sha1);
return signedBytes;
Я пытаюсь проверить подпись в программе Java, как показано ниже :
//read xml file to get modulus and exponent bytes
File publicKeyFileQA = new File(PUBLIC_KEY_FILE_QA);
Map<String, BigInteger> publicKeyModulusExponentValues = DSXCRM_3YBP_Global_WebServicesUtil.readXMLFile(publicKeyFileQA);
BigInteger publicKeyModulus = publicKeyModulusExponentValues.get("modulus");
BigInteger publicKeyExponent = publicKeyModulusExponentValues.get("exponent");
System.out.println("BigInteger Modulus : " publicKeyModulus "BigInteger Exponent : " publicKeyExponent);
String messageWithSignature = (String) mapDataToPost.get("SignedMessage");
String encryptedMessage = (String) mapDataToPost.get("EncryptedMessage");
byte[] signatureBytes = DatatypeConverter.parseBase64Binary(messageWithSignature);
System.out.println("Signature bytes : " new String(signatureBytes));
byte[] cipherMessage = DatatypeConverter.parseBase64Binary(encryptedMessage);
System.out.println("Cipher Message : " new String(cipherMessage));
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(publicKeyModulus, publicKeyExponent);
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
Signature sig = Signature.getInstance("SHA1withRSA");
sig.initVerify(publicKey); //public key of sender
sig.update(cipherMessage);
boolean isRightSender = sig.verify(signatureBytes);
System.out.println("isRightSender : " isRightSender);
но результат для сопоставления подписи равен false. Я не понимаю, что не так. Не могли бы вы, пожалуйста, предоставить какие-либо предложения. Существует ли какой-либо другой алгоритм, совместимый как с C #, так и с JAVA, чтобы иметь одинаковые подписи? Спасибо!
Комментарии:
1. Я не вижу никаких явных ошибок в вашем коде. Может быть, это кодировка байтов сообщения и подписи, отправленных с C # на Java?
2. да, я согласен с вами. возможно, я столкнулся с какой-то проблемой с кодировкой.
Ответ №1:
Вы считываете поле с именем «SignedMessage», сохраняете его в переменной с именем «messageWithSignature», а затем обрабатываете его так, как если бы это была просто подпись. Какие данные на самом деле присутствуют в этом большом двоичном объекте?
Далее вы печатаете двоичные данные, как если бы это был текст (через new String(byte[])
). Вы должны напечатать Base64 или hex с обеих сторон, чтобы увидеть, совпадают ли они как для байтов сообщения (которые, как представляется, вызываются cipherMessage
в вашем верификаторе), так и для подписи.
Если вы переключаетесь на статические данные (чтобы избежать таких вещей, как подсчет новых строк при чтении), тогда должно быть допустимо выполнять
byte[] originalData = Encoding.UTF8.GetBytes("This is a static message test.");
Console.WriteLine(Convert.ToBase64String(rsa.SignData(originalData), "SHA1"));
затем возьмите этот вывод, поместите его в свою Java-программу и проверьте
byte[] originalData = "This is a static message test.".getBytes("UTF-8");
byte[] signature = Base64.getDecoder().decode(theOutputFromTheCSharpProgram);
Signature verifier = new Signature("SHA1withRSA");
verifier.initVerify(publicKey);
verifier.update(originalData);
System.out.println(verifier.verify(signature));
Если эта часть не работает, то вы не должны точно представлять один и тот же ключ с обеих сторон.
Двоичный формат подписей для RSA одинаков в C # и Java; поэтому, как только вы правильно настроите передачу данных, все должно работать.
Комментарии:
1. Я пробовал вышеуказанную часть, проблема в том, что после разбора строки base64 в byte [] в java это не то же самое, что мы получаем из C #. Я использую DatatypeConverter.parseBase64Binary (messageWithSignature);
2. Я правильно понял, что мне нужно использовать строку base64 только для перехода из C # в java в byte[] поскольку никакой другой формат не был бы совместим между C # и java, я принимаю ваш ответ. Тем не менее, у меня есть некоторые трудности с тем, что подпись не соответствует.
3. @Coder Вы уверены, что создаете
publicKey
правильно? В частности, BigInteger, скорее всего, интерпретирует что-либо с установленным старшим битом как отрицательное число. Проверьте, не тестируется ли она как меньшая, чем BigInteger.zero4. Да, потому что, когда программа C # использует открытый ключ, сгенерированный программой Java, для шифрования данных и закрытый ключ для расшифровки данных, это работает нормально. Поэтому в идеале подпись также должна совпадать.