Search code examples
javasslibm-midrange

getting an AS400 java application called from RPGLE to trust a .PFX certificate


I have been working on this all day, and am just guessing as to what to do. It seems as though people that know how this functionality works assumes that everyone else knows what they are talking about. They will say things like use utility ABC to generate DEF, but assume that you know what ABC is, how to use it, and what to do with DEF after it's done. Let me pre-face by saying I'm an AS400 RPG coder at heart, but can do a few things in Java. Enough to be dangerous I guess. I still don't quite understand what a trust store, or key store even is technically.

Basically, I have developed a simple AS400 java application, which basically just calls some HTTP methods to send a transaction to an external party. This application is called directly from an RPGLE program. This has been working fine, but now they have decided to use HTTPS. The client has sent me a .PFX file that contains some stuff in it from when they created the key using a utility called digital certificate manager on the AS400. I have found enough information to gather that I have to have an SSL properties file in my root application directory in IFS, and the job is looking at that properties file and it seems to have the correct parameters. What I am having a hard time with, is how you can have the certificate trust the application. I'm not sure if you need the .PFX file to exist in your IFS root directory of the application, or if you have to create a trust store/key store, or if you need anything at all besides the SSL properties file? I have found answers of yes to all questions, depending on who you ask. Some answers lead you down the path of doing exhaustive things to get to a certain point, only to have nothing happen. This is more of a vent than anything. I have almost come to the conclusion that this stuff is impossible. :)

For what it's worth, below is the code I'm using for connecting to the service using just HTTP. I have been looking for a simple step by step process to explain what is required for HTTPS handshakes to occur successfully on the AS400. I don't have enough information to know whether I need to get more from the client, or if I have enough to make it work on my own.

HttpClient m_HttpClient = null; 
PostMethod m_PostMthd = null;
SimpleHttpConnectionManager m_simpleHttpConMnger = new 
SimpleHttpConnectionManager();
int timeoutInMilliseconds = 10000;
m_PostMthd = new PostMethod(urlEndpoint);
m_HttpClient = new HttpClient(m_simpleHttpConMnger);
HttpConnectionManagerParams lhttpConMnger = 
m_simpleHttpConMnger.getParams();
lhttpConMnger.setConnectionTimeout(timeoutInMilliseconds);
lhttpConMnger.setSoTimeout(timeoutInMilliseconds);

m_PostMthd.setRequestHeader("SOAPAction",SOAPAction);
m_PostMthd.setRequestHeader("Content-Type","text/xml; charset=UTF-8");  


m_PostMthd.setRequestEntity(new StringRequestEntity(inputMsg));

int l_status = m_HttpClient.executeMethod(m_PostMthd);
System.out.println("EXECUTION STATUS : " + l_status+"\n");

InputStream is = m_PostMthd.getResponseBodyAsStream();
BufferedReader rd = new BufferedReader(new InputStreamReader(is));
String line;
StringBuilder response = new StringBuilder();
while ((line = rd.readLine()) != null)
    {
    response.append(line);
    response.append('\r');
    }

    rd.close();
    return response.toString();

Solution

  • I think there are two separate issues here: how do I use a PFX file in Java, and what am I supposed to do with it. The first one is fairly straight forward, the second one may require a bit of extra information, but I'll answer it as best I can.

    Handling PFX in Java

    PFX is a container file format that can contain a bunch of different kinds of data that relate to SSL/TLS and cryptography. Java has its own container format called a JKS KeyStore. Broadly speaking, these are equivalent ways to hold the same sort of data, and you just need to export from one into the other. It's easier to do this beforehand, than to try and do it at run time. Citing this answer:

    keytool -importkeystore \
            -srckeystore mypfxfile.pfx -srcstoretype pkcs12 \
            -destkeystore clientcert.jks -deststoretype JKS
    

    Keytool is included with the JDK. It'll ask for some passwords along the way. You can use the keytool -list -keystore file.jks command to show the contents of the JKS file.

    What am I supposed to do with this stuff?

    This depends on the situation, and here your question is a bit vague. In all cases, the purpose is authentication: the client needs to verify that it's talking to the correct server, and optionally, the server needs to verify that it's talking to the correct client. This involves three bits of information and a lot of math.

    • The private key and public key (along with some math, commonly the RSA algorithm) work to create a lock and key system. You keep the private key to yourself, and you share the public key with anyone who needs it. The way these are used in authentication is that the private key is used to mathematically make a claim that can be verified with the public key. The math is such that it's practically impossible to forge this claim; therefore, if the math checks out you can be sure that the person you're talking to holds the private key. This is digital signing in a nutshell.
    • A certificate embeds a public key and adds a bunch of information to it (a key is basically just a giant prime number, so it's not much use on its own). The information added consists of the identity of the key holder, and the identity of the issuer who vouches for the validity of the certificate, all of this sealed with digital signatures.

    Depending on the application, the PFX file you received may contain just a certificate, or it may contain a certificate and the corresponding private key (or both of these).

    The output of keytool -list will show any number of trustedCertEntry records, these are certificates for which no private key is available, and it may include a privateKeyEntry, which is a record for which both a certificate and its private key are present. Take note of the alias of any private key entry.

    Client Authenticates Server

    When an HTTPS connection is created, the server presents a certificate to the client, along with proof that the server holds the private key that corresponds to the public key embedded in the certificate. The client will have to decide whether it trusts the server's identity, or aborts the connection. It does so by mathematically checking the certificate presented by the server against a list of pre-shared, trusted certificates called the trust store (cacerts is a common filesystem name).

    Your OS'es and web browsers all ship with a default trust store; this mechanism is behind the padlock icon in your browser.

    In your case, it's likely that the endpoint you're connecting to uses a certificate that's not present in the default trust store, so you have to provide your own [trust store].

    Assuming Java on AS/400 works the same way as Java on Windows and Linux, you can (citing this answer) pass a Java system property called javax.net.ssl.trustStore that contains the filesystem path to the JKS keystore that contains the trusted signer's certificate.

    An alternative is to name the JKS file jssecacerts and put it in your jre/lib/security directory, but keep in mind that it now affects all java processes that run using that JVM installation in stead of just the one for which you're doing this work.

    Server Authenticates Client

    It's not used much on the web, but HTTPS has the option of doing two-way certificate verification. That means that after the client has authenticated the server in the way described above, the server now authenticates the client. It's the exact same process, but in the other direction.

    It's a bit more common in proprietary SOAP services, so it may be the case here. Your question didn't really make that clear to me.

    It means your application will have to present the server with a certificate and it will have to possess the private key that corresponds to the public key embedded in the certificate. Sadly, I've never had to do this in Java myself, so I don't know the exact commands you have to pass. What you need to look for is a way to tell your connection these things:

    • what JKS keystore file to use (+its password)
    • which key alias from that JKS to use (+its password)