简体   繁体   中英

How to fix "invalid key format" while read public or private key from file?

I have created the public and private key . The public and private key generated from php code :

<?php
    require __DIR__ . '/vendor/autoload.php';
    use phpseclib\Crypt\RSA;
    $rsa = new RSA();
    extract($rsa->createKey()); 
    $rsa->setPrivateKeyFormat(RSA::PRIVATE_FORMAT_PKCS8);
    $rsa->setPublicKeyFormat(RSA::PUBLIC_FORMAT_PKCS8);

    file_put_contents("privateKey.pem",$privatekey);
    file_put_contents("publicKey.pem", $publickey);

The java source code to read those keys:

    import java.io.*;
    import java.security.*;
    import java.security.spec.*;

    public class PublicKeyReader {

    public static PublicKey get(String filename)
        throws Exception {

        File f = new File(filename);
        FileInputStream fis = new FileInputStream(f);
        DataInputStream dis = new DataInputStream(fis);
        byte[] keyBytes = new byte[(int)f.length()];
        dis.readFully(keyBytes);
        dis.close();

        X509EncodedKeySpec spec =
          new X509EncodedKeySpec(keyBytes);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePublic(spec);
      }

   public static void main (String [] args) throws Exception {
    PublicKeyReader publicKeyReader = new PublicKeyReader();
    PublicKey publicKey = publicKeyReader.get("key/testPub.pem");
    System.out.println(publicKey);
   }
   }

It produces java.security.InvalidKeyException: invalid key format.

Need help on this. Thanks in advance.

First of all, as noted in the comments, there is no such thing as a PKCS#8 public key. That means that the PHP library doesn't know what it is talking about. Instead, what you seem to get, if neubert is correct, is a structure defined for X.509 certificates called X509EncodedKeySpec . And in the Java code, you are indeed trying to use that to read in the public key.

However, what you forget is that X509EncodedKeySpec is a binary format, specified in ASN.1 DER. What you receive is a PEM encoded key that uses ASCII armor. In other words, the binary has been encoded to base64 and a header and footer line has been added. This is done to make it compatible with text interfaces such as mail (Privacy Enhanced Mail or PEM).

So what you do is to remove the armor. You can best do this using a PEM reader such as the one provided by Bouncy Castle.

PemReader reader = new PemReader(new FileReader("spki.pem"));
PemObject readPemObject = reader.readPemObject();
String type = readPemObject.getType();
byte[] subjectPublicKey = readPemObject.getContent();

System.out.println(type);

X509EncodedKeySpec spec = new X509EncodedKeySpec(subjectPublicKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPublicKey pubKey = (RSAPublicKey) kf.generatePublic(spec);
System.out.println(pubKey);

which prints

PUBLIC KEY

followed by

Sun RSA public key, 1024 bits
  params: null
  modulus: 119445732379544598056145200053932732877863846799652384989588303737527328743970559883211146487286317168142202446955508902936035124709397221178664495721428029984726868375359168203283442617134197706515425366188396513684446494070223079865755643116690165578452542158755074958452695530623055205290232290667934914919
  public exponent: 65537

For the skeptics - hi neubert ;) - here is the definition of SubjectPublicKeyInfo from the X.509 specification :

SubjectPublicKeyInfo  ::=  SEQUENCE  {
     algorithm            AlgorithmIdentifier,
     subjectPublicKey     BIT STRING  }

where subjectPublicKey contains the encoded public key in PKCS#1 format - in the case of an RSA public key, of course.

And here is the decoded version of neubert's key so you can compare. The parsed key in Java is the same key.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM