Search code examples
sshbouncycastleopensshpublic-keyjava-security

How to convert ssh public key to PublicKey in java


I want to convert my ssh public key in *.pub file to a PublicKey(java.security.PublicKey).

The reason is that I need to inspect the algorithm of the ssh public key, is there any way to do that?

since I've got the below error.

java.lang.IllegalArgumentException: failed to construct sequence from byte[]: unexpected end-of-contents marker

    at org.bouncycastle.asn1.ASN1Sequence.getInstance(Unknown Source)
    at org.bouncycastle.asn1.x509.SubjectPublicKeyInfo.getInstance(Unknown Source)
import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;

public class PublicKeyUtil {
  private PublicKeyUtil() {}
  // (skip)
  public static PublicKey loadPublicKey(String encoded)
      throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {

    byte[] publicBytes = Base64.getDecoder().decode(encoded);
    PublicKey publicKey =
        new JcaPEMKeyConverter().getPublicKey(SubjectPublicKeyInfo.getInstance(publicBytes)); // I've got an error here, when calling the SubjectPublicKeyInfo.getInstance(publicBytes)

    return KeyFactory.getInstance(
            publicKey.getAlgorithm(), new org.bouncycastle.jce.provider.BouncyCastleProvider())
        .generatePublic(new X509EncodedKeySpec(publicBytes));
  }
  // (skip)
}

below is my build.gradle

implementation 'org.bouncycastle:bcprov-jdk18on:1.78.1'
implementation 'org.bouncycastle:bcpkix-jdk18on:1.78.1'

Solution

  • This can be achieved with BouncyCastle. First, an AsymmetricKeyParameter instance must be created. Actually, this is enough to use with BouncyCastle classes, so that PublicKey is not necessarily required.

    However, if PublicKey is required for any reason (e.g. if the JCA/JCE classes are to be used), it can be created via an OpenSSHPublicKeySpec instance.

    Since you have not specified an algorithm, an RSA key is used in the following example:

    import java.security.KeyFactory;
    import java.security.PublicKey;
    import java.security.Security;
    import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
    import org.bouncycastle.crypto.util.OpenSSHPublicKeyUtil;
    import org.bouncycastle.jcajce.spec.OpenSSHPublicKeySpec;
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    
    ...
    
    Security.addProvider(new BouncyCastleProvider());
    String ssh_rsa = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDgKfP2Vvx9HwhzvKFE4Yo5NdIec1KF+awxRtj+ArN83nBLErJ1FIS5hcTrJaUjAimoLfK2kigF4QWyfgjvTBzv8djCXEsjIhavbUyATaFEtq2JzrPFf2gLkgOkPhIH7ZNXx+YhHlMa44lFeyQNGqhOqaloWKuaeNw421FYx/cemuyv//OrdCLNgEms7kqDyc9V9JUkhsjElhHHIQA/SsI4eNrxNsUbLtQJQaThTm4o9iWSAjqy4sqJj//EmBwV2C4seHIuWztZTx5TF7LxD1nlxCcxONrea4LGxMK28ngujpzimEJb7ORmjvxmO62LpyZ9HuIwwKAhPsnVuTUElUD9 whatever";
    AsymmetricKeyParameter asymmetricKeyParameter = OpenSSHPublicKeyUtil.parsePublicKey(Base64.getDecoder().decode(ssh_rsa.split(" ")[1]));
    OpenSSHPublicKeySpec openSSHPublicKeySpec = new OpenSSHPublicKeySpec(OpenSSHPublicKeyUtil.encodePublicKey(asymmetricKeyParameter));
    PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(openSSHPublicKeySpec);
    
    ...
    

    Please note that in the second step, the algorithm must be specified. Depending on the algorithm, e.g. Ed25519 or ECDSA must be used instead of RSA (the algorithm can be determined from the prefix, e.g. for Ed25519 the prefix is ssh-ed25519 or for ECDSA something like ecdsa-sha2-nistp256).