2
\$\begingroup\$

I am building a JAX-WS client with a GUI that connects to a server by accepting self-signed server certificates (when it uses HTTPS). The accepting of the certificates is not automatic, it is similar to what happens in any browser when you try to connect to a website that is not trusted. That is, usually a security pop-up appears displaying the certificate details and the user can either accept or reject the certificate.

Https Connection Endpoint function:

  private void buildControlEndpoint() {

    Map<String, Object> context = ((BindingProvider)control).getRequestContext();
    URL address = getConnectionEndpoint();
    if (address != null && Settings.getSettings().isUsingHttpConnection()) {
        context.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,address.toString());
    }
    else{
        address = getSecureConnectionEndpoint(); //returns example: https://19.18.88.131:5914
        SSLSocket socket;

        try {
            TrustManager[] trustCerts = new TrustManager[] { new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
                public void checkServerTrusted(X509Certificate[] certs,
                        String authType) throws CertificateException {
                    return;
                }
                public void checkClientTrusted(X509Certificate[] certs,
                        String authType) throws CertificateException {
                    return;
                }
            } };
            HttpsURLConnection conn = (HttpsURLConnection) address.openConnection();
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null, trustCerts, null);

            conn.setSSLSocketFactory(sc.getSocketFactory());
            SocketFactory factory = sc.getSocketFactory();
            socket = (SSLSocket) factory.createSocket(
                     getHostName(), getSecureConnectionEndpoint()
                    .getPort());

            socket.startHandshake();
            setCerts(socket.getSession().getPeerCertificates());

            X509Certificate[] peerCertificates = (X509Certificate[]) socket
                    .getSession().getPeerCertificates();
            X509Certificate serverCert = peerCertificates[0];
            System.out.println("Server Certificate is:   "+serverCert);
            CertificateController store = new CertificateController();

            String alias = getHostName() + "-" + getPort();

            if(store.CertificateComparer(alias, serverCert) == false){
                if(store.LoadCertificate(serverCert) == true){
                context.put(JAXWSProperties.SSL_SOCKET_FACTORY, sc.getSocketFactory());
                store.WriteTrustStore(alias, serverCert);
                }
                else{
                    context.remove(JAXWSProperties.SSL_SOCKET_FACTORY, sc.getSocketFactory());
                }
            }
        }
        catch (IOException | KeyManagementException | NoSuchAlgorithmException  e) {
            e.printStackTrace();
        }
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier()
        {
            public boolean verify(String hostname, SSLSession session)
            {
                return true;
            }
        });
        context.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,address.toString());

    }
} 

Certificate Controller class:

public class CertificateController {

private X509Certificate c;
private KeyStore trustStore  = null;
private File keystoreFile = null;
char[] password = "changeit".toCharArray();

public void OpenCertificate(File file) throws Exception{
    InputStream targetStream = new FileInputStream(file);
    CertificateFactory cf = CertificateFactory.getInstance("X509");
    c = (X509Certificate) cf.generateCertificate(targetStream);
}

public boolean CertificateComparer(String alias, X509Certificate c){
    try {
        OpenTrustStore();
        X509Certificate storecert = (X509Certificate) trustStore.getCertificate(alias);
        if(storecert == c){
            return true;
        }
        else{
            return false;
        }
    } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) {
        return false;
    }
}

public void WriteTrustStore(String alias, X509Certificate c){
    try {
        OpenTrustStore();
        trustStore.setCertificateEntry(alias, c);
        FileOutputStream out = new FileOutputStream(keystoreFile);
        trustStore.store(out, password);
        out.close();
    } catch (KeyStoreException | NoSuchAlgorithmException
            | CertificateException | IOException e) {
        e.printStackTrace();
    }
}

public boolean LoadCertificate(X509Certificate c){
    CertificatePopup PopUp = new CertificatePopup();
    if(PopUp.DisplayCertficate((X509Certificate) c) == true){
        return true;
    }
    else{
        PopUp.NeedCertificate();
        return false;
    }
}

private KeyStore OpenTrustStore() {
    char SEP = File.separatorChar;
    keystoreFile = new File(System.getProperty("java.home") + SEP + "lib"
            + SEP + "security" + SEP + "cacerts");
    try {
        setTrustStore(trustStore);
        FileInputStream in = new FileInputStream(keystoreFile);
        trustStore.load(in, password);
        in.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (CertificateException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return trustStore;
}

public KeyStore getTrustStore() {
    return trustStore;
}
public void setTrustStore(KeyStore trustStore) {
    try {
        this.trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
    } catch (KeyStoreException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

public X509Certificate getC() {
    return c;
}

public void setC(X509Certificate c) {
    this.c = c;
}
}

Pop-up view class:

public class CertificatePopup {

public boolean DisplayCertficate(java.security.cert.X509Certificate cert){
    final JPanel panel = new JPanel();
    String message =  " Do you want to accept this certificate? \n"
            + "\n Certificate for: " + cert.getSubjectDN()
            + "\n Certificate issued by: "
            + cert.getIssuerDN()
            + "\n The certificate is valid from: "
            + cert.getNotBefore() + "\n Certificate SN#: "
            + cert.getSerialNumber() + "\n Generated with: "
            + cert.getSigAlgName();

    String title = "Security Alert";
    String[] options = { "yes", "no" };
    int confirm = JOptionPane.showOptionDialog(panel, message, title, JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
            null, options, options[0]);

    if (confirm == JOptionPane.YES_OPTION){
        return true;
    } else if (confirm == JOptionPane.NO_OPTION) {
        return false;
    }
    else{
        return false;
    }

}

public void NeedCertificate(){
    final JPanel panel = new JPanel();
    JOptionPane.showMessageDialog(panel, "GUI needs a valid certificate to use HTTPS!","ERROR!",JOptionPane.ERROR_MESSAGE);
}
}

And somewhere in the main class I am calling:

buildControlEndpoint();

This code works perfectly fine, although I have my doubts about a few things:

  1. Obviously security is the most important thing here! Although I am accepting the certificates manually, how risky is this from Security provider point of view? I know I am liable to man in the middle attacks, but don't browsers follow a similar strategy? At end of the day the user is liable.

  2. The code structure! I haven't done much of GUI programming before, so I am worried that my design pattern might be not so intelligent. Although I have tried to follow MVC, I not confident about it. I haven't used any ActionListeners because I know it is not necessary when you have just JOptionPane. Is there something I could do better in designing the code?

  3. Just discovered a new bug, it occurs when I am trying to load certificates from multiple servers one after another.JOptionPane hangs as multiple windows open in quick succession. I tried encapsulating it in EventQueue.invokeLater, still doesn't help. Is there any known workaround for this?

\$\endgroup\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.