Skip to content

Commit

Permalink
Add support for client authentication certificates
Browse files Browse the repository at this point in the history
2011-09-26  Lars Herschke  <[email protected]>

    * netx/net/sourceforge/jnlp/resources/Messages.properties: Add
    CVExportPasswordMessage, CVImportPasswordMessage and
    CVPasswordTitle.
    * netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java (initialize):
    Initialize SSLContext with the user's client certificates.
    * netx/net/sourceforge/jnlp/security/CertificateUtils.java
    (addPKCS12ToKeyStore, addPKCS12ToKeyStore, dumpPKCS12): New methods.
    * netx/net/sourceforge/jnlp/security/viewer/CertificatePane.java
    (getPasswords): New method.
    (ImportButtonListener.actionPerformed): Import client certificates
    in PKCS12 format.
    (ExportButtonListener.actionPerformed): Export client certificates
    in PKCS12 format.
  • Loading branch information
lherschi committed Sep 26, 2011
1 parent d7494fc commit e009f1b
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 5 deletions.
16 changes: 16 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
2011-09-26 Lars Herschke <[email protected]>

* netx/net/sourceforge/jnlp/resources/Messages.properties: Add
CVExportPasswordMessage, CVImportPasswordMessage and
CVPasswordTitle.
* netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java (initialize):
Initialize SSLContext with the user's client certificates.
* netx/net/sourceforge/jnlp/security/CertificateUtils.java
(addPKCS12ToKeyStore, addPKCS12ToKeyStore, dumpPKCS12): New methods.
* netx/net/sourceforge/jnlp/security/viewer/CertificatePane.java
(getPasswords): New method.
(ImportButtonListener.actionPerformed): Import client certificates
in PKCS12 format.
(ExportButtonListener.actionPerformed): Export client certificates
in PKCS12 format.

2011-09-23 Omair Majid <[email protected]>

RH738814: Access denied at ssl handshake
Expand Down
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Common
- PR789: typo in jrunscript.sh
- RH734081: Javaws cannot use proxy settings from Firefox
- RH738814: Access denied at ssl handshake
- Support for authenticating using client certificates

New in release 1.1 (2011-XX-XX):
* Security updates
Expand Down
3 changes: 3 additions & 0 deletions netx/net/sourceforge/jnlp/resources/Messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,12 @@ CVCertificateViewer=Certificates
CVCertificateType=Certificate Type
CVDetails=Details
CVExport=Export
CVExportPasswordMessage=Enter password to protect key file:
CVImport=Import
CVImportPasswordMessage=Enter password to access file:
CVIssuedBy=Issued By
CVIssuedTo=Issued To
CVPasswordTitle=Authentication Required
CVRemove=Remove
CVRemoveConfirmMessage=Are you sure you want to remove the selected certificate?
CVRemoveConfirmTitle=Confirmation - Remove Certificate?
Expand Down
6 changes: 5 additions & 1 deletion netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import javax.jnlp.*;
import javax.naming.ConfigurationException;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
Expand Down Expand Up @@ -225,8 +226,11 @@ public static void initialize(boolean isApplication) throws IllegalStateExceptio
try {
SSLSocketFactory sslSocketFactory;
SSLContext context = SSLContext.getInstance("SSL");
KeyStore ks = KeyStores.getKeyStore(KeyStores.Level.USER, KeyStores.Type.CLIENT_CERTS);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, KeyStores.getPassword());
TrustManager[] trust = new TrustManager[] { VariableX509TrustManager.getInstance() };
context.init(null, trust, null);
context.init(kmf.getKeyManagers(), trust, null);
sslSocketFactory = context.getSocketFactory();

HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory);
Expand Down
49 changes: 49 additions & 0 deletions netx/net/sourceforge/jnlp/security/CertificateUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,15 @@
package net.sourceforge.jnlp.security;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
Expand Down Expand Up @@ -117,6 +120,41 @@ public static final void addToKeyStore(X509Certificate cert, KeyStore ks)
ks.setCertificateEntry(alias, cert);
}

public static void addPKCS12ToKeyStore(File file, KeyStore ks, char[] password)
throws Exception {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(bis, password);

Enumeration<String> aliasList = keyStore.aliases();

while (aliasList.hasMoreElements()) {
String alias = aliasList.nextElement();
Certificate[] certChain = keyStore.getCertificateChain(alias);
Key key = keyStore.getKey(alias, password);
addPKCS12ToKeyStore(certChain, key, ks);
}
}

public static void addPKCS12ToKeyStore(Certificate[] certChain, Key key, KeyStore ks)
throws KeyStoreException {
String alias = null;

// does this certificate already exist?
alias = ks.getCertificateAlias(certChain[0]);
if (alias != null) {
return;
}

// create a unique alias for this new certificate
Random random = new Random();
do {
alias = new BigInteger(20, random).toString();
} while (ks.getCertificate(alias) != null);

ks.setKeyEntry(alias, key, KeyStores.getPassword(), certChain);
}

/**
* Checks whether an X509Certificate is already in one of the keystores
* @param c the certificate
Expand Down Expand Up @@ -177,4 +215,15 @@ public static void dump(Certificate cert, PrintStream out) throws IOException,
encoder.encodeBuffer(cert.getEncoded(), out);
out.println(X509Factory.END_CERT);
}

public static void dumpPKCS12(String alias, File file, KeyStore ks, char[] password)
throws Exception {
Certificate[] certChain = ks.getCertificateChain(alias);
Key key = ks.getKey(alias, KeyStores.getPassword());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(null, null);
keyStore.setKeyEntry(alias, key, password, certChain);
keyStore.store(bos, password);
}
}
38 changes: 34 additions & 4 deletions netx/net/sourceforge/jnlp/security/viewer/CertificatePane.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
Expand Down Expand Up @@ -100,6 +101,7 @@ public class CertificatePane extends JPanel {
new CertificateType(KeyStores.Type.JSSE_CA_CERTS),
new CertificateType(KeyStores.Type.CERTS),
new CertificateType(KeyStores.Type.JSSE_CERTS),
new CertificateType(KeyStores.Type.CLIENT_CERTS)
};

JTabbedPane tabbedPane;
Expand Down Expand Up @@ -301,6 +303,18 @@ public void focusOnDefaultButton() {
}
}

private char[] getPassword(final String label) {
JPasswordField jpf = new JPasswordField();
int result = JOptionPane.showConfirmDialog(parent,
new Object[]{label, jpf}, R("CVPasswordTitle"),
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.INFORMATION_MESSAGE);
if (result == JOptionPane.OK_OPTION)
return jpf.getPassword();
else
return null;
}

/** Allows storing KeyStores.Types in a JComponent */
private static class CertificateType {
private final KeyStores.Type type;
Expand Down Expand Up @@ -364,7 +378,17 @@ public void actionPerformed(ActionEvent e) {
if (returnVal == JFileChooser.APPROVE_OPTION) {
try {
KeyStore ks = keyStore;
CertificateUtils.addToKeyStore(chooser.getSelectedFile(), ks);
if (currentKeyStoreType == KeyStores.Type.CLIENT_CERTS) {
char[] password = getPassword(R("CVImportPasswordMessage"));
if (password != null) {
CertificateUtils.addPKCS12ToKeyStore(
chooser.getSelectedFile(), ks, password);
} else {
return;
}
} else {
CertificateUtils.addToKeyStore(chooser.getSelectedFile(), ks);
}
File keyStoreFile = new File(KeyStores
.getKeyStoreLocation(currentKeyStoreLevel, currentKeyStoreType));
if (!keyStoreFile.isFile()) {
Expand Down Expand Up @@ -408,9 +432,15 @@ public void actionPerformed(ActionEvent e) {
String alias = keyStore.getCertificateAlias(certs
.get(selectedRow));
if (alias != null) {
Certificate c = keyStore.getCertificate(alias);
PrintStream ps = new PrintStream(chooser.getSelectedFile().getAbsolutePath());
CertificateUtils.dump(c, ps);
if (currentKeyStoreType == KeyStores.Type.CLIENT_CERTS) {
char[] password = getPassword(R("CVExportPasswordMessage"));
if (password != null)
CertificateUtils.dumpPKCS12(alias, chooser.getSelectedFile(), keyStore, password);
} else {
Certificate c = keyStore.getCertificate(alias);
PrintStream ps = new PrintStream(chooser.getSelectedFile().getAbsolutePath());
CertificateUtils.dump(c, ps);
}
repopulateTables();
}
}
Expand Down

0 comments on commit e009f1b

Please sign in to comment.