Подтвердить что ты не робот

Получение секретного ключа RSA из PEM 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)

Содержимое файла "privatekey.key"

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

Помощь в получении меня из этого будет высоко ценится... Заранее спасибо.

4b9b3361

Ответ 1

Parsing PKCS1 (только формат PKCS8 работает из коробки на Android) ключ оказался утомительной задачей на Android из-за отсутствия поддержки ASN1, но разрешимой, если вы включили Spongy castle, чтобы читать целые числа 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);
}

Ответ 2

Вы только что опубликовали этот закрытый ключ, так что теперь весь мир знает, что это такое. Надеюсь, это было просто для тестирования.

EDIT: Другие отметили, что текстовый заголовок openssl опубликованного ключа, ----- BEGIN RSA PRIVATE KEY -----, указывает, что он является PKCS # 1. Однако фактическое содержимое Base64 данного ключа является PKCS # 8. Очевидно, копия OP и вставила заголовок и трейлер ключа 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);
    }
}

Ответ 3

Это формат закрытого ключа PKCS # 1. Попробуйте этот код. Он не использует Bouncy Castle или других сторонних поставщиков криптографии. Просто java.security и sun.security для разбора парсеров DER. Также он поддерживает разбор секретного ключа в формате PKCS # 8 (файл PEM с заголовком "----- НАЧАТЬ ЧАСТНЫЙ КЛЮЧ -----" ).

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");
    }

Ответ 4

Проблема, с которой вы столкнетесь, состоит в том, что существуют два типа ключей в формате PEM: PKCS8 и SSLeay. Это не помогает OpenSSL, как представляется, использовать оба в зависимости от команды:

Обычная команда openssl genrsa будет генерировать формат SSLeay PEM. Экспорт из файла PKCS12 с помощью openssl pkcs12 -in file.p12 создаст файл PKCS8.

Последний формат PKCS8 можно открыть изначально на Java с помощью PKCS8EncodedKeySpec. С другой стороны, отформатированные ключи SSLeay не могут быть открыты изначально.

Чтобы открыть закрытые ключи SSLeay, вы можете использовать провайдер BouncyCastle, как это делали многие, или Not-Yet-Commons-SSL заимствовали минимальный объем необходимого кода от BouncyCastle для поддержки разбора ключей PKCS8 и SSLeay в форматах PEM и DER: http://juliusdavies.ca/commons-ssl/pkcs8.html. (Я не уверен, что Not-Yet-Commons-SSL будет соответствовать FIPS)

Идентификация формата ключа

Вывод из справочных страниц OpenSSL заголовков заголовков для двух форматов выглядит следующим образом:

Формат PKCS8

Не шифруется: -----BEGIN PRIVATE KEY-----
Зашифровано: -----BEGIN ENCRYPTED PRIVATE KEY-----

Формат SSLeay

-----BEGIN RSA PRIVATE KEY-----

(Кажется, это противоречит другим ответам, но я тестировал вывод OpenSSL с использованием PKCS8EncodedKeySpec. Только ключи PKCS8, показывающие ----BEGIN PRIVATE KEY----- работают изначально)

Ответ 5

Как ответили другие, ключ, который вы пытаетесь выполнить, не имеет правильных заголовков PKCS # 8, которые Oracle PKCS8EncodedKeySpec должен понять. Если вы не хотите преобразовывать ключ с помощью 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));

Ответ 6

Убедитесь, что ваш файл id_rsa не имеет расширения, например .txt или .rtf. Формат Rich Text добавляет дополнительные символы в ваш файл, и они добавляются в массив байтов. Который в конечном итоге вызывает недопустимую ошибку частного ключа. Короче говоря, скопируйте файл, а не контент.

Ответ 7

Вместо того, чтобы вручную разбора файла PEM, используйте BouncyCastle org.bouncycastle.util.io.pem.PemReader.

    String pem = "-----BEGIN......";
    byte[] content;
    try (PemReader pemReader = new PemReader(new StringReader(pem))) {
        content = pemReader.readPemObject().getContent();
    }
    PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(content);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    PrivateKey result = keyFactory.generatePrivate(encodedKeySpec);