0

I have two text files, one with a "-----BEGIN CERTIFICATE-----" header, one with a "-----BEGIN RSA PRIVATE KEY-----" header. I need to use CXF ClientBuilder to make a REST service call to a remote host.

I think my loading of the cert file is ok, but I can't figure out how to handle the private key file properly.

I have the following tentative code (which doesn't quite compile yet, as you'll see) to initialize the Client object (with some minor elisions):

private Certificate buildCertFromFile(String fileName) throws CertificateException {
    return CertificateFactory.getInstance("X.509").generateCertificate(ClassLoaderUtils.getResourceAsStream(fileName, <ThisClass>.class));
}

@PostConstruct
public void init() {
    try {
        KeyStore    trustStore  = KeyStore.getInstance("jks");
        trustStore.load(null, null);
        trustStore.setCertificateEntry("cert", buildCertFromFile("<path to cert file>"));

        KeyStore    keyStore    = KeyStore.getInstance("jks");
        keyStore.load(null, "abc".toCharArray());

        // Need something here.
        keyStore.setKeyEntry("key", key, "abc", chain);

        ClientBuilder   builder = ClientBuilder.newBuilder();
        builder.trustStore(trustStore);
        builder.keyStore(keyStore, "abc");

        builder.hostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String host, SSLSession session) {
                try {
                    Certificate[] certs = session.getPeerCertificates();
                    return certs != null && certs[0] instanceof X509Certificate;
                }
                catch (SSLException ex) {
                    return false;
                }
            }

        });

        client  = builder.build();
    }
    catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException ex) {
        ex.printStackTrace();
    }
}

Update:

I'm experimenting with the following method to load the private key file:

public static PrivateKey getPrivateKey(String filename) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
    String  contents    = IOUtils.toString(ClassLoaderUtils.getResourceAsStream(filename, TguardService.class), "UTF-8");
    contents    = contents.replaceAll("-----[A-z ]+-----", "").trim();
    System.out.println("contents[" + contents + "]");
    byte[]  bytes   = Base64.getDecoder().decode(contents);
    System.out.println("decoded[" + new String(bytes) + "]");
    PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
    KeyFactory kf = KeyFactory.getInstance("RSA");
    return kf.generatePrivate(spec);
}

However, this is giving me the following error:

Caused by: java.lang.IllegalArgumentException: Illegal base64 character a
at java.util.Base64$Decoder.decode0(Unknown Source)
at java.util.Base64$Decoder.decode(Unknown Source)
at java.util.Base64$Decoder.decode(Unknown Source)
at com.att.detsusl.tguardrest.TguardService.getPrivateKey(TguardService.java:58)

The debugging output shows that "contents" looks somewhat like this:

contents[MIIEp....
...
...
...kdOA=]

Update:

Ok, I managed to figure out that I had to remove ALL the newlines from the encoded string, so now it gets through base64 decode, but now it fails on the call to "generatePrivate()", with the following:

java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(Unknown Source)
at java.security.KeyFactory.generatePrivate(Unknown Source)
at com.att.detsusl.tguardrest.TguardService.getPrivateKey(TguardService.java:63)

I've seen some notes that imply that I must have a "PKCS#1" format, instead of "PKSC#8" and talk about calling "openssl" to convert the file. I'd really rather not do that. Is there a straightforward way to do this conversion (assuming this is what I need) in Java? I intend this code to execute only once at application startup.

Update:

Ok, after considering the alternatives, I used openssl to convert the file to PKCS#8 format. After that, I was able to complete the construction of the Client object, but I get another error while trying to make a connection. I considered it possible that this is a related but different problem (and I wanted to reduce the number of "Updates" on a single posting), so I posted that as a separate question at CXF REST client call with 2-way auth failing with "unable to find valid certification path to requested target" .

Also note that I've converted the key file from PKCS#1 to PKCS#8, but I never did anything with the cert file.

In the meantime, I could use a little more background on PKCS#1 vs. PKCS#8. How "legacy" is PKCS#1? I note that the original name of the key file that was given to me used an acronym representing the previous name of our organization, which changed several years ago.

Community
  • 1
  • 1
David M. Karr
  • 14,317
  • 20
  • 94
  • 199
  • 1
    Yes, you indeed have OpenSSL 'legacy' format, which is PKCS1 not PKCS8. Converting to PKCS8 with OpenSSL is much easier, but since you don't want that see http://stackoverflow.com/questions/7216969/getting-rsa-private-key-from-pem-base64-encoded-private-key-file or with bouncycastle http://stackoverflow.com/questions/3243018/how-to-load-rsa-private-key-from-file . Or consider using PKCS12 instead for cert(s) AND privatekey, which is supported by basic Java and doesn't make your key immediately obvious to anyone who glances momentarily at your jar. – dave_thompson_085 Apr 06 '17 at 22:37
  • As you can see from my update, I've decided to convert the file for now, but I need more info. I could also use more background on your suggestion to convert to PKCS12 and your comments about that. – David M. Karr Apr 07 '17 at 15:24
  • Is there any practical way to determine exactly which PKCS standard a file is written to? – David M. Karr Apr 07 '17 at 15:48

0 Answers0