#java #c_sharp #криптография #rsa
Почему при генерации RSA ключа длиной 512 бит в C# и в Java разная длина в байтах публичного ключа? Пример кода C#: RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(512); XDocument xdocPub = XDocument.Parse(rsa.ToXmlString(false)); string publicKey = xdocPub.Element("RSAKeyValue").Element("Modulus").Value; byte[] publicArray = Convert.FromBase64String(publicKey); Console.WriteLine("Public Key Byte Array Length: " + publicArray.Length); Console.WriteLine("Public Key Base64 Length: " + publicKey.Length); Console.WriteLine("Public Key Text: " + publicKey); Результат: Public Key Byte Array Length: 64 Public Key Base64 Length: 88 Public Key Text: uELcIjgR6GpiayZ1wHruHOtO8dpg+xmg85VAvunWUEL+aifoyk9+CA/dez4UmuIwLEgRlpVoIIOD0e5i9v8lrQ== Пример кода Java: final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(new RSAKeyGenParameterSpec(512, RSAKeyGenParameterSpec.F4)); KeyPair pair = keyGen.genKeyPair(); final byte[] arrayPublic = ((RSAPublicKey) pair.getPublic()).getModulus().toByteArray(); System.out.println("Public Key [origin] lenght: " + arrayPublic.length); String publicBase64 = Base64.getEncoder().encodeToString(arrayPublic); System.out.println("Public Key [base64] lenght : " + publicBase64.length()); System.out.println("Public Key [base64]: " + publicBase64); Результат: Public Key [origin] lenght: 65 Public Key [base64] lenght : 88 Public Key [base64]: ANuun1/CnOmk2ghsydz4cKvPCd7q0YqqeyjzLqtKx7QHqv1tFjJYsXquYGcY8zkaPsPBrRlCAJov7+J88GaFDKc= Заметил, что в байт массиве ключа сгенерированного в Java присутствует нулевой байт в начале массива ключа. Самое интересное, что если я отправляю ключ из C# приложения длиной 64 байта, то в Java приложении получаю ошибку "exponent is larger than modulus" в следующем коде: try { final KeyFactory kfac = KeyFactory.getInstance("RSA"); final BigInteger modulus = new BigInteger("здесь вставляется byte[] массив публичного ключа от C#"); final RSAPublicKeySpec kspec1 = new RSAPublicKeySpec(modulus, RSAKeyGenParameterSpec.F4); RSAPublicKey publicKey = (RSAPublicKey) kfac.generatePublic(kspec1); } catch (GeneralSecurityException e) { e.printStackTrace(); } При этом, если я добавлю нулевой байт в начало ключа перед отправкой из C# приложения в Java приложение, то со стороны Java приложения нет никаких проблем с инициализацией криптографии и шифровкой отправляемых данных. Но в этом случае я не могу декодировать входящие данные в C# приложении этим же экземпляром криптографии. Получаю ошибку "Плохие данные". Подскажите, что я делаю не так и почему длина ключей разная? UPD 1: добавлен тест в Java PublicKey взят из первого примера сгенерированного в C#. public class Test { public static void main(String[] args) { final String base64key = "uELcIjgR6GpiayZ1wHruHOtO8dpg+xmg85VAvunWUEL+aifoyk9+CA/dez4UmuIwLEgRlpVoIIOD0e5i9v8lrQ=="; try { byte[] publicKeyArray = Base64.getDecoder().decode(base64key); final KeyFactory kfac = KeyFactory.getInstance("RSA"); final BigInteger modulus = new BigInteger(1, publicKeyArray); final RSAPublicKeySpec kspec1 = new RSAPublicKeySpec(modulus, RSAKeyGenParameterSpec.F4); RSAPublicKey publicKey = (RSAPublicKey) kfac.generatePublic(kspec1); byte[] array = publicKey.getModulus().toByteArray(); System.out.println("Public Key [Array] lenght : " + array.length); String publicBase64 = Base64.getEncoder().encodeToString(array); System.out.println("Public Key [base64] lenght : " + publicBase64.length()); System.out.println("Public Key [base64]: " + publicBase64); } catch (GeneralSecurityException e) { e.printStackTrace(); } } } В результате инициализации RSAPublicKey в Java Base64 ключ не соответствует входному. Хотя по длине 65 байт. Но это работает только с signum = 1. Public Key [Array] lenght : 65 Public Key [base64] lenght : 88 Public Key [base64]: ALhC3CI4EehqYmsmdcB67hzrTvHaYPsZoPOVQL7p1lBC/mon6MpPfggP3Xs+FJriMCxIEZaVaCCDg9HuYvb/Ja0= UPD 2: проблему C# генерации ключей RSA решила библиотека Bouncy Castle. У библиотеки есть исходники поэтому я взял только RSA часть и внедрил в свой проект.
Ответы
Ответ 1
Лишний байт BigInteger.toByteArray включает в результат знаковый бит числа: ... The array will contain the minimum number of bytes required to represent this BigInteger, including at least one sign bit, which is (ceil((this.bitLength() + 1)/8)) ... Т.ч. да, нулевой байт в начале можно отрезать по необходимости. Сделать это можно с помощью System.arraycopy: byte[] array = publicKey.getModulus().toByteArray(); //убираем лишний нолик final byte[] keyBytes = new byte[64]; System.arraycopy(array, 1, keyBytes, 0, 64); Посмотрите ответ @Maarten Bodewes на близкий вопрос на английском (How insert bits into block in java cryptography?), там приводится готовый метод для «нормализации» массива байтов с учетом ведущего нуля, также учитывается случай, когда массив короче 64 (вроде возникает только для приватных ключей). Плохие данные Конструктор BigInteger(byte[] val) также ожидает получить знаковое представление числа и пытается считать у ключа знаковый бит. Попробуйте использовать конструктор, который принимает знаковый бит отдельно BigInteger(int signum, byte\[\] magnitude): new BigInteger(1, /*байты ключа*/) Похожие вопросы на en.SO: how to build a rsa key with a modulus of 64 bytes in java How insert bits into block in java cryptography?
Комментариев нет:
Отправить комментарий