java.security.InvalidKeyException: неверный формат ключа при генерации открытого, закрытого ключа из файла PEM - PullRequest
0 голосов
/ 04 октября 2018

Я прошел через много похожих тем, но не повезло !!

Я хочу сгенерировать открытый и закрытый ключи, используя файл PEM.Ниже приведен код, который я использую для этого:

        String pemFileNme = "C:\\Users\\amitmm\\Desktop\\clean\\key.pem";

        File pubKeyFile = new File(pemFileNme);
        File privKeyFile = new File(pemFileNme);

        // read public key DER file
        DataInputStream dis = new DataInputStream(new 
        FileInputStream(pubKeyFile));
        byte[] pubKeyBytes = new byte[(int)pubKeyFile.length()];
        dis.readFully(pubKeyBytes);
        dis.close();

        // read private key DER file
        dis = new DataInputStream(new FileInputStream(privKeyFile));
        byte[] privKeyBytes = new byte[(int)privKeyFile.length()];
        dis.read(privKeyBytes);
        dis.close();

        KeyFactory keyFactory = KeyFactory.getInstance("RSA");

        // decode public key
        X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pubKeyBytes);
        RSAPublicKey pubKey = (RSAPublicKey) 
        keyFactory.generatePublic(pubSpec);

        // decode private key
        PKCS8EncodedKeySpec privSpec = new 
        PKCS8EncodedKeySpec(privKeyBytes);
        RSAPrivateKey privKey = (RSAPrivateKey) 
        keyFactory.generatePrivate(privSpec);

Исключение:

Exception in thread "main" java.security.spec.InvalidKeySpecException: 
java.security.InvalidKeyException: invalid key format
at
sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:205)
at java.security.KeyFactory.generatePublic(KeyFactory.java:334)
at main.java.me.txedo.security.Main2.f1(Main2.java:47)
at main.java.me.txedo.security.Main2.main(Main2.java:20)
Caused by: java.security.InvalidKeyException: invalid key format
at sun.security.x509.X509Key.decode(X509Key.java:387)

Содержимое файла PEM:

----- НАЧАЛОRSA PRIVATE KEY ----- MIIEowIBAAKCAQEAwnEEodFEf86 + Ae + wYyI // u1kekIWnA3RfzbAwWD77uG7D9Ci 9vVNbPO4XT2hKL03 / q7d7KTgrA1sjBltfaOzVfA56x1S / 0cYVk4xI440dpLo0F + м RIqRw5fh8IuUlUIr3I4A7ESkDQQsZbDpdgCiNbrlADqLotcZyB4rU4uURW8QUI / W eqsD6TOQs4bI + 3o3xAKkky2kXujSaaa3tDxgUPTmSQ0Buk7Hx / IVzwyV7qjWiR4U C46rHpnWxfF0DWuJUOYgJmBQ8xFOQwt4Ec / у + 0m8top8cqQF + gpBn9iLXpbtahA3 pqyvLuNXRH9yn8mlEneBrjjl6U0H3W / AV7 / dGwIBAwKCAQEAgaCtwTYtqonUAUp1 l2wqqfOYUYFkaAk2VM8rK5X9SevSCosXT04znffQPikWGyjP / x8 + ncNAcrOdsrue U8J3jqAmnL43VNoQOYl2F7Qi +bdF4D / ELbG2gmVBSwe4Y4FykwlV8thtXgLIQ8tG TqsWznyYqtGybI9mhWlyN7Ji2POMDZP5Lwx7M01pMezwpnsZSmPVL9TgVrtWv4xt C0vPyuy9THlFWtkOdHItNK + vOTcpuHn29rFUJI / D3R + SQjcdqj3aaqljOtdeBxgd yDl2 / Z4rUyetgzcZMfNTt / NRT0hOJ6R6 / 2S7gFCTtxMHBh3vVCH + pLLnQyJvcPQu AsORSwKBgQDhOPr1x / 8BioqaasoXvO9NsGktCgPDjbC4d3jR8n6lCa42X / eIahaD xi1VGWyQhdO7aMXiDmzOtox7xHcMRh + a5ySIs9gTsHkMB2hqwIUNg25INRkQ3Vr3 eWnoTBGsfJqC1TEME3ocKwmyz57ZAe4yyR / ZRdDX5DUt9qCCFeA8uQKBgQDdAzbq 7BlJkbTYfdlIRNJEJAO3wWqQTx8X0ttCMMwDluOT9l + RR / KuUxl85ph + kwJci6E / ixfeMTW1NcsMY / lB6mTP0oooalU1MP7gpPSu + 24zhLXnUHZotbNbv9nk6w / 1WWhz FBt5w2DG4kQPFK6LSySqcVuzIGQyvWD5PbpGcwKBgQCWJfyj2qoBBwcRnIa6ffTe dZtzXAKCXnXQT6XhTFRuBnQkP / pa8WRX2XOOEPMLA + J88IPsCZ3fJF2n2E9dhBUR 722wd + VidaYIBPBHKwNeV57azhC16OdPpkaa3WEdqGcB43YIDPwSx1vMimnmAUl3 ML / mLos6mCNz + cBWuUAoewKBgQCTV3nx8ruGYSM6 / pDa2IwtbVfP1kcK32oP4eeB dd1Xue0NTupg2qHJjLuombr / DKw9smt / SG / pdiPOI9yy7VDWnEM1NwbFnDjOIKnr GKMfUkl3rc6aNaRFzneSf + aYnLVOO5r3Yrz715XZ7C1fYx8Hh23G9j0iFZgh05X7 fnwu9wKBgHyC0X26KZQ0ukan5jDSiz4dapUp2d3F + vnRzZa2AOsmo995gsXLdfsJ n0o4Z3LsQJUDRI3tQ4dXe /5jS4oFrOdxALOAw6YmvEv / 3oHwsCYPDhqLNfIJ9I6m Dt3yG61pUJiCArhPaYG17NQoCxF6Xi6GUajRsECbr8DdyGMAu5eE вручную - END RS3 - 101 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *.Я попробовал код из Bouncycastle, не повезло, та же ошибка.

Python-код, который работает с этим файлом:

def t2e_enc(plaintext, pk_pem_file = './2017-12-04T062008Z.pem'):
''' 
Function for encryption of Track2 credit card data.
This function uses private key to derivate public part used for encryption

'''
with open(pk_pem_file, 'rb') as pk:
    private_key = serialization.load_pem_private_key(pk.read(), 
    password=None, backend=default_backend())

public_key = serialization.load_pem_public_key(
    private_key.public_key().public_bytes(
        serialization.Encoding.PEM, 
        serialization.PublicFormat.SubjectPublicKeyInfo),
        backend=default_backend()
    )

ciphertext = public_key.encrypt(
    plaintext,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA1()),
        algorithm=hashes.SHA1(),
        label=None
    )
)
b64ciphertext=base64.b64encode(ciphertext)
return b64ciphertext

Я делаю это впервые, так что терпите меня, если естьЛюбая глупая ошибка.

1 Ответ

0 голосов
/ 04 октября 2018

Partial dupe Загрузка открытого ключа RSA из файла

Итак, вы «написали» (я полагаю, скопировал) код, который четко говорит, что вам нужны два файла в форме DER, содержащие PKCS8и кодировки «X509».(То, что Java называет X.509, на самом деле представляет собой компонент SubjectPublicKeyInfo из X.509.) Вы предоставляете ему один файл в форме PEM, а не DER, содержащий кодировку PKCS1, а не PKCS8 или .X509 - а ты удивлен, что это не работает?Python работает, потому что он вызывает OpenSSL, а OpenSSL поддерживает более десятка кодировок и форматов для приватных ключей, включая этот;Java поддерживает только один (вне хранилищ ключей), который не является этим.(Голые) публичные клавиши немного лучше;внутренне libcrypto поддерживает несколько форм, но на практике используются только две из них, и одна из них соответствует Java - хотя многие публичные ключи распространяются, хранятся и используются в форме сертификатов X.509, что предоставляет еще несколько форм для беспокойствао.

В вашей ситуации есть приблизительно 7 решений:

  • самое простое - использовать командную строку OpenSSL для преобразования вашего одного файла в два файла, которые хочет Java:

    # (corrected! pkey is inconsistent!) 
    openssl pkcs8 -topk8 -nocrypt -in input.pem -outform der -out private.der
    
    openssl pkey -in input.pem -pubout -outform der -out public.der
    # or for very old versions (should not be needed now)
    openssl rsa -in input.pem -pubout -outform der -out public.der
    

    Эти файлы теперь могут быть прочитаны с помощью кода, который вы разместили (за исключением разделенных имен файлов).Обратите внимание, что это преобразование не должно быть сделано в той же системе;при необходимости вы можете сделать это в другом месте и скопировать файлы, если вы используете метод, который работает с двоичными файлами, т.е. НЕ вырезать и вставлять.

  • , если вы действительно хотите только один файл, но это может бытьконвертируйте, создайте файл private.der, как указано выше, и прочитайте его, используя только части кода, связанные с закрытыми ключами, затем выполните:

    RSAPrivateCrtKey priv2 = (RSAPrivateCrtKey)privKey;
    PublicKey pubkey = keyFactory.generatePublic(new RSAPublicKeySpec(priv2.getModulus(), priv2.getPublicExponent()));
    
  • вы можете конвертировать файлы в PKCS8 и'X509' PEM , опуская -outform der в приведенных выше преобразованиях, затем считывая эти файлы и вручную де-PEM, удаляя заголовок и строки трейлера и преобразовывая base64 в двоичный файл (удаляя или пропуская разрывы строк);это приводит к бинарным кодировкам PKCS8 и X509, которые вы можете запустить через существующий код.Это так же много работы на стороне openssl и больше работы на стороне Java, поэтому нет очевидного преимущества, за исключением того, что файлы PEM являются допустимым текстом и могут быть вырезаны и вставлены при необходимости.

  • комбинируя их, вы можете конвертировать только в PEM PKCS8, читать это по пуле 3 (de-PEM, затем части вашего закрытого ключа), а затем извлекать publickey из privatekey по пуле 2

  • один из способов использовать формат, который у вас есть (не преобразованный) в простой Java, - это де-PEM для пули 3, давая вам кодировку PKCS1, затем вручную создать кодировку PKCS8, а затем продолжить работу, как раньшечтобы запустить PKCS8 через KeyFactory и извлечь publickey по пуле 2. См. мой ответ на Java: преобразовать закрытый ключ DKIM из RSA в DER для JavaMail , чтобы получить действительно уродливый способ сделать это (включая один де-PEM метод).Есть лучший способ, если вы используете BouncyCastle (у которого есть класс для этого типа ASN.1), но если вы используете BouncyCastle, лучше вообще не использовать этот метод, см. Ниже.

  • Еще один способ использовать не преобразованный формат в простой Java - это де-PEM для каждого пункта 3, затем проанализировать структуру ASN.1 PKCS1 и создать RSAPrivateCrtKeySpec, который вы можете запустить через KeyFactory вместо кодировки PKCS8 затем извлекают publickey для пули 2. Это еще более сложно, хотя я думаю, что где-то видел это;добавлю если найду.И снова BouncyCastle может улучшить этот метод, но не нуждается в этом, см. Ниже.

  • наконец, если у вас есть BouncyCastle, это очень просто.Вы не говорите, что пытались «без удачи», но следующий код BouncyCastle - это все, что вам нужно и работает:

    try( Reader r = new FileReader(filename) ){
      KeyPair pair = new JcaPEMKeyConverter().getKeyPair((PEMKeyPair)new PEMParser(r).readObject());
    }
    

    Обратите внимание, что это дает вам KeyPair с объектами privatekey и publickeyиз одного файла.

...