001: package pygmy.core;
002:
003: import javax.net.ssl.*;
004: import java.io.File;
005: import java.io.FileInputStream;
006: import java.io.IOException;
007: import java.io.InputStream;
008: import java.net.Socket;
009: import java.net.ServerSocket;
010: import java.security.GeneralSecurityException;
011: import java.security.KeyStore;
012: import java.security.Principal;
013: import java.security.PrivateKey;
014: import java.security.cert.X509Certificate;
015: import java.util.logging.Logger;
016: import java.util.logging.Level;
017:
018: /**
019: * This EndPoint provides SSL sockets for http protocol. This extends the ServerSocket to create SSLSockets for http.
020: *
021: * <table class="inner">
022: * <tr class="header"><td>Parameter Name</td><td>Explanation</td><td>Default Value</td><td>Required</td></tr>
023: * <tr class="row"><td>keystore</td><td>The keystore of the certificates used by this socket. This contains both the certificate used to authenticate the server as well as authenticate clients.</td><td>None</td><td>Yes</td></tr>
024: * <tr class="altrow"><td>storepass</td><td>Password for the keystore.</td><td>None</td><td>Yes</td></tr>
025: * <tr class="row"><td>keypass</td><td>Password for the key with the given alias.</td><td>Defaults to storepass</td><td>No, but its a good idea</td></tr>
026: * <tr class="altrow"><td>alias</td><td>The name of the certificate in the keystore used for server authentication.</td><td>sslkey</td><td>No</td></tr>
027: * <tr class="row"><td>ciphers</td><td>A comma seperated list of cipher suites to use to encrypt the SSL socket.</td><td></td><td>No</td></tr>
028: * <tr class="atlrow"><td>protocols</td><td>A comma seperated list of protocols to use in negotiating the SSL socket.</td><td></td><td>No</td></tr>
029: * <tr class="row"><td>clientauth</td><td>Setting this to true will require clients authenticate to the server.</td><td>false</td><td>No</td></tr>
030: * </table>
031: */
032: public class SSLServerSocketEndPoint extends ServerSocketEndPoint {
033:
034: private static final Logger log = Logger
035: .getLogger(ServerSocketEndPoint.class.getName());
036:
037: private static final ConfigOption KEYSTORE_OPTION = new ConfigOption(
038: "keystore", true, "The keystore used by the SSL server.");
039: private static final ConfigOption STOREPASS_OPTION = new ConfigOption(
040: "storepass", true, "The keystore password.");
041: private static final ConfigOption KEYPASS_OPTION = new ConfigOption(
042: "keypass", false,
043: "The password for the key in the keystore.");
044: private static final ConfigOption ALIAS_OPTION = new ConfigOption(
045: "alias", "sslkey",
046: "The alias to the key used by the SSL server.");
047: private static final ConfigOption CIPHERS_OPTION = new ConfigOption(
048: "ciphers", false,
049: "Comma seperated list of ciphers to use for the SSL server.");
050: private static final ConfigOption PROTOCOLS_OPTION = new ConfigOption(
051: "protocols", false,
052: "Comma seperated list of protocols for SSL server.");
053: private static final ConfigOption CLIENT_AUTH_OPTION = new ConfigOption(
054: "clientauth", "false",
055: "Require client authentication during SSL handshake.");
056:
057: public void initialize(String name, Server server)
058: throws IOException {
059: super .initialize(name, server);
060: try {
061: File keystoreFile = new File(KEYSTORE_OPTION.getProperty(
062: server, endpointName));
063: String storepass = STOREPASS_OPTION.getProperty(server,
064: endpointName);
065: String keypass = KEYPASS_OPTION.getProperty(server,
066: endpointName);
067: keypass = keypass == null ? storepass : keypass;
068: KeyStore keystore = loadKeystoreFromFile(keystoreFile,
069: storepass.toCharArray());
070:
071: SSLContext context = SSLContext.getInstance("SSL");
072: context.init(getKeyManagers(keystore,
073: keypass.toCharArray(), ALIAS_OPTION.getProperty(
074: server, endpointName)),
075: getTrustManagers(keystore), null);
076: factory = context.getServerSocketFactory();
077: } catch (GeneralSecurityException e) {
078: log.log(Level.SEVERE,
079: "Security Exception while initializing.", e);
080: throw (IOException) new IOException().initCause(e);
081: }
082: }
083:
084: protected String getProtocol() {
085: return "https";
086: }
087:
088: protected ServerSocket createSocket(int port) throws IOException {
089: ServerSocket serverSocket = super .createSocket(port);
090: String cipherSuites = CIPHERS_OPTION.getProperty(server,
091: getName());
092: if (cipherSuites != null) {
093: ((SSLServerSocket) serverSocket)
094: .setEnabledCipherSuites(cipherSuites.split(","));
095: }
096:
097: String protocols = PROTOCOLS_OPTION.getProperty(server,
098: getName());
099: if (protocols != null) {
100: ((SSLServerSocket) serverSocket)
101: .setEnabledProtocols(protocols.split(","));
102: }
103:
104: boolean clientAuth = CLIENT_AUTH_OPTION.getBoolean(server,
105: getName()).booleanValue();
106: if (clientAuth) {
107: ((SSLServerSocket) serverSocket).setNeedClientAuth(true);
108: }
109: return serverSocket;
110: }
111:
112: private KeyStore loadKeystoreFromFile(File file, char[] password)
113: throws IOException, GeneralSecurityException {
114: KeyStore keystore = KeyStore.getInstance("JKS");
115: InputStream stream = new FileInputStream(file);
116: keystore.load(stream, password);
117: stream.close();
118:
119: return keystore;
120: }
121:
122: private TrustManager[] getTrustManagers(KeyStore keystore)
123: throws GeneralSecurityException {
124: TrustManagerFactory factory = TrustManagerFactory
125: .getInstance("SunX509");
126: factory.init(keystore);
127:
128: return factory.getTrustManagers();
129: }
130:
131: private KeyManager[] getKeyManagers(KeyStore keystore, char[] pwd,
132: String alias) throws GeneralSecurityException {
133: KeyManagerFactory factory = KeyManagerFactory
134: .getInstance("SunX509");
135: factory.init(keystore, pwd);
136: KeyManager[] kms = factory.getKeyManagers();
137: if (alias != null) {
138: for (int i = 0; i < kms.length; i++) {
139: if (kms[i] instanceof X509KeyManager)
140: kms[i] = new AliasForcingKeyManager(
141: (X509KeyManager) kms[i], alias);
142: }
143: }
144:
145: return kms;
146: }
147:
148: private class AliasForcingKeyManager implements X509KeyManager {
149: X509KeyManager baseKM = null;
150: String alias = null;
151:
152: public AliasForcingKeyManager(X509KeyManager keyManager,
153: String alias) {
154: baseKM = keyManager;
155: this .alias = alias;
156: }
157:
158: public String chooseClientAlias(String[] keyType,
159: Principal[] issuers, Socket socket) {
160: return baseKM.chooseClientAlias(keyType, issuers, socket);
161: }
162:
163: public String chooseServerAlias(String keyType,
164: Principal[] issuers, Socket socket) {
165: String[] validAliases = baseKM.getServerAliases(keyType,
166: issuers);
167: if (validAliases != null) {
168: for (int j = 0; j < validAliases.length; j++) {
169: if (validAliases[j].equals(alias))
170: return alias;
171: }
172: }
173: return baseKM.chooseServerAlias(keyType, issuers, socket); // use default if we can't find the alias.
174: }
175:
176: public X509Certificate[] getCertificateChain(String alias) {
177: return baseKM.getCertificateChain(alias);
178: }
179:
180: public String[] getClientAliases(String keyType,
181: Principal[] issuers) {
182: return baseKM.getClientAliases(keyType, issuers);
183: }
184:
185: public PrivateKey getPrivateKey(String alias) {
186: return baseKM.getPrivateKey(alias);
187: }
188:
189: public String[] getServerAliases(String keyType,
190: Principal[] issuers) {
191: return baseKM.getServerAliases(keyType, issuers);
192: }
193: }
194: }
|