2

I have the following public key (as an example):

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjKAmiieDC6SEYpxdI5Kn
iRUmUwE5EQL2nyDNDrV4dpC28mIcvKlCHWrq8YL7vpKya5GRUYl5xFNoB73s0UGn
8AtZBlG82/vbAPI5g9OoF2Df+0PusG5da+yFZXJNIyx1Kmgp4Ca4BR4WHGYo2LiW
zvhjCi9OBO6ERFrlCX1tGCI8mVxo54PzSMbo6LxYmJcJgUneVERjmQe1+tvggeP5
J44xJB5ompRkXg3VEeqYiqC8RfU3cL2DxTLsQqz/ndtpyGwjd1VCreXZCveDJlHN
WDZHvaHIReJa4aQp93NZVLhhVl0sHF1QM/7RSrDvRK7CGAZKq8COQ3/F2zLpMOPM
PQIDAQAB
-----END PUBLIC KEY-----

I need to be able to convert this into a format that I can insert into the ssh known_hosts file. For example:

localhost ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDj0HsJJ4I0p+gRYrpv3JFORk0NFB8YwlRdGBxja453pBzMBm6LyEmSqZAvv0UCguLI+Avo1HmDLJlrWd+1wVECRNSxD9twqcD5pkQcowy5hWOH0KbmBIdoYQqkm+nGhwSLSDJ5wO9k/F26D03d5/c0gNjB9UU9HrJ8zyB185vezxc6VM/kLlcoUMHT1aL/+cxbvlq5tkJDCmEQg05k4LgBWdfwUAXA1n3DlI9bU+CWb9hnmBUPFMHge56+Z1fhaJfvVW6VxLMh/W1NxK1Cxo4ig+0U0fYInqoMNcBT/6C7P2OdA8DbESCF5E7/9/eTLfsbW7EB7Ka3Mfyfm2a0Cswx

These two public keys should be the same, I think. They're just expressed in two different forms. (Right?) For the life of me, I can't figure out how to do this.

So far, I'm loading a private key from a PEM file and parse it like so. The PEMParser comes from bouncy castle:

FileReader keyPairReader = new FileReader(new File(applicationPropertiesService.getConfigDir(), KEY_PAIR_FILE));
PEMParser parser = new PEMParser(keyPairReader);

I then get the public/private keypair from the parser.

PEMKeyPair pemKeyPair = (PEMKeyPair) parser.readObject();

From here I can get a SubjectPublicKeyInfo object and I can get my data and encode it to base64:

String pkBase64 = new BASE64Encoder().encode(publicKey.getEncoded())

This gives me the base64 string in the first public key above. But, for the life of me, I can't figure out how to get the ssh-rsa format.

Any help would be appreciated.

Thanks!

Doug Hughes
  • 931
  • 1
  • 9
  • 21
  • 1
    Take a look [here](http://stackoverflow.com/questions/3588120/given-a-java-ssh-rsa-publickey-how-can-i-build-an-ssh2-public-key) – Ebbe M. Pedersen Oct 02 '14 at 21:23
  • Unfortunately, that simply doesn't return the correct value. I get the byte array back, base64-ify it, and it doesn't match the expected outcome. It's the right length and the first 31 characters match, but the rest of it doesn't. I assume the first 31 are due to the sha-rsa being prepended to the byte array. – Doug Hughes Oct 02 '14 at 22:10

2 Answers2

2
package hr.yottabyte.crypto.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class CryptoUtils {

    public static String encodeAsOpenSSH(X509Certificate certificate) throws Exception {
        PublicKey key = certificate.getPublicKey();
        String subject = certificate.getSubjectDN().getName();
        X509EncodedKeySpec spec = new X509EncodedKeySpec(key.getEncoded());
        KeyFactory kf = KeyFactory.getInstance("RSA");
        RSAPublicKey pubKey = (RSAPublicKey) kf.generatePublic(spec);       
        return encodeAsOpenSSH(pubKey, subject);
    }

    public static String encodeAsOpenSSH(RSAPublicKey key, String subject) {
        return encodeAsOpenSSH(key) + " " + subject;
    }
    
    public static String encodeAsOpenSSH(RSAPublicKey key) {
        byte[] keyBlob = keyBlob(key.getPublicExponent(), key.getModulus());
        byte[] encodedByteArray = Base64.getEncoder().encode(keyBlob);
        String encodedString = new String(encodedByteArray);
        return "ssh-rsa " + encodedString;
    }   

    private static byte[] keyBlob(BigInteger publicExponent, BigInteger modulus) {
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            writeLengthFirst("ssh-rsa".getBytes(), out);
            writeLengthFirst(publicExponent.toByteArray(), out);
            writeLengthFirst(modulus.toByteArray(), out);
            return out.toByteArray();
        } catch (IOException e) {
            System.out.println("Failed");
            e.printStackTrace();
        }
        return null;
    }

    private static void writeLengthFirst(byte[] array, ByteArrayOutputStream out) throws IOException {
        out.write((array.length >>> 24) & 0xFF);
        out.write((array.length >>> 16) & 0xFF);
        out.write((array.length >>> 8) & 0xFF);
        out.write((array.length >>> 0) & 0xFF);
        if (array.length == 1 && array[0] == (byte) 0x00)
            out.write(new byte[0]);
        else
            out.write(array);
    }

}
0

Ok, This wasn't quite what I thought it was. It turns out that link Ebbe M. Pedersen provided is correct after all. I had been comparing the resulting string to the wrong known key value. So, this had been working, my brain simply hadn't been working.

Doug Hughes
  • 931
  • 1
  • 9
  • 21