java.security.InvalidKeyException:从PEM文件生成公用,专用密钥时,无效的密钥格式

时间:2018-10-04 03:35:40

标签: java rsa sha1 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);

例外:

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文件内容:

  

----- BEGIN RSA私钥-----       MIIEowIBAAKCAQEAwnEEodFEf86 + Ae + wYyI // u1kekIWnA3RfzbAwWD77uG7D9Ci       9vVNbPO4XT2hKL03 / q7d7KTgrA1sjBltfaOzVfA56x1S / 0cYVk4xI440dpLo0F + m       RIqRw5fh8IuUlUIr3I4A7ESkDQQsZbDpdggCiNbrlADqLotcZyB4rU4uURW8QUI / W       eqsD6TOQs4bI + 3o3xAKkky2kXujSaaa3tDxgUPTmSQ0Buk7Hx / IVzwyV7qjWiR4U       C46rHpnWxfF0DWuJUOYgJmBQ8xFOQwt4Ec / u + 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       -----结束RSA私钥-----

我尝试手动删除文件的页眉和页脚。我尝试了来自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 :(得分:4)

部分欺骗Load RSA public key from file

因此,您“编写”(假定为复制)代码清楚地表明您需要两个DER格式的文件,其中包含PKCS8和“ X509”编码。 (Java在这里所谓的X.509实际上是X.509的SubjectPublicKeyInfo component 。)您给它一个文件,以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()));
    
  • 您可以通过从上述转换中省略-outform der来将文件转换为PKCS8和'X509',然后读取这些文件并通过删除来手动进行'de-PEM'标题行和结尾行,并将base64转换为二进制(删除或跳过换行符);这将导致二进制PKCS8和X509编码,您可以在现有代码中运行它们。在openssl方面的工作量很大,而在Java方面的工作量更多,因此没有明显的优势,除了PEM文件是有效的文本,并且在必要时可以剪切和粘贴。

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

  • 使用纯Java中的格式(未转换)的一种方法是,根据项目符号3取消PEM,为您提供PKCS1编码,然后手动构造PKCS8编码,然后像以前一样继续运行PKCS8,直到完成KeyFactory并提取每个项目符号的公钥2。请参阅我在Java: Convert DKIM private key from RSA to DER for JavaMail上的答案以获取执行此操作的非常难看的方法(包括一种de-PEM方法)。如果您使用BouncyCastle(具有用于此ASN.1类型的类),则是一种更好的方法,但是,如果您使用BouncyCastle,则最好完全不使用此方法,请参见下文。

  • 在纯Java中使用未转换格式的另一种方法是根据项目符号3解PEM,然后解析the ASN.1 structure of PKCS1并构造一个RSAPrivateCrtKeySpec,您可以在{{1} } 而不是使用PKCS8编码,然后每个子弹2提取publickey。这甚至更加复杂,尽管我认为我在某处看到过它。如果发现的话会添加。再次,BouncyCastle可以改进此方法,但不需要,请参见下文。

  • 最后,如果您有BouncyCastle,那就太容易了。您没有说“没有运气”时尝试了什么,但是下面的BouncyCastle代码是您所需要的并且可以正常工作:

    KeyFactory

    请注意,这为您提供了一个文件中包含私钥和公钥对象的try( Reader r = new FileReader(filename) ){ KeyPair pair = new JcaPEMKeyConverter().getKeyPair((PEMKeyPair)new PEMParser(r).readObject()); }