Цифровая подпись, созданная на C #, не соответствует проверке Java

#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.zero

4. Да, потому что, когда программа C # использует открытый ключ, сгенерированный программой Java, для шифрования данных и закрытый ключ для расшифровки данных, это работает нормально. Поэтому в идеале подпись также должна совпадать.