Аутентификация подписи, созданной на c #, с помощью открытого ключа на java

Я новичок в проверке и сертификатах и ​​т. Д. Я столкнулся с проблемой, что мне нужно подписать сообщение на C #, а затем проверить подпись на java, проблема, с которой я столкнулся, что я не могу загрузить открытый ключ на java на (PublicKey) с использованием строки Base64, созданной на C #, я использовал следующий код для генерации закрытого и открытого ключей на стороне C #

 CspParameters cspParams = new CspParameters { ProviderType = 1 };
            cspParams.KeyContainerName = "MyKeyContainer";
            RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(1024);

            string publicKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(false));
            string privateKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(true));
            System.Diagnostics.Debug.WriteLine("pub:" + publicKey);
            System.Diagnostics.Debug.WriteLine("pri:" + privateKey);

            Console.WriteLine("Key added to container: \n  {0}", rsaProvider.ToXmlString(true));

затем я использовал следующий код для создания открытого ключа на стороне Java:

    X509EncodedKeySpec specc = new X509EncodedKeySpec(org.apache.commons.codec.binary.Base64.decodeBase64("BgIAAACkAABSU0ExAAQAAA......"));
            KeyFactory xx = KeyFactory .getInstance("RSA");
            PublicKey ssx=  xx.generatePublic(specc);

обратите внимание, что я скопировал строку открытого ключа base64 из консоли C #. Когда я пытаюсь запустить код на стороне Java, я получаю следующее исключение:

java.security.spec.InvalidKeySpecException: Inappropriate key specification: invalid key format
at sun.security.provider.DSAKeyFactory.engineGeneratePublic(Unknown Source)
at java.security.KeyFactory.generatePublic(Unknown Source)

Мне нужно найти способ сгенерировать закрытый и открытый ключ на С # (и сгенерировать файл .cer для открытого ключа), чтобы загрузить его на стороне Java, или найти способ загрузить строку открытого ключа base64 в объект (Publickey) на стороне Java. пожалуйста помоги !


person Mr MAmour    schedule 12.10.2017    source источник
comment
Код KeyFactory .getInstance("RSA"); и исключение не соответствуют, потому что используется DSAKeyFactory вместо RSAKeyFactory. Убедитесь, что вы выполняете правильный код   -  person pedrofb    schedule 12.10.2017


Ответы (1)


Вариант 1. Те ​​же данные, другой формат.

Самый простой способ передать открытый ключ RSA из .NET - это проверить, что значение открытого показателя степени равно { 01 00 01 }, а затем отправить значение модуля. На стороне получателя вы принимаете модуль и утверждаете публичную экспоненту.

RSAParameters keyParams = rsa.ExportParameters(false);

if (!keyParams.Exponent.SequenceEqual(new byte[] { 0x01, 0x00, 0x01 }))
    throw new InvalidOperationException();

Send(keyParams.Modulus);

Затем Создание ключей RSA из известных параметров в Java говорит, что вы можете просто восстановить его на стороне Java.

Вариант 2: Тот же формат, другой парсер.

Следующий вариант - продолжать использовать большой двоичный объект CSP, но писать синтаксический анализатор на Java. Данные являются результатом вызова CryptExportKey с PUBLICKEYBLOB, создавая структуру данных, как описано на странице https://msdn.microsoft.com/en-us/library/ee442238.aspx и https://msdn.microsoft.com/en-us/library/windows/desktop/aa375601 (v = vs.85) .aspx # pub_BLOB .

В итоге:

Заголовок (который вы можете пропустить или просто проверить на соответствие фиксированным значениям, которые вы ожидаете):

  • Байт, значение 0x06 (PUBLICKEYBLOB)
  • Байт, значение 0x02 (blob v2)
  • Короткое значение 0x0000 (зарезервировано)
  • Целое число (хранящееся как little-endian), идентифицирующее ключ как RSA (0x0000A400 или 0x00002400)
  • Целое число (хранящееся как little-endian), идентифицирующее следующий сегмент как открытый ключ RSA (немного избыточно, но технически теперь имеет другую структуру): 0x31415352

После всего этого приходят соответствующие данные:

  • Разрядность модуля, хранящаяся как целое число без знака с прямым порядком байтов. Для вашего 1024-битного примера это будет 1024, он же 0x00000400, он же { 00 04 00 00 } (LE).
  • Открытая экспонента, хранящаяся как целое число без знака с прямым порядком байтов. Это почти всегда 0x00010001 (он же { 01 00 01 00 }), но, поскольку он есть, вы должны уважать его.
  • Следующие bitLen/8 байтов представляют значение модуля. Поскольку это открытый ключ, которым должны быть «остальные байты в этом массиве».

Вариант 3. Создайте сертификат

.NET Framework не имеет встроенной этой возможности (в текущей версии 4.7). Вы можете использовать P / Invoke для CertCreateSelfSignCertificate , но это потребует довольно много изменений (поскольку RSACryptoServiceProvider не позволит вам получить дескриптор ключа, поэтому вам также придется P / Invoke все это тоже).

Вы могли бы самостоятельно "выпустить" сертификат, закодированный в формате DER. Хотя это весело, это сложно сделать правильно, и, вероятно, это не жизнеспособный путь.

Если вы можете перейти на .NET Core, возможность создания сертификатов была добавлена ​​в .NET Core 2.0 через CertificateRequest. Для очень простого сертификата от вашего ключа:

var certReq = new CertificateRequest(
    "CN=SubjectCN",
    rsaProvider,
    HashAlgorithmName.SHA256,
    RSASignaturePadding.Pkcs1);

// add any extensions you want. I'm not adding any because I said "simple".

DateTimeOffset now = DateTimeOffset.UtcNow;
X509Certificate2 cert = certReq.CreateSelfSigned(now, now.AddMinutes(90));
byte[] xfer = cert.RawData;
person bartonjs    schedule 12.10.2017