java.security.InvalidKeyException:从 PEM 文件生成公钥、私钥时密钥格式无效

ami*_*mit 1 java sha1 rsa public-key-encryption pem

我经历了许多类似的话题,但没有运气!!

我想使用 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);
Run Code Online (Sandbox Code Playgroud)

例外:

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)
Run Code Online (Sandbox Code Playgroud)

PEM 文件内容:

----- BEGIN RSA私钥----- MIIEowIBAAKCAQEAwnEEodFEf86 +阂+ wYyI // u1kekIWnA3RfzbAwWD77uG7D9Ci 9vVNbPO4XT2hKL03 / q7d7KTgrA1sjBltfaOzVfA56x1S / 0cYVk4xI440dpLo0F +米RIqRw5fh8IuUlUIr3I4A7ESkDQQsZbDpdgCiNbrlADqLotcZyB4rU4uURW8QUI / W eqsD6TOQs4bI + 3o3xAKkky2kXujSaaa3tDxgUPTmSQ0Buk7Hx / IVzwyV7qjWiR4U C46rHpnWxfF0DWuJUOYgJmBQ8xFOQwt4Ec / U + 0m8top8cqQF + gpBn9iLXpbtahA3 pqyvLuNXRH9yn8mlEneBrjjl6U0H3W / AV7 / dGwIBAwKCAQEAgaCtwTYtqonUAUp1 l2wqqfOYUYFkaAk2VM8rK5X9SevSCosXT04znffQPikWGyjP / 8 + ncNAcrOdsrue U8J3jqAmnL43VNoQOYl2F7Qi + bdF4D / ELbG2gmVBSwe4Y4FykwlV8thtXgLIQ8tG TqsWznyYqtGybI9mhWlyN7Ji2POMDZP5Lwx7M01pMezwpnsZSmPVL9TgVrtWv4xt C0vPyuy9THlFWtkOdHItNK + vOTcpuHn29rFUJI / D3R + SQjcdqj3aaqljOtdeBxgd yDl2 / Z4rUyetgzcZMfNTt / NRT0hOJ6R6 / 2S7gFCTtxMHBh3vVCH + pLLnQyJvcPQuAsORSwKBgQDhOPr1x / 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+vnRzZa2AOsmo995gsXLdfsJn0o4Z3LsQJUDRI3tQ4dXe/5jS4oFrOdxALOAw6YmvEv/3oHwsCYPDhqLNfIJ9I6m Dt3yG61pUJiCarhPaYG17NQoCxF6Xi6GUajRsECbr8DIVGMAu-----ENDKEY-----END

我尝试手动删除文件页眉和页脚。我尝试了 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
Run Code Online (Sandbox Code Playgroud)

我是第一次这样做,所以如果有任何愚蠢的错误,请原谅我。

dav*_*085 5

部分欺骗从文件加载 RSA 公钥

因此,您“编写”(我假设是复制)代码,明确说明您需要两个 DER 格式的文件,其中包含 PKCS8 和“X509”编码。(这里 Java 所说的 X.509 实际上是 X.509的 SubjectPublicKeyInfo组件。)你给它一个文件,以 PEM 形式而不是 DER,包含一个 PKCS1 编码,而不是 PKCS8X509——你很惊讶它不起作用?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
    
    Run Code Online (Sandbox Code Playgroud)

    现在可以通过您发布的代码读取这些文件(文件名分开除外)。请注意,此转换不必在同一系统上完成;如有必要,您可以在其他地方执行并复制文件,如果您使用适用于二进制文件的方法,即不剪切和粘贴。

  • 如果你真的只想要一个文件,但它可以被转换,private.der按照上面的方法创建文件并只用你的代码中与私钥相关的部分来读取它,然后执行:

    RSAPrivateCrtKey priv2 = (RSAPrivateCrtKey)privKey;
    PublicKey pubkey = keyFactory.generatePublic(new RSAPublicKeySpec(priv2.getModulus(), priv2.getPublicExponent()));
    
    Run Code Online (Sandbox Code Playgroud)
  • 您可以通过省略上述转换将文件转换为 PKCS8 和 'X509' PEM-outform der,然后读取这些文件并通过删除标题和尾部行并将 base64 转换为二进制(删除或跳过换行符)来手动“de-PEM” ; 这会生成二进制 PKCS8 和 X509 编码,您可以运行现有代码。这与 openssl 方面的工作一样多,而在 Java 方面的工作更多,因此没有明显的优势,除了 PEM 文件是有效文本并且可以在必要时进行剪切和粘贴。

  • 结合这些,您可以仅转换为 PKCS8 PEM,阅读每个项目符号 3(de-PEM,然后是代码的私钥部分),然后从每个项目符号 2 的私钥中提取公钥

  • 在普通 Java 中使用您拥有的(未转换的)格式的一种方法是对每个项目符号 3 进行 de-PEM,为您提供 PKCS1 编码,然后手动构建 PKCS8 编码,然后像以前一样继续通过 PKCS8 运行 PKCS8KeyFactory并提取每个项目符号的公钥2. 请参阅我在Java 上的回答:Convert DKIM private key from RSA to DER for JavaMail,这是一种非常丑陋的方法(包括一种 de-PEM 方法)。如果您使用 BouncyCastle(它有一个用于此 ASN.1 类型的类),则有更好的方法,但如果您使用 BouncyCastle,则最好完全不要使用此方法,请参见下文。

  • 在普通 Java 中使用未转换格式的另一种方法是按第 3 项对 PEM 进行 de-PEM,然后解析PKCS1 的 ASN.1 结构并构造一个RSAPrivateCrtKeySpec您可以运行的KeyFactory 而不是PKCS8 编码,然后按第 2 项提取公钥。这甚至更复杂,虽然我想我在某处见过它;如果我找到它会添加。同样,BouncyCastle 可以改进这种方法,但不需要,见下文。

  • 最后,如果你有 BouncyCastle,那就太容易了。你没有说你试过什么“没有运气”,但下面的 BouncyCastle 代码是你所需要的,并且确实有效:

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

    请注意,这为您KeyPair提供了一个文件中的私钥和公钥对象。