Сгенерируйте пару ключей openssl, используя java - PullRequest
2 голосов
/ 10 апреля 2020

Мне нужно создать пару ключей openssl в java, которая имитирует следующее:

openssl ecparam -name prime256v1 -genkey -noout -out prime256v1.key

openssl pkcs8 -topk8 -in prime256v1.key -out prime256v1-priv.pem -nocrypt

openssl ec -in prime256v1-priv.pem -pubout -out prime256v1-pub.pem

Моя java программа выглядит следующим образом:

public static void main(String args[]) throws Exception{

    Security.addProvider(new BouncyCastleProvider());
    KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC");
    ECGenParameterSpec spec = new ECGenParameterSpec("secp256r1");
    g.initialize(spec);
    KeyPair keyPair = g.generateKeyPair();

    byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
    String publicKeyContent = Base64.encode(publicKeyBytes);
    String publicKeyFormatted = "-----BEGIN PUBLIC KEY-----" + System.lineSeparator();
    for (final String row:
            Splitter
                    .fixedLength(64)
                    .split(publicKeyContent)
            )
    {
        publicKeyFormatted += row + System.lineSeparator();
    }
    publicKeyFormatted += "-----END PUBLIC KEY-----";
    BufferedWriter writer = new BufferedWriter(new FileWriter("publickey.pem"));
    writer.write(publicKeyFormatted);
    writer.close();

    byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
    String privateKeyContent = Base64.encode(privateKeyBytes);
    String privateKeyFormatted = "-----BEGIN PRIVATE KEY-----" + System.lineSeparator();
    for (final String row:
            Splitter
                    .fixedLength(64)
                    .split(privateKeyContent)
            )
    {
        privateKeyFormatted += row + System.lineSeparator();
    }
    privateKeyFormatted += "-----END PRIVATE KEY-----";
    BufferedWriter writer2 = new BufferedWriter(new FileWriter("privatekey.pem"));
    writer2.write(privateKeyFormatted);
    writer2.close();
}

Приведенный выше код работает, но сгенерированный закрытый ключ кажется длиннее, чем сгенерированный с помощью утилиты командной строки, о которой я упоминал вверху.

Privatekey с командной строкой:

-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgGuyf3+/6+rnDKw0D
WbxVyggwNL0jlTVAzGm6cpl3ji2hRANCAAQ7zLtxLLvl6LJHJAlYAZr4hAc09fZn
bAniYIeKVqVBdKIvb5R445PFiUDFcfyneeX/resPXJHMEm/vAxfQeMqL
-----END PRIVATE KEY-----

Privatekey с java:

-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgYFPrkmxnwjVBgpUV
B02/luLD1rt9
UWZHj62YdhwYQESgCgYIKoZIzj0DAQehRANCAATZp7Jl8KXXApA
hvv9qeQtX5LbHQkrCdx3DfkUC
GgCUMSJWKxs7yJPNKtFZnFUTFZfyEF76fdEzky
zIon5H04MX
-----END PRIVATE KEY-----

Даже если я уберу здесь 2 лишние строки, даже в этом случае это будет ключ большего размера.

Publickey с командной строкой:

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEO8y7cSy75eiyRyQJWAGa+IQHNPX2
Z2wJ4mCHilalQXSiL2+UeOOTxYlAxXH8p3nl/63rD1yRzBJv7wMX0HjKiw==
-----END PUBLIC KEY-----

Ключ Publi c с Java:

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2aeyZfCl1wKQIb7/ankLV+S2x0JK
wncdw35FAhoA
lDEiVisbO8iTzSrRWZxVExWX8hBe+n3RM5MsyKJ+R9ODFw==
-----END PUBLIC KEY-----

Итак, мой первый вопрос касается длины закрытого ключа. Кажется дольше. Мой второй вопрос, кажется, я не разделяю сгенерированные байты ключа должным образом. Есть, конечно, больше строк, чем ожидалось. Как это исправить?

1 Ответ

2 голосов
/ 10 апреля 2020

... длина закрытого ключа ... кажется длиннее

Точнее говоря, структура, представляющая / содержащая закрытый ключ, длиннее. Java включает необязательное - и ненужное (избыточное) в поле PKCS8 - 'параметры' данных c, определяемых алгоритмом, которые определены в ECPrivateKey в Приложение SEC1 C .4 , а OpenSSL - нет. Это будет игнорироваться при повторном чтении. Фактическое значение ключа в обеих структурах - правильный размер.

Я неправильно разделяю сгенерированные байты ключа

Скорее, разделяю (base64) символы, которые кодируют байты (структуры ключа).

Посмотрите на выходные данные вашего Base64.encode до the Splitter. Могу поспорить, вы найдете, что он уже содержит новые строки после каждых 76 символов base64, в соответствии со стандартами MIME (RF C 1521 et seq), которые некоторые люди считают более распространенными (или более важными?) Или, по крайней мере, более новыми, чем PEM. (Хотя XML и JWX еще новее и теперь довольно распространены, и вообще не вставляют разрывы строк.) В результате ваш Splitter получает:

  • первые 64 символа из первого строка
  • оставшиеся 12 символов из первой строки, новая строка и 51 символ из второй строки
  • остальные 25 символов из второй строки, новая строка и (до) 38 символы из третьей строки
  • et c.

Хотя OpenSSL записывает файлов PEM с переносами строк тела через каждые 64 символа (кроме последней строки), в соответствии с Стандарт PEM (RF C 1421) всегда позволял считывать файлы с любым числом от 4 до 76 символов в соответствии с MIME. Последние версии, начиная с 1.1.0 в 2016 году, теперь достаточно широко распространенные, могут читать строки до нескольких сотен символов. Таким образом, если ваши файлы должны быть прочитаны (что угодно, используя) библиотеку OpenSSL, вы можете просто написать версию split-at-76 без каких-либо дополнительных изменений, за исключением того, что есть разрыв строки, заканчивающий строку last . Другое программное обеспечение может отличаться; если вам нужно быть в безопасности или строго соответствовать, сначала удалите разрывы строк из вашего вывода Base64.encode, а затем добавьте их обратно с правильным интервалом 64. См. недавно опубликованную повторную спецификацию,

PS: если вы используете Java для помещения этого ключа в хранилище ключей PKCS12 (для этого вам необходимо / получить / создать сертификат), командная строка openssl может прочитать это напрямую и преобразовать (1) приватный ключ в PEM, (2) сертификат в PEM, из которого вы можете извлечь публичный ключ в PEM.

...