0

After generate a pair of keys with the command:

ssh-keygen -b 2048 -t rsa

I am trying use the public key (file: 'key_rsa.pub') in a java/servlet project. In the servlet class, I read the file like this:

@WebServlet(name = "Login", urlPatterns = "/login")
public class Login extends HttpServlet {
...
        InputStream publicKeyInputStream = getClass().getClassLoader().getResourceAsStream("key_rsa.pub");
        if (publicKeyInputStream == null) {
            out.println("fail - no public key");
            out.flush();
            return;
        }
        String publicKeyString = new String(publicKeyInputStream.readAllBytes(), StandardCharsets.UTF_8);
        try {
            PublicKey publicKey = util.RSAKeyGenerator.getPublicKey(publicKeyString);
...
        } catch (Exception e) {
...
        }
}

the method 'RSAKeyGenerator.getPublic' looks like that:

public class RSAKeyGenerator {
    public static final String RSA_ALGORITHM = "RSA";

    public static final int KEY_SIZE = 2048;

    public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
        keyPairGenerator.initialize(KEY_SIZE);
        return keyPairGenerator.generateKeyPair();
    }

    public static PublicKey getPublicKey(String publicKeyString) throws Exception {
        byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyString);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        return keyFactory.generatePublic(keySpec);
    }

    public static PrivateKey getPrivateKey(String privateKeyString) throws Exception {
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyString);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        return keyFactory.generatePrivate(keySpec);
    }
}

wheh I execute the application, the line 'byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyString);' in the method is giving the error 'Illegal base64 character'. What I am done wrong here?

arquivo key_rsa.pub

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCko/IRJIBoIN6fe3k8mCzsereXnQlTSwHutyMqZh7/JqbWJA31036I0YZUuOJ0d4WAMDWktWrfn7dkAlt+dqQtAlyzPUrqOc/euQ0iZPYhrd8Gn9jn+8WAQyt9L3LemgQ1ZWnXgNUk5Z1Y1vcbp8GsRWWLrxR7uvtbonU7QDJv5vEFnleObPqKeqobbg1iUoaXYkw0IAqhCTNW3ZvfWQic4FeHeNNVtT8XUrGyNPZkSgQ8QHsvSvNiLJ2bROhBD4AUZKBN/XRvNs+OSJM7agGroznHGxpwj7jZ0V+MH1HvHt95+4SnFAQJVfjza3lUPoMIOphq24V6hLzCr17lPECj kleber@kleber-desktop

Update

I follow the sugestion from @WJS below, and now my getPublicKey method looks like that:

public static PublicKey getPublicKey(String publicKeyString) throws Exception {
    publicKeyString = publicKeyString.replaceAll("ssh-rsa\\s+", "").replaceAll("\\s.*","");
    byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyString);
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
    return keyFactory.generatePublic(keySpec);
}

I think this works for remove the extra characters at the beginning and at the end of the string, but now I am getting the error java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format in the line return keyFactory.generatePublic(keySpec);

Kleber Mota
  • 8,521
  • 31
  • 94
  • 188
  • 2
    I *think* the key actually has more characters in it than just the literal key itself. You should print it out and look at it. It's your public key, it should be safe to post here as well. – markspace Jul 13 '23 at 23:46
  • I copy and paste the content for the file rsa_key.pub in the question. I tried remove the substring "ssh-rsa " with `publicKeyString = publicKeyString.replaceAll("ssh-rsa ", "");`, but got the same error because I do not realize the final " kleber@kleber-desktop". I tried add `publicKeyString = publicKeyString.replaceAll(" kleber@kleber-desktop", "");` to remove that part too, but still got the same error. How I got only the key from this string? – Kleber Mota Jul 14 '23 at 00:04
  • Probably this part of the file at the end, kleber@kleber-desktop you should remove this and the leading space , and remove ssh-rsa and it's lagging space before decoding., And perhaps rejoin those onto it in their respective positions after decoding. – Samuel Marchant Jul 14 '23 at 01:09

3 Answers3

3

The OpenSSH key format (SSH2) uses a different encoding.

You must convert the key to PEM or DER or change your command to:

ssh-keygen -b 2048 -t rsa -m PEM.

John Hanley
  • 74,467
  • 6
  • 95
  • 159
2

X509EncodedKeySpec() requires a Base64 encoded ASN.1/DER encoded public key in X.509/SPKI format, while PKCS8EncodedKeySpec() requires a Base64 encoded ASN.1/DER encoded private key in PKCS#8 format.

Your ssh-keygen statement generated the keys in OPENSSH format (as already found in this answer), which cannot be directly processed by the posted Java code.


You can convert the private OpenSSH key into a private PKCS#8 key as follows (note that the file will be overwritten). The converted key is unencrypted (-N "") as required by the posted Java code:

ssh-keygen -p -N "" -m pkcs8 -f "<path to existing private OpenSSH key>" // attention: original file is overwritten

Important here is the specifier -m pkcs8, which defines PKCS#8 as the target format. The specifier -m pem used in the other answer defines PKCS#1, which also cannot be (directly) processed by the posted Java code.


The public key in X.509/SPKI format can be determined from the public OpenSSH key as follows:

ssh-keygen -f "<path to existing public OpenSSH key>" -e -m pkcs8 > "<path to X.509 key to be generated>"

or alternatively from the generated private PKCS#8 key:

ssh-keygen -f "<path to existing private PKCS#8 key>" -i -m PKCS8 -e > "<path to X.509 key to be generated>"

As an alternative to a conversion, the private key can be exported directly in PKCS#8 format:

ssh-keygen -b 2048 -t rsa -N "" -m pkcs8 -f "<path to private PKCS#8 key to be generated>"

as already posted in the other answer but with the required target format.

Note that the public key generated this way still has the OpenSSH format and must be explicitly converted as described above.


The generated PKCS#8 and X.509/SPKI keys are PEM encoded, i.e. they consist of header, footer and Base64 encoded body with line breaks after 64 characters. If the header, footer and line breaks are removed, the Base64 encoded ASN.1/DER encoded keys are obtained, which can be imported by the posted Java code.

Topaco
  • 40,594
  • 4
  • 35
  • 62
  • this worked, thank you. but can you see what the cause for this other issue: https://stackoverflow.com/questions/76686318/error-javax-crypto-badpaddingexception-decryption-error-while-trying-decrypt – Kleber Mota Jul 14 '23 at 09:15
1

I believe you may have extraneous characters at the beginning or end of the String. This worked for me. This ensures that only the string between the blanks is used.

key = key.replaceAll("ssh-rsa\\s+", "").replaceAll("\\s.*","");
byte[]bytes = Base64.getDecoder().decode(s);
WJS
  • 36,363
  • 4
  • 24
  • 39
  • I think this worked for the purpose of remove the extraneous characters, but now I am getting the error `java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format` in the line `return keyFactory.generatePublic(keySpec);` – Kleber Mota Jul 14 '23 at 00:38
  • Not certain about that. Your question was about an illegal base64 character. – WJS Jul 14 '23 at 01:54