Получение секретного ключа RSA от УГР base64 закодированный файл закрытого ключа
У меня есть файл с закрытым ключом (кодировка PEM BASE64). Я хочу использовать его в другом месте, чтобы расшифровать некоторые другие данные.Используя Java, я попытался прочитать файл и декодировать данные, закодированные в BASE64... Это фрагмент кода, который я попробовал....
import java.io.*;
import java.nio.ByteBuffer;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import com.ibm.crypto.fips.provider.RSAPrivateKey;
import com.ibm.misc.BASE64Decoder;
public class GetPrivateKey {
public static RSAPrivateKey get() throws Exception {
File privateKeyFile = new File("privatekey.key");
byte[] encodedKey = new byte[(int) privateKeyFile.length()];
new FileInputStream(privateKeyFile).read(encodedKey);
ByteBuffer keyBytes = new BASE64Decoder().decodeBufferToByteBuffer(encodedKey.toString());
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(keyBytes.array());
KeyFactory kf = KeyFactory.getInstance("RSA", "IBMJCEFIPS");
RSAPrivateKey pk = (RSAPrivateKey) kf.generatePrivate(privateKeySpec);
return pk;
}
public static void main(String[] args) throws Exception {
PrivateKey privKey = FormatMePlease.get();
System.out.println(privKey.toString());
}
}
Я получаю следующие ошибки
Exception in thread "main" java.security.spec.InvalidKeySpecException: Inappropriate key specification: DerInputStream.getLength(): lengthTag=127, too big.
at com.ibm.crypto.fips.provider.RSAKeyFactory.b(Unknown Source)
at com.ibm.crypto.fips.provider.RSAKeyFactory.engineGeneratePrivate(Unknown Source)
at java.security.KeyFactory.generatePrivate(Unknown Source)
at GetPrivateKey.get(GetPrivateKey.java:24)
at GetPrivateKey.main(GetPrivateKey.java:29)
Содержимое файла " приватекей.ключ "
-----BEGIN RSA PRIVATE KEY-----
MIIEuwIBADANBgkqhkiG9w0BAQEFAASCBKUwggShAgEAAoIBAF53wUbKmDHtvfOb8u1HPqEBFNNF
csnOMjIcSEhAwIQMbgrOuQ+vH/YgXuuDJaURS85H8P4UTt6lYOJn+SFnXvS82E7LHJpVrWwQzbh2
QKh13/akPe90DlNTUGEYO7rHaPLqTlld0jkLFSytwqfwqn9yrYpM1ncUOpCciK5j8t8MzO71LJoJ
g24CFxpjIS0tBrJvKzrRNcxWSRDLmu2kNmtsh7yyJouE6XoizVmBmNVltHhFaDMmqjugMQA2CZfL
rxiR1ep8TH8IBvPqysqZI1RIpB/e0engP4/1KLrOt+6gGS0JEDh1kG2fJObl+N4n3sCOtgaz5Uz8
8jpwbmZ3Se8CAwEAAQKCAQAdOsSs2MbavAsIM3qo/GBehO0iqdxooMpbQvECmjZ3JTlvUqNkPPWQ
vFdiW8PsHTvtackhdLsqnNUreKxXL5rr8vqi9qm0/0mXpGNi7gP3m/FeaVdYnfpIwgCe6lag5k6M
yv7PG/6N8+XrWyBdwlOe96bGohvB4Jp2YFjSTM67QONQ8CdmfqokqJ8/3RyrpDvGN3iX3yzBqXGO
jPkoJQv3I4lsYdR0nl4obHHnMSeWCQCYvJoZ7ZOliu/Dd0ksItlodG6s8r/ujkSa8VIhe0fnXTf0
i7lqa55CAByGN4MOR0bAkJwIB7nZzQKurBPcTAYJFFvAc5hgMnWT0XW83TehAoGBALVPGnznScUw
O50OXKI5yhxGf/XDT8g28L8Oc4bctRzI+8YfIFfLJ57uDGuojO/BpqtYmXmgORru0jYR8idEkZrx
gf62czOiJrCWTkBCEMtrNfFHQJQCQrjfbHofp7ODnEHbHFm7zdlbfNnEBBaKXxd2rVv4UTEhgftv
wsHcimbXAoGBAIViWrHWElMeQT0datqlThE/u51mcK4VlV7iRWXVa1/gAP85ZAu44VvvDlkpYVkF
zSRR+lHSOzsubDMN45OBQW6UA3RPg4TCvrTOmhQUeF5XPuSdcD0R2At6pdaLwAKnOtILg13Ha6ym
Igjv8glodvem3hWLmpHIhNBiaXtf8wqpAoGADH5a8OhvKOtd8EChGXyp9LDW+HRw9vbyN/gi9dQX
ltgyoUBb1jDllgoJSRHgRFUvyvbb/ImR5c03JwqtiQ8siWTC9G5WGeS+jcSNt9fVmG7W1L14MbrG
Jj8fFns/7xrOlasnlPdgA+5N+CONtI/sZY2D/KZr0drhPhZBcWJlFxkCgYAn+4SOPEo/6hjKNhA6
vER7fSxDEVsDg+rDh3YgAWpvUdlaqBxqOyAqi600YugQZGHK2lv7vNYOdmrunuIx7BPuDqY+bjtR
R4Mc9bVQAZbXSLXMl7j2RWwKfNhLSJbk9LX4EoVtTgLjvOUE4tAdq9fFgpqdwLwzqPTO9kECP4++
CQKBgH6tO/xcNxG/uXUideluAn3H2KeyyznZMJ7oCvzf26/XpTAMI243OoeftiKVMgxuZ7hjwqfn
/VHXABc4i5gchr9RzSb1hZ/IqFzq2YGmbppg5Ok2cgwalDoDBi21bRf8aDRweL62mO+7aPnCQZ58
j5W72PB8BAr6xg0Oro25O4os
-----END RSA PRIVATE KEY-----
Подобные вопросы были размещены здесь, но они не принесли мне никакой пользы.
Почти все они предложили использовать Bouncycastle провайдер, который не готов использовать, как предполагается, использовать провайдера, который соответствует FIPS и не уверен, что поставщик BC соответствует FIPS.
Помощь в том, чтобы вытащить меня из этого, была бы очень признательна...
Заранее спасибо.6 ответов:
Разбор PKCS1 (только формат PKCS8 работает из коробки на Android) ключ оказался утомительной задачей на Android из-за отсутствия поддержки ASN1, но разрешимой, если вы включаетегубчатый замок jar для чтения целых чисел DER.
String privKeyPEM = key.replace( "-----BEGIN RSA PRIVATE KEY-----\n", "") .replace("-----END RSA PRIVATE KEY-----", ""); // Base64 decode the data byte[] encodedPrivateKey = Base64.decode(privKeyPEM, Base64.DEFAULT); try { ASN1Sequence primitive = (ASN1Sequence) ASN1Sequence .fromByteArray(encodedPrivateKey); Enumeration<?> e = primitive.getObjects(); BigInteger v = ((DERInteger) e.nextElement()).getValue(); int version = v.intValue(); if (version != 0 && version != 1) { throw new IllegalArgumentException("wrong version for RSA private key"); } /** * In fact only modulus and private exponent are in use. */ BigInteger modulus = ((DERInteger) e.nextElement()).getValue(); BigInteger publicExponent = ((DERInteger) e.nextElement()).getValue(); BigInteger privateExponent = ((DERInteger) e.nextElement()).getValue(); BigInteger prime1 = ((DERInteger) e.nextElement()).getValue(); BigInteger prime2 = ((DERInteger) e.nextElement()).getValue(); BigInteger exponent1 = ((DERInteger) e.nextElement()).getValue(); BigInteger exponent2 = ((DERInteger) e.nextElement()).getValue(); BigInteger coefficient = ((DERInteger) e.nextElement()).getValue(); RSAPrivateKeySpec spec = new RSAPrivateKeySpec(modulus, privateExponent); KeyFactory kf = KeyFactory.getInstance("RSA"); PrivateKey pk = kf.generatePrivate(spec); } catch (IOException e2) { throw new IllegalStateException(); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException(e); } catch (InvalidKeySpecException e) { throw new IllegalStateException(e); }
Вы только что опубликовали этот закрытый ключ, так что теперь весь мир знает, что это такое. Надеюсь, это было только для проверки.
EDIT: Другие отметили, что текстовый заголовок OpenSSL опубликованного ключа,- - - - - BEGIN RSA PRIVATE KEY - - - -, указывает, что это PKCS#1. Однако фактическим содержимым Base64 рассматриваемого ключа является PKCS#8. Очевидно, ОП скопировал и наклеил заголовок и трейлер ключа PKCS#1 на ключ PKCS#8 по какой-то неизвестной причине. Пример кода у меня есть ниже приведены работы с закрытыми ключами PKCS#8.
Вот некоторый код, который создаст закрытый ключ из этих данных. Вам придется заменить в base64 декодирование с приложениями IBM в base64 декодер.
public class RSAToy { private static final String BEGIN_RSA_PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEuwIBADAN ...skipped the rest\n" // + ... // + ... skipped the rest // + ... + "-----END RSA PRIVATE KEY-----"; public static void main(String[] args) throws Exception { // Remove the first and last lines String privKeyPEM = BEGIN_RSA_PRIVATE_KEY.replace("-----BEGIN RSA PRIVATE KEY-----\n", ""); privKeyPEM = privKeyPEM.replace("-----END RSA PRIVATE KEY-----", ""); System.out.println(privKeyPEM); // Base64 decode the data byte [] encoded = Base64.decode(privKeyPEM); // PKCS8 decode the encoded RSA private key PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded); KeyFactory kf = KeyFactory.getInstance("RSA"); PrivateKey privKey = kf.generatePrivate(keySpec); // Display the results System.out.println(privKey); } }
Это формат закрытого ключа PKCS#1. Попробуйте этот код. Он не использует Bouncy Castle или других сторонних крипто-провайдеров. Только Ява.безопасность и солнце.безопасность для разбора DER sequece. Также он поддерживает синтаксический анализ закрытого ключа в формате PKCS#8 (PEM-файл с заголовком " - - - - - BEGIN PRIVATE KEY - - - -").
import sun.security.util.DerInputStream; import sun.security.util.DerValue; import java.io.File; import java.io.IOException; import java.math.BigInteger; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.RSAPrivateCrtKeySpec; import java.util.Base64; public static PrivateKey pemFileLoadPrivateKeyPkcs1OrPkcs8Encoded(File pemFileName) throws GeneralSecurityException, IOException { // PKCS#8 format final String PEM_PRIVATE_START = "-----BEGIN PRIVATE KEY-----"; final String PEM_PRIVATE_END = "-----END PRIVATE KEY-----"; // PKCS#1 format final String PEM_RSA_PRIVATE_START = "-----BEGIN RSA PRIVATE KEY-----"; final String PEM_RSA_PRIVATE_END = "-----END RSA PRIVATE KEY-----"; Path path = Paths.get(pemFileName.getAbsolutePath()); String privateKeyPem = new String(Files.readAllBytes(path)); if (privateKeyPem.indexOf(PEM_PRIVATE_START) != -1) { // PKCS#8 format privateKeyPem = privateKeyPem.replace(PEM_PRIVATE_START, "").replace(PEM_PRIVATE_END, ""); privateKeyPem = privateKeyPem.replaceAll("\\s", ""); byte[] pkcs8EncodedKey = Base64.getDecoder().decode(privateKeyPem); KeyFactory factory = KeyFactory.getInstance("RSA"); return factory.generatePrivate(new PKCS8EncodedKeySpec(pkcs8EncodedKey)); } else if (privateKeyPem.indexOf(PEM_RSA_PRIVATE_START) != -1) { // PKCS#1 format privateKeyPem = privateKeyPem.replace(PEM_RSA_PRIVATE_START, "").replace(PEM_RSA_PRIVATE_END, ""); privateKeyPem = privateKeyPem.replaceAll("\\s", ""); DerInputStream derReader = new DerInputStream(Base64.getDecoder().decode(privateKeyPem)); DerValue[] seq = derReader.getSequence(0); if (seq.length < 9) { throw new GeneralSecurityException("Could not parse a PKCS1 private key."); } // skip version seq[0]; BigInteger modulus = seq[1].getBigInteger(); BigInteger publicExp = seq[2].getBigInteger(); BigInteger privateExp = seq[3].getBigInteger(); BigInteger prime1 = seq[4].getBigInteger(); BigInteger prime2 = seq[5].getBigInteger(); BigInteger exp1 = seq[6].getBigInteger(); BigInteger exp2 = seq[7].getBigInteger(); BigInteger crtCoef = seq[8].getBigInteger(); RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(modulus, publicExp, privateExp, prime1, prime2, exp1, exp2, crtCoef); KeyFactory factory = KeyFactory.getInstance("RSA"); return factory.generatePrivate(keySpec); } throw new GeneralSecurityException("Not supported format of a private key"); }
Проблема, с которой вы столкнетесь, заключается в том, что существует два типа PEM-форматированных ключей: PKCS8 и SSLeay. Это не помогает, что OpenSSL, кажется, использует оба в зависимости от команды:
Обычная команда
Последний формат PKCS8 может быть открыт изначально в Java с помощьюopenssl genrsa
генерирует формат SSLeay PEM. Экспорт из файла PKCS12 с помощьюopenssl pkcs12 -in file.p12
создаст файл PKCS8.PKCS8EncodedKeySpec
. С другой стороны, форматированные ключи SSLeay не могут быть открыты изначально.Для открытия закрытых ключей SSLeay, вы можете либо использовать BouncyCastle provider, как многие делали раньше, либо еще не-Commons-SSL заимствовали минимальное количество необходимого кода от BouncyCastle для поддержки синтаксического анализа ключей PKCS8 и SSLeay в формате PEM и DER: http://juliusdavies.ca/commons-ssl/pkcs8.html . (я не уверен, что еще не-Commons-SSL будет соответствовать FIPS)
Идентификация Формата Ключа
Исходя из man-страниц OpenSSL, ключевые заголовки для двух форматов выглядят следующим образом:
PKCS8 Формат
Не зашифровано:
-----BEGIN PRIVATE KEY-----
Зашифровано:-----BEGIN ENCRYPTED PRIVATE KEY-----
Формат SSLeay
-----BEGIN RSA PRIVATE KEY-----
(эти ответы, похоже, противоречат другим ответам, но я проверил вывод OpenSSL с помощью
PKCS8EncodedKeySpec
. Только ключи PKCS8, показывающие----BEGIN PRIVATE KEY-----
работу изначально)
Как ответили другие, ключ, который вы пытаетесь разобрать, не имеет соответствующих заголовков PKCS#8, которые должны понимать
PKCS8EncodedKeySpec
Oracle. Если вы не хотите конвертировать ключ с помощьюopenssl pkcs8
или анализировать его с помощью внутренних API JDK, вы можете добавить заголовок PKCS#8 следующим образом:static final Base64.Decoder DECODER = Base64.getMimeDecoder(); private static byte[] buildPKCS8Key(File privateKey) throws IOException { final String s = new String(Files.readAllBytes(privateKey.toPath())); if (s.contains("--BEGIN PRIVATE KEY--")) { return DECODER.decode(s.replaceAll("-----\\w+ PRIVATE KEY-----", "")); } if (!s.contains("--BEGIN RSA PRIVATE KEY--")) { throw new RuntimeException("Invalid cert format: "+ s); } final byte[] innerKey = DECODER.decode(s.replaceAll("-----\\w+ RSA PRIVATE KEY-----", "")); final byte[] result = new byte[innerKey.length + 26]; System.arraycopy(DECODER.decode("MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKY="), 0, result, 0, 26); System.arraycopy(BigInteger.valueOf(result.length - 4).toByteArray(), 0, result, 2, 2); System.arraycopy(BigInteger.valueOf(innerKey.length).toByteArray(), 0, result, 24, 2); System.arraycopy(innerKey, 0, result, 26, innerKey.length); return result; }
Как только этот метод установлен, вы можете передать его выходные данные в конструктор
PKCS8EncodedKeySpec
следующим образом:new PKCS8EncodedKeySpec(buildPKCS8Key(privateKey));