6

I have a file containing a public RSA key (generated with ssh-keygen). I'd like to read the file and generate a PublicKey object.

Prior to that I converted the file, since reading the original files seems to be impossible:

# http://unix.stackexchange.com/questions/220354/how-to-convert-public-key-from-pem-to-der-format/220356#220356
ssh-keygen -f ~/.ssh/id_rsa.pub -e -m PEM > ~/.ssh/id_rsa.pub.pem
openssl rsa -RSAPublicKey_in -in ~/.ssh/id_rsa.pub.pem -inform PEM -outform DER -out ~/.ssh/id_rsa.pub.der -RSAPublicKey_out

From Java - Encrypt String with existing public key file I defined the function readFileBytes:

public static byte[] readFileBytes(String filename) throws IOException {
    Path path = Paths.get(System.getProperty("user.home") + filename);
    return Files.readAllBytes(path);
}

Now I'd like to read the file and generate the PublicKey object, but I could not find a way to do that; java.security.spec.RSAPublicKeySpec does not provide a fitting constructor and java.security.spec.X509EncodedKeySpec throws an error java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: algid parse error, not a sequence:

//RSAPublicKeySpec publicSpec = new RSAPublicKeySpec(readFileBytes("/.ssh/id_rsa.pub.der"));
// No fitting construktor

X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(readFileBytes("/.ssh/id_rsa.pub.der"));
// Gives: "algid parse error, not a sequence"
Community
  • 1
  • 1
Edward
  • 4,453
  • 8
  • 44
  • 82

2 Answers2

6

I had a project in which (RSA) encryption was necessary, this is how I reconstructed the publicKey given the publicKey's byte array, that was just read from the file.

public PublicKey reconstruct_public_key(String algorithm, byte[] pub_key) {
    PublicKey public_key = null;

    try {
        KeyFactory kf = KeyFactory.getInstance(algorithm);
        EncodedKeySpec pub_key_spec = new X509EncodedKeySpec(pub_key);
        public_key = kf.generatePublic(pub_key_spec);
    } catch(NoSuchAlgorithmException e) {
        System.out.println("Could not reconstruct the public key, the given algorithm oculd not be found.");
    } catch(InvalidKeySpecException e) {
        System.out.println("Could not reconstruct the public key");
    }

    return public_key;
}

Then you could call the procedure similar to this call, reconstruct_public_key("RSA", readFileBytes("path/to/your/publicKey/file"));

EDIT : I tried to do it myself (write the public key to a file, read that file and reconstruct the key). This works :

public static void main(String args[]) {
    String path = "./pub_key_test.txt";

    // Generate a keypair to write to file
    KeyPair kp = generate_key();
    PublicKey pub_key = kp.getPublic();
    File file = new File(path);

    try {
        // Write to file
        file.createNewFile();
        FileOutputStream out = new FileOutputStream(path);

        out.write(pub_key.getEncoded()); // Write public key to the file
        out.close();

        // Read from file
        FileInputStream in = new FileInputStream(path);
        byte[] pub_key_arr = new byte[in.available()];
        in.read(pub_key_arr, 0, in.available());
        in.close();

        // Reconstruct public key
        PublicKey reconstructed_pub_key = reconstruct_public_key("RSA", pub_key_arr);
    } catch(IOException e) {
        System.out.println("Could not open the file : " + e.getStackTrace());
    }
}

And this is the generate_key procedure :

public KeyPair generate_key() {
    while(true) { // Else the compiler will complain that this procedure does not always return a "KeyPair"
        try {
            final KeyPairGenerator key_generator = KeyPairGenerator.getInstance("RSA");
            key_generator.initialize(2048); // Keys of 2048 bits (minimum key length for RSA keys) are safe enough (according to the slides 128bit keys > 16 years to brute force it)

            final KeyPair keys = key_generator.generateKeyPair();
            return keys;
        } catch(NoSuchAlgorithmException e) {
            System.out.println("The given encryption algorithm (RSA) does not exist. -- generate_key() - Cryptography.");
        }
    }
}

If you test this, you will see that the publicKey is reconstructed successfully.

EDIT : I tried doing it myself, using the ssh-keygen tool. This is what i did :

  • First I generated a RSA private key (.PEM format)
  • Outputted the public key portion to .DER format, so it can be used by Java.

This is how I did the conversion, which is a bit different of yours :

openssl rsa -in private_key_file.pem -pubout -outform DER -out java_readable_file.der

And I did the file reading like here, which doesn't differ much of yours. I tested this and Java successfully reconstructed the public key.

Kevin
  • 2,813
  • 3
  • 20
  • 30
  • Well, than I always get "Could not reconstruct the public key" - but I still don't know why the public key can't be reconstructed,... Maybe something went wrong during the conversion-process, but what? Would be also great to have a way to read the `ssh-keygen`-generated files directly, without the manual conversion in the terminal. – Edward Aug 20 '15 at 11:53
  • @Edward I edited my answer in order to answer your question. – Kevin Aug 20 '15 at 12:13
  • I try to read existing keys that have been generated with the tool `ssh-keygen` (typically in `~/.ssh/id_rsa.pub`). They seem to be in `PEM`-format. I managed to read the private key from `~/.ssh/id_rsa`, but reading the public-key does not work. It really seems to be a problem with the files generated with `ssh-keygen` (hope its clear now). [+1 for your example] – Edward Aug 20 '15 at 12:25
4

Creating RSA Private Key

openssl genrsa -out rsaprivkey.pem 1024

Generates the public key in DER format.

openssl rsa -in rsaprivkey.pem -pubout -outform DER -out rsapubkey.der

We use this code extracts the public key RSA OR DSA from the X.509 certificate.

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;

/**
 * This class is capable of extracting a public key from a X.509 certficate 
 * and returning the PublicKey representation from a referenced byte array.
 * 
 */
public class ExtractPublicKey {

  // Certificate Filename (Including Path Info)
  private static final String certFilename = "cacert.pem";

  // Public Key Filename (Including Path Info)
  private static final String pubKeyFilename = "rsapublic.key";

  public static PublicKey generatePublicKey(byte[] encodedKey)
      throws NoSuchAlgorithmException, InvalidKeySpecException {

    X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(encodedKey);
    boolean isSupportedKey = false;
    KeyFactory factory;
    PublicKey retKey = null;

    //first try the DSA alg
    try {
      factory = KeyFactory.getInstance("DSA");
      retKey = factory.generatePublic(pubSpec);
      isSupportedKey = true;
    } catch (InvalidKeySpecException e) {
      System.out.println("Could not create DSA Public Key: " + e.toString());      
    }

    //if DSA didnt work, then try RSA    
    if (!isSupportedKey) {
      try {
        factory = KeyFactory.getInstance("RSA");
        retKey = factory.generatePublic(pubSpec);
        isSupportedKey = true;
      } catch (InvalidKeySpecException e) {
        System.out.println("Could not create RSA Public Key: " + e.toString());
      }      
    }

    // if not DSA or RSA
    if (!isSupportedKey) {
      throw new InvalidKeySpecException("Unsupported key spec: Not RSA or DSA");
    }

    return retKey;
  }   

}
Kevin
  • 2,813
  • 3
  • 20
  • 30
  • We provide SSO for Google apps and for extracting public key from x509 we use above code. And Its a Java code not Google app specific code This also work fine with-in our other project. If you want more information How to extract OR How to generate key Check this link. https://developers.google.com/google-apps/help/articles/sso-keygen#creating-rsa-key-pairs – Mangesh Mandavgane Aug 31 '15 at 04:39