0001: /**
0002: * Copyright (c) 2000/2001 Thomas Kopp
0003: * All rights reserved.
0004: *
0005: * Redistribution and use in source and binary forms, with or without
0006: * modification, are permitted provided that the following conditions
0007: * are met:
0008: * 1. Redistributions of source code must retain the above copyright
0009: * notice, this list of conditions and the following disclaimer.
0010: * 2. Redistributions in binary form must reproduce the above copyright
0011: * notice, this list of conditions and the following disclaimer in the
0012: * documentation and/or other materials provided with the distribution.
0013: *
0014: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
0015: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0016: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0017: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
0018: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0019: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
0020: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0021: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
0022: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
0023: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0024: * SUCH DAMAGE.
0025: */package org.w3c.jigsaw.https.socket;
0026:
0027: import java.io.BufferedInputStream;
0028: import java.io.FileInputStream;
0029: import java.io.IOException;
0030:
0031: import java.lang.reflect.Constructor; /* import java.lang.reflect.InvocationHandler; */
0032: import java.lang.reflect.InvocationTargetException;
0033: import java.lang.reflect.Method; /* import java.lang.reflect.Proxy; */
0034:
0035: import java.net.InetAddress;
0036: import java.net.ServerSocket;
0037: import java.net.Socket;
0038:
0039: import java.security.InvalidAlgorithmParameterException;
0040: import java.security.KeyManagementException;
0041: import java.security.KeyStore;
0042: import java.security.KeyStoreException;
0043: import java.security.NoSuchAlgorithmException;
0044: import java.security.Provider;
0045: import java.security.SecureRandom;
0046: import java.security.Security;
0047: import java.security.UnrecoverableKeyException;
0048:
0049: import java.security.cert.CertificateException;
0050:
0051: import java.security.spec.InvalidParameterSpecException;
0052:
0053: import javax.net.ServerSocketFactory;
0054:
0055: import javax.net.ssl.SSLKeyException;
0056: import javax.net.ssl.SSLProtocolException;
0057: import javax.net.ssl.SSLServerSocket;
0058: import javax.net.ssl.SSLServerSocketFactory;
0059:
0060: import org.w3c.jigsaw.http.httpd;
0061: import org.w3c.jigsaw.http.socket.SocketClient;
0062: import org.w3c.jigsaw.http.socket.SocketClientFactory;
0063: import org.w3c.jigsaw.http.socket.SocketClientState;
0064:
0065: import org.w3c.jigsaw.https.SSLAdapter;
0066:
0067: import org.w3c.util.ObservableProperties;
0068:
0069: /**
0070: * @author Thomas Kopp, Dialogika GmbH
0071: * @version 1.1, 27 December 2000, 6 February 2004
0072: *
0073: * This class extends a Jigsaw SocketClientFactory designed for the
0074: * http protocol
0075: * in order to supply a SocketClientFactory for the https protocol
0076: * in accordance with the JSSE API.
0077: *
0078: * Three legal tricks are applied for working around if required:
0079: * Proxy classes are used for addressing multiple inheritance and
0080: * non-official api.
0081: * Non-static access via introspection provides for addressing static api.
0082: * The java.lang.Object type is used for mapping non-official types.
0083: */
0084: public class SSLSocketClientFactory extends SocketClientFactory {
0085:
0086: /**
0087: * The used api-part of javax.net.ssl.KeyManagerFactory.
0088: * A static interface KeyManagerFactory can be used under a
0089: * real proxy approach.
0090: */
0091: private static final class KeyManagerFactory extends Delegator {
0092:
0093: /**
0094: * Creates the specified pseudo-proxy front end.
0095: *
0096: * @param target the target object in use
0097: */
0098: private KeyManagerFactory(Object target) {
0099: super (target);
0100: }
0101:
0102: /**
0103: * Supplies the default factory algorithm.
0104: *
0105: * @return the default algorithm
0106: */
0107: public/* static */String getDefaultAlgorithm() {
0108: try {
0109: return (String) invoke("getDefaultAlgorithm", null,
0110: null);
0111: } catch (Exception ex) {
0112: RuntimeException rex = new RuntimeException(ex
0113: .toString());
0114: SSLAdapter.fillInStackTrace(rex, ex);
0115: throw rex;
0116: }
0117: }
0118:
0119: /**
0120: * Generates a key manager factory.
0121: *
0122: * @param algorithm the name of the factory algorithm
0123: * @return a key manager factory instance
0124: * @throws NoSuchAlgorithmException if the factory algorithm is
0125: * unavailable
0126: */
0127: public/* static */Object getInstance(String algorithm)
0128: throws NoSuchAlgorithmException {
0129: try {
0130: return invoke("getInstance",
0131: new Class[] { String.class },
0132: new Object[] { algorithm });
0133: } catch (NoSuchAlgorithmException ex) {
0134: throw ex;
0135: } catch (Exception ex) {
0136: RuntimeException rex = new RuntimeException(ex
0137: .toString());
0138: SSLAdapter.fillInStackTrace(rex, ex);
0139: throw rex;
0140: }
0141: }
0142:
0143: /**
0144: * Initializes this factory.
0145: *
0146: * @param ks the underlying keystore
0147: * @param password the key access password in use
0148: * @throws KeyStoreException if initialization fails
0149: * @throws NoSuchAlgorithmException if the specified algorithm
0150: * is unavailable
0151: * @throws UnrecoverableKeyException if the key in question
0152: * cannot be recovered
0153: */
0154: public void init(KeyStore ks, char[] password)
0155: throws KeyStoreException, NoSuchAlgorithmException,
0156: UnrecoverableKeyException {
0157: try {
0158: invoke("init", new Class[] { KeyStore.class,
0159: char[].class }, new Object[] { ks, password });
0160: } catch (KeyStoreException ex) {
0161: throw ex;
0162: } catch (NoSuchAlgorithmException ex) {
0163: throw ex;
0164: } catch (UnrecoverableKeyException ex) {
0165: throw ex;
0166: } catch (Exception ex) {
0167: RuntimeException rex = new RuntimeException(ex
0168: .toString());
0169: SSLAdapter.fillInStackTrace(rex, ex);
0170: throw rex;
0171: }
0172: }
0173:
0174: /**
0175: * Alternatively initializes this factory.
0176: *
0177: * @param parameters the manager factory parameters
0178: * (unavailable prior to JDK 1.4)
0179: * @throws InvalidAlgorithmParameterException if initialization fails
0180: */
0181: public void init(Object parameters)
0182: throws InvalidAlgorithmParameterException {
0183: try {
0184: invoke("init", new Class[] { Object.class },
0185: new Object[] { parameters });
0186: } catch (InvalidAlgorithmParameterException ex) {
0187: throw ex;
0188: } catch (Exception ex) {
0189: RuntimeException rex = new RuntimeException(ex
0190: .toString());
0191: SSLAdapter.fillInStackTrace(rex, ex);
0192: throw rex;
0193: }
0194: }
0195:
0196: /**
0197: * Supplies the available key managers of this factory.
0198: *
0199: * @return the available key managers
0200: */
0201: public Object getKeyManagers() {
0202: try {
0203: return invoke("getKeyManagers", null, null);
0204: } catch (Exception ex) {
0205: RuntimeException rex = new RuntimeException(ex
0206: .toString());
0207: SSLAdapter.fillInStackTrace(rex, ex);
0208: throw rex;
0209: }
0210: }
0211: }
0212:
0213: /**
0214: * The used api-part of javax.net.ssl.TrustManagerFactory.
0215: * A static interface TrustManagerFactory can be used under a
0216: * real proxy approach.
0217: */
0218: private static final class TrustManagerFactory extends Delegator {
0219:
0220: /**
0221: * Creates the specified pseudo-proxy front end.
0222: *
0223: * @param target the target object in use
0224: */
0225: private TrustManagerFactory(Object target) {
0226: super (target);
0227: }
0228:
0229: /**
0230: * Supplies the default factory algorithm.
0231: *
0232: * @return the default algorithm
0233: */
0234: public/* static */String getDefaultAlgorithm() {
0235: try {
0236: return (String) invoke("getDefaultAlgorithm", null,
0237: null);
0238: } catch (Exception ex) {
0239: RuntimeException rex = new RuntimeException(ex
0240: .toString());
0241: SSLAdapter.fillInStackTrace(rex, ex);
0242: throw rex;
0243: }
0244: }
0245:
0246: /**
0247: * Generates a trust manager factory.
0248: *
0249: * @param algorithm the name of the factory algorithm
0250: * @return a trust manager factory instance
0251: * @throws NoSuchAlgorithmException if the factory algorithm
0252: * is unavailable
0253: */
0254: public/* static */Object getInstance(String algorithm)
0255: throws NoSuchAlgorithmException {
0256: try {
0257: return invoke("getInstance",
0258: new Class[] { String.class },
0259: new Object[] { algorithm });
0260: } catch (NoSuchAlgorithmException ex) {
0261: throw ex;
0262: } catch (Exception ex) {
0263: RuntimeException rex = new RuntimeException(ex
0264: .toString());
0265: SSLAdapter.fillInStackTrace(rex, ex);
0266: throw rex;
0267: }
0268: }
0269:
0270: /**
0271: * Initializes this factory.
0272: *
0273: * @param ks the underlying keystore
0274: * @throws KeyStoreException if initialization fails
0275: */
0276: public void init(KeyStore ks) throws KeyStoreException {
0277: try {
0278: invoke("init", new Class[] { KeyStore.class },
0279: new Object[] { ks });
0280: } catch (KeyStoreException ex) {
0281: throw ex;
0282: } catch (Exception ex) {
0283: RuntimeException rex = new RuntimeException(ex
0284: .toString());
0285: SSLAdapter.fillInStackTrace(rex, ex);
0286: throw rex;
0287: }
0288: }
0289:
0290: /**
0291: * Alternatively initializes this factory.
0292: *
0293: * @param parameters the manager factory parameters
0294: * (unavailable prior to JDK 1.4)
0295: * @throws InvalidAlgorithmParameterException if initialization fails
0296: */
0297: public void init(Object parameters)
0298: throws InvalidAlgorithmParameterException {
0299: try {
0300: invoke("init", new Class[] { Object.class },
0301: new Object[] { parameters });
0302: } catch (InvalidAlgorithmParameterException ex) {
0303: throw ex;
0304: } catch (Exception ex) {
0305: RuntimeException rex = new RuntimeException(ex
0306: .toString());
0307: SSLAdapter.fillInStackTrace(rex, ex);
0308: throw rex;
0309: }
0310: }
0311:
0312: /**
0313: * Supplies the available trust managers of this factory.
0314: *
0315: * @return the available truat managers
0316: */
0317: public Object getTrustManagers() {
0318: try {
0319: return invoke("getTrustManagers", null, null);
0320: } catch (Exception ex) {
0321: RuntimeException rex = new RuntimeException(ex
0322: .toString());
0323: SSLAdapter.fillInStackTrace(rex, ex);
0324: throw rex;
0325: }
0326: }
0327: }
0328:
0329: /**
0330: * The used api-part of javxx.net.ssl.SSLContext.
0331: * A static interface SSLContext can be used under a real proxy approach.
0332: */
0333: private static final class SSLContext extends Delegator {
0334:
0335: /**
0336: * Creates the specified pseudo-proxy front end.
0337: *
0338: * @param target the target object in use
0339: */
0340: private SSLContext(Object target) {
0341: super (target);
0342: }
0343:
0344: /**
0345: * Generates an ssl context, which implements the specified
0346: * secure socket protocol.
0347: *
0348: * @param protcol the name of protocol implementation
0349: * @return an ssl context instance
0350: * @throws NoSuchAlgorithmException if the specified implementation
0351: * is not available
0352: */
0353: public/* static */Object getInstance(String protocol)
0354: throws NoSuchAlgorithmException {
0355: try {
0356: return invoke("getInstance",
0357: new Class[] { String.class },
0358: new Object[] { protocol });
0359: } catch (NoSuchAlgorithmException ex) {
0360: throw ex;
0361: } catch (Exception ex) {
0362: RuntimeException rex = new RuntimeException(ex
0363: .toString());
0364: SSLAdapter.fillInStackTrace(rex, ex);
0365: throw rex;
0366: }
0367: }
0368:
0369: /**
0370: * Initializes this context.
0371: *
0372: * @param km the key manager array used
0373: * @param tm the trust manager array used
0374: * @param random the secure random used for initializing seed
0375: * @throws KeyManagementException if initialization fails
0376: */
0377: public void init(Object km, Object tm, SecureRandom random)
0378: throws KeyManagementException {
0379: try {
0380: invoke("init", new Class[] { Object.class,
0381: Object.class, SecureRandom.class },
0382: new Object[] { km, tm, random });
0383: } catch (KeyManagementException ex) {
0384: throw ex;
0385: } catch (Exception ex) {
0386: RuntimeException rex = new RuntimeException(ex
0387: .toString());
0388: SSLAdapter.fillInStackTrace(rex, ex);
0389: throw rex;
0390: }
0391: }
0392:
0393: /**
0394: * Supplies an ssl server socket factory.
0395: *
0396: * @return a server socket factory instance.
0397: */
0398: public SSLServerSocketFactory getServerSocketFactory() {
0399: try {
0400: return (SSLServerSocketFactory) invoke(
0401: "getServerSocketFactory", null, null);
0402: } catch (Exception ex) {
0403: RuntimeException rex = new RuntimeException(ex
0404: .toString());
0405: SSLAdapter.fillInStackTrace(rex, ex);
0406: throw rex;
0407: }
0408: }
0409: }
0410:
0411: /**
0412: * The generic manager factory paremeters bridge.
0413: * A static interface ManagerFactoryParametersFactory can be used under
0414: * a real proxy approach.
0415: */
0416: private static final class ManagerFactoryParametersFactory extends
0417: Delegator {
0418:
0419: /**
0420: * Creates the specified pseudo-proxy front end.
0421: *
0422: * @param target the target object in use
0423: */
0424: private ManagerFactoryParametersFactory(Object target) {
0425: super (target);
0426: }
0427:
0428: /**
0429: * Generates a manager factory parameters instance.
0430: *
0431: * @param path the generic path argument for a parameters instance
0432: * @param password the password for a parameters instance
0433: * @return a manager factory parameters instance
0434: * @throws InvalidAlgorithmParameterException if the specified
0435: * arguments are not suitable
0436: * @throws NoSuchMethodException if the specified method
0437: * is not available
0438: */
0439: public/* static */Object getInstance(String path,
0440: String password)
0441: throws InvalidAlgorithmParameterException,
0442: NoSuchMethodException {
0443: try {
0444: return invoke("getInstance", new Class[] {
0445: String.class, String.class }, new Object[] {
0446: path, password });
0447: } catch (InvalidAlgorithmParameterException ex) {
0448: throw ex;
0449: } catch (NoSuchMethodException ex) {
0450: throw ex;
0451: } catch (Exception ex) {
0452: RuntimeException rex = new RuntimeException(ex
0453: .toString());
0454: SSLAdapter.fillInStackTrace(rex, ex);
0455: throw rex;
0456: }
0457: }
0458:
0459: /**
0460: * Generates a manager factory parameters instance.
0461: *
0462: * @param path the generic path argument for a parameters instance
0463: * @return a manager factory parameters instance
0464: * @throws InvalidAlgorithmParameterException if the specified
0465: * arguments are not suitable
0466: * @throws NoSuchMethodException if the specified method is not
0467: * available
0468: */
0469: public/* static */Object getInstance(String path)
0470: throws InvalidAlgorithmParameterException,
0471: NoSuchMethodException {
0472: try {
0473: return invoke("getInstance",
0474: new Class[] { String.class },
0475: new Object[] { path });
0476: } catch (InvalidAlgorithmParameterException ex) {
0477: throw ex;
0478: } catch (NoSuchMethodException ex) {
0479: throw ex;
0480: } catch (Exception ex) {
0481: RuntimeException rex = new RuntimeException(ex
0482: .toString());
0483: SSLAdapter.fillInStackTrace(rex, ex);
0484: throw rex;
0485: }
0486: }
0487:
0488: /**
0489: * Generates a manager factory parameters instance.
0490: *
0491: * @return a manager factory parameters instance
0492: * @throws InvalidAlgorithmParameterException if the specified
0493: * arguments are not suitable
0494: * @throws NoSuchMethodException if the specified method is not
0495: * available
0496: */
0497: public/* static */Object getInstance()
0498: throws InvalidAlgorithmParameterException,
0499: NoSuchMethodException {
0500: try {
0501: return invoke("getInstance", null, null);
0502: } catch (InvalidAlgorithmParameterException ex) {
0503: throw ex;
0504: } catch (NoSuchMethodException ex) {
0505: throw ex;
0506: } catch (Exception ex) {
0507: RuntimeException rex = new RuntimeException(ex
0508: .toString());
0509: SSLAdapter.fillInStackTrace(rex, ex);
0510: throw rex;
0511: }
0512: }
0513: }
0514:
0515: /**
0516: * The standard delegation pattern used as a quasi-proxy implementation.
0517: * Unfortunately, a real proxy requires at least JDK 1.3.
0518: */
0519: private static class Delegator /* implements InvocationHandler */{
0520:
0521: /**
0522: * The facade type in use
0523: */
0524: private final Class facade;
0525:
0526: /**
0527: * The delegation target type
0528: */
0529: private final Class type;
0530:
0531: /**
0532: * The delegation target object
0533: */
0534: private final Object peer;
0535:
0536: /**
0537: * Constructs a delegator instance.
0538: *
0539: * @param target the target object in use
0540: * @param source the source interface in use
0541: */
0542: public Delegator(Object target, Class source) {
0543: if (target instanceof Class) {
0544: // static delegation
0545: type = (Class) target;
0546: peer = null;
0547: } else {
0548: // object delegation
0549: type = target.getClass();
0550: peer = target;
0551: }
0552: facade = source;
0553: }
0554:
0555: /**
0556: * Constructs a delegator instance.
0557: *
0558: * @param target the target object in use
0559: */
0560: public Delegator(Object target) {
0561: this (target, null);
0562: }
0563:
0564: /**
0565: * Delegates the specified method.
0566: *
0567: * @param name the accessed method name
0568: * @param deftypes the method type
0569: * @param args the method arguments
0570: * @return the method invocation result
0571: * @throws Throwable if the invocation fails
0572: */
0573: /*
0574: public Object invoke(String method, Class[] types, Object[] args)
0575: throws Throwable {
0576: String name = method.getName();
0577: Class[] deftypes = method.getParameterTypes();
0578: ...
0579: */
0580: public Object invoke(String name, Class[] deftypes,
0581: Object[] args) throws Exception {
0582: int count = ((args != null) ? args.length : 0);
0583: Class[] types = new Class[count];
0584: Object[] param = new Object[count];
0585: for (int i = 0; i < count; i++) {
0586: Object arg = args[i];
0587: // a delegator proxy being aware of its own, hence
0588: // supplying its target object and interface
0589: // type if applicable
0590: Class art = null;
0591: if (null != arg) {
0592: art = arg.getClass();
0593: /*
0594: if (Proxy.isProxyClass(art)) {
0595: InvocationHandler handler=Proxy.getInvocationHandler(arg);
0596: if (Delegator.class == handler.getClass()) {
0597: // supply the underlying peer instance
0598: arg = ((Delegator)handler).peer;
0599: // supply the wrapped interface type
0600: Class[] cls = art.getInterfaces();
0601: if ((null != cls)&&(cls.length == 1)) {
0602: art = cls[0];
0603: }
0604: }
0605: }
0606: */
0607: if (arg instanceof Delegator) {
0608: Delegator ref = (Delegator) arg;
0609: arg = ref.peer;
0610: if (null != ref.facade) {
0611: art = ref.facade;
0612: }
0613: }
0614: }
0615:
0616: // runtime types overwrite interface types
0617: types[i] = ((null != art) ? art : ((null != deftypes)
0618: && (i < deftypes.length) ? deftypes[i]
0619: : Object.class));
0620: param[i] = arg;
0621: }
0622: Method m = type.getMethod(name, types);
0623: return m.invoke(peer, param);
0624: }
0625:
0626: /**
0627: * Supplies a proxy for this delegator.
0628: *
0629: * @param source the interface in use
0630: * @return an delegation proxy instance
0631: */
0632: /*
0633: public Object getProxy(Class source) {
0634: return Proxy.newProxyInstance(source.getClassLoader(),
0635: new Class[] { source }, this);
0636: }
0637: */
0638: }
0639:
0640: /**
0641: * The client authentication support level indicator
0642: * (for JSSE backward compatibility),
0643: * which also indicates the api level in question.
0644: */
0645: private static final boolean supportsOptionalClientAuth;
0646:
0647: /**
0648: * The implementation nmespace depending on the api level in question.
0649: */
0650: private static final String implementationNamespace;
0651:
0652: static {
0653: boolean supported = false;
0654: try {
0655: Class c = javax.net.ssl.SSLServerSocket.class;
0656: Class cp[] = { java.lang.Boolean.TYPE };
0657: Method ic = null;
0658: supported = (null != c.getMethod("setWantClientAuth", cp));
0659: } catch (Exception ex) {
0660: supported = false;
0661: } finally {
0662: supportsOptionalClientAuth = supported;
0663: implementationNamespace = (supported ? "javax.net.ssl."
0664: : "com.sun.net.ssl.");
0665: }
0666: }
0667:
0668: /**
0669: * The property key for the system protocol package lookup
0670: */
0671: public static final String PROTOCOL_HANDLER_S = "java.protocol.handler.pkgs";
0672:
0673: /**
0674: * static flag for enabling debug output if applicable
0675: */
0676: private static boolean debug = false;
0677:
0678: /**
0679: * The context used for creating a server socket factory
0680: */
0681: private SSLContext context = null;
0682:
0683: /**
0684: * The daemon of this factory
0685: */
0686: private httpd daemon = null;
0687:
0688: /**
0689: * The daemon bind address for this factory
0690: */
0691: private InetAddress bindAddr = null;
0692:
0693: /**
0694: * The daemon bind address for this factory
0695: */
0696: private int maxClients = 0;
0697:
0698: /**
0699: * factory method for creating a secure server socket
0700: * @return a new server socket instance
0701: * @throws java.io.IOException due to socket creation problems
0702: */
0703: public ServerSocket createServerSocket() throws IOException {
0704: int port = daemon.getPort();
0705: int clients = Math.max(128, maxClients);
0706: ServerSocket serversocket = null;
0707: if (bindAddr == null) {
0708: serversocket = getFactory().createServerSocket(port,
0709: clients);
0710: } else {
0711: serversocket = getFactory().createServerSocket(port,
0712: clients, bindAddr);
0713: }
0714: // tk, 1 February 2004,
0715: // added optional client authentication,
0716: // which is forced, if a truststore is configured and
0717: // the org.w3c.jigsaw.ssl.authenticate is not set to false
0718: if (serversocket instanceof SSLServerSocket) {
0719: ObservableProperties props = daemon.getProperties();
0720:
0721: // decide client authentication based on trust configuration
0722: boolean mandatory;
0723: mandatory = props.getBoolean(
0724: SSLProperties.MUST_AUTHENTICATE_P, false);
0725: boolean generic;
0726: generic = props.getBoolean(
0727: SSLProperties.TRUSTSTORE_GENERIC_P, false);
0728: String trust;
0729: trust = props.getString(SSLProperties.TRUSTSTORE_PATH_P,
0730: null);
0731:
0732: boolean authenticate = mandatory || generic
0733: || ((null != trust) && (trust.length() > 0));
0734:
0735: if (authenticate) {
0736: SSLServerSocket sslsocket = (SSLServerSocket) serversocket;
0737: if (mandatory) {
0738: sslsocket.setNeedClientAuth(true);
0739: } else {
0740: if (supportsOptionalClientAuth) {
0741: sslsocket.setWantClientAuth(true);
0742: } else {
0743: throw new SSLProtocolException(
0744: "Optional client "
0745: + "authentication not supported by the"
0746: + " current api level. Consider upgrading"
0747: + " your api or using obligatory client"
0748: + " authentication or using server "
0749: + "authentication only");
0750: }
0751: }
0752: }
0753: }
0754: return serversocket;
0755: }
0756:
0757: /**
0758: * Adds a security provider.
0759: *
0760: * @param provider the provider class name in question
0761: * @throws java.lang.ClassNotFoundException if the provider is unavailable
0762: * @throws java.lang.IllegalAccessException if the provider has no
0763: * accessible default constructor
0764: * @throws java.lang.InstantiationException if the provider cannot be
0765: * instantiated
0766: */
0767: private static final void addProvider(String provider)
0768: throws ClassNotFoundException, IllegalAccessException,
0769: InstantiationException {
0770: if (null != provider) {
0771: if (null == Security.getProvider(provider)) {
0772: Class support = Class.forName(provider);
0773: Provider supplier = (Provider) support.newInstance();
0774: Security.addProvider(supplier);
0775: if (debug) {
0776: System.out.println("Added new security provider: "
0777: + supplier.getInfo() + ".");
0778: }
0779: }
0780: }
0781: }
0782:
0783: /**
0784: * Sets the protocol handler.
0785: *
0786: * @param handler the handler class name in question
0787: */
0788: private static final void setHandler(String handler) {
0789: if (null != handler) {
0790: System.setProperty(PROTOCOL_HANDLER_S, handler);
0791: if (debug) {
0792: System.out.println("Set new protocol handler: "
0793: + handler + ".");
0794: }
0795: }
0796: }
0797:
0798: /**
0799: * Loads a key store for read access.
0800: *
0801: * @param props the underlying property set
0802: * @param typekey the store type property key
0803: * @param pathkey the keystore path property key
0804: * @param passkey the password property key
0805: * @return the loaded keystore
0806: * @throws KeyStoreException if initialization fails
0807: * @throws java.io.IOException if keystore cannot be loaded
0808: * @throws NoSuchAlgorithmException if the integrity check is inavailable
0809: * @throws CertificateException if the a certificate is inaccessible
0810: */
0811: private static final KeyStore getStore(ObservableProperties props,
0812: String typekey, String pathkey, String passkey)
0813: throws KeyStoreException, IOException,
0814: NoSuchAlgorithmException, CertificateException {
0815: String storepath = props.getString(pathkey, null);
0816: if (null != storepath) {
0817: if ("".equals(storepath.trim()))
0818: storepath = null;
0819: }
0820: String storepass = props.getString(passkey, null);
0821: if ((null != storepath) || (null != storepass)) {
0822: String storetype = props.getString(typekey, KeyStore
0823: .getDefaultType());
0824: KeyStore store = KeyStore.getInstance(storetype);
0825:
0826: store.load((null != storepath) ? new BufferedInputStream(
0827: new FileInputStream(storepath)) : null,
0828: (null != storepass) ? storepass.toCharArray()
0829: : new char[0]);
0830: return store;
0831: } else {
0832: return null;
0833: }
0834: }
0835:
0836: /**
0837: * Instantiates a manager factory parameters object.
0838: *
0839: * @param props the underlying property set
0840: * @param typekey the store type property key
0841: * @param pathkey the keystore path property key
0842: * @param passkey the password property key
0843: * @return the specified manager factory parameters instance
0844: * @throws InvalidParameterSpecException if api support is not sufficient
0845: * @throws InvalidAlgorithmParameterException if generic initialization
0846: * fails
0847: * @throws ClassNotFoundException if the provider is unavailable
0848: */
0849: private static final Object getParams(ObservableProperties props,
0850: String typekey, String pathkey, String passkey)
0851: throws InvalidParameterSpecException,
0852: InvalidAlgorithmParameterException, ClassNotFoundException {
0853: if (supportsOptionalClientAuth) {
0854: String paratype = props.getString(typekey, null);
0855: if ((null != paratype) && (paratype.length() > 0)) {
0856: Class parameterFactory = Class.forName(paratype);
0857: String path = props.getString(pathkey, null);
0858: String pass = props.getString(passkey, null);
0859:
0860: ManagerFactoryParametersFactory mfpboot;
0861: mfpboot = new ManagerFactoryParametersFactory(
0862: parameterFactory);
0863: Object mfpload = null;
0864: try {
0865: mfpload = mfpboot.getInstance(path, pass);
0866: } catch (NoSuchMethodException ex) {
0867: try {
0868: mfpload = mfpboot.getInstance(path);
0869: } catch (NoSuchMethodException sub) {
0870: try {
0871: mfpload = mfpboot.getInstance();
0872: } catch (NoSuchMethodException next) {
0873: throw new InvalidAlgorithmParameterException(
0874: "Factory specified by type property has no "
0875: + "suitable instantiation method");
0876: }
0877: }
0878: }
0879:
0880: Class managerFactoryParameters = Class
0881: .forName(implementationNamespace
0882: + "ManagerFactoryParameters");
0883: if (managerFactoryParameters.isInstance(mfpload)) {
0884: return new Delegator(mfpload,
0885: managerFactoryParameters);
0886: } else {
0887: throw new InvalidAlgorithmParameterException(
0888: "Factory specified by type property does not "
0889: + "supply manager factory parameters");
0890: }
0891: } else {
0892: throw new InvalidAlgorithmParameterException(
0893: "No manager "
0894: + "factory parameter class specified as the type property");
0895: }
0896: } else {
0897: throw new InvalidParameterSpecException(
0898: "Generic manager "
0899: + "factory parameters not supported by the current api level. "
0900: + "Consider upgrading your api or using a classic keystore");
0901: }
0902: }
0903:
0904: /**
0905: * Creates an ssl context.
0906: *
0907: * @param props the underlying property set
0908: * @return the ssl context used for the socket factory
0909: * @throws ClassNotFoundException if the provider is unavailable
0910: * @throws KeyStoreException if keystore initialization fails
0911: * @throws IOException if keystore cannot be loaded
0912: * @throws NoSuchAlgorithmException if the integrity check is unavailable
0913: * @throws InvalidParameterSpecException if api support is not sufficient
0914: * @throws InvalidAlgorithmParameterException if generic initialization
0915: * fails
0916: * @throws CertificateException if the a certificate is inaccessible
0917: * @throws UnrecoverableKeyException if the key in question cannot
0918: * be recovered
0919: * @throws InstantiationException if the specified factory is abstract
0920: * @throws IllegalAccessException if factory constructor is unreachable
0921: * @throws InvocationTargetException if factory initialization fails
0922: * @throws KeyManagementException if initialization fails
0923: */
0924: private static final SSLContext createContext(
0925: ObservableProperties props) throws ClassNotFoundException,
0926: KeyStoreException, IOException, NoSuchAlgorithmException,
0927: InvalidParameterSpecException,
0928: InvalidAlgorithmParameterException, CertificateException,
0929: UnrecoverableKeyException, InstantiationException,
0930: IllegalAccessException, InvocationTargetException,
0931: KeyManagementException {
0932:
0933: // switch according to api-level
0934: Class keyManagerFactory = Class.forName(implementationNamespace
0935: + "KeyManagerFactory");
0936: Class trustManagerFactory = Class
0937: .forName(implementationNamespace
0938: + "TrustManagerFactory");
0939: Class sslContext = Class.forName(implementationNamespace
0940: + "SSLContext");
0941:
0942: // the ugly but legal key manager factory bootstrap
0943: KeyManagerFactory kmfboot = new KeyManagerFactory(
0944: keyManagerFactory);
0945: String kmftype = props.getString(
0946: SSLProperties.KEYMANAGER_TYPE_P, null);
0947: Object kmfload = kmfboot
0948: .getInstance((null != kmftype) ? kmftype : kmfboot
0949: .getDefaultAlgorithm());
0950: KeyManagerFactory kmf = new KeyManagerFactory(kmfload);
0951:
0952: boolean kgen = props.getBoolean(
0953: SSLProperties.KEYSTORE_GENERIC_P,
0954: SSLProperties.DEFAULT_KEYSTORE_GENERIC);
0955: if (kgen) {
0956: // generic key material instantiation (not prior to before JDK 1.4)
0957: Object kmfp = getParams(props,
0958: SSLProperties.TRUSTSTORE_TYPE_P,
0959: SSLProperties.TRUSTSTORE_PATH_P,
0960: SSLProperties.TRUSTSTORE_PASSWORD_P);
0961:
0962: kmf.init(kmfp);
0963: } else {
0964: KeyStore ks = getStore(props,
0965: SSLProperties.KEYSTORE_TYPE_P,
0966: SSLProperties.KEYSTORE_PATH_P,
0967: SSLProperties.KEYSTORE_PASSWORD_P);
0968:
0969: // reusing the store password for key access
0970: String keypass = props.getString(
0971: SSLProperties.KEYSTORE_PASSWORD_P, null);
0972:
0973: kmf.init(ks, (null != keypass ? keypass.toCharArray()
0974: : new char[0]));
0975: }
0976:
0977: // the ugly but legal trust manager factory bootstrap
0978: TrustManagerFactory tmfboot;
0979: tmfboot = new TrustManagerFactory(trustManagerFactory);
0980: String tmftype = props.getString(
0981: SSLProperties.TRUSTMANAGER_TYPE_P, null);
0982: Object tmfload = tmfboot
0983: .getInstance((null != tmftype) ? tmftype : tmfboot
0984: .getDefaultAlgorithm());
0985: TrustManagerFactory tmf = new TrustManagerFactory(tmfload);
0986:
0987: boolean tgen = props.getBoolean(
0988: SSLProperties.TRUSTSTORE_GENERIC_P,
0989: SSLProperties.DEFAULT_TRUSTSTORE_GENERIC);
0990: if (tgen) {
0991: // generic trust material instantiation (not < JDK 1.4)
0992: Object tmfp = getParams(props,
0993: SSLProperties.TRUSTSTORE_TYPE_P,
0994: SSLProperties.TRUSTSTORE_PATH_P,
0995: SSLProperties.TRUSTSTORE_PASSWORD_P);
0996: tmf.init(tmfp);
0997: } else {
0998: KeyStore ts = getStore(props,
0999: SSLProperties.TRUSTSTORE_TYPE_P,
1000: SSLProperties.TRUSTSTORE_PATH_P,
1001: SSLProperties.TRUSTSTORE_PASSWORD_P);
1002: tmf.init(ts);
1003: }
1004:
1005: // accessing the protocol type
1006: String protocol = props.getString(
1007: SSLProperties.PROTOCOL_NAME_P,
1008: SSLProperties.DEFAULT_PROTOCOL_NAME);
1009:
1010: // the ugly but legal ssl context bootstrap
1011: SSLContext ctxboot = new SSLContext(sslContext);
1012: Object ctxload = ctxboot.getInstance(protocol);
1013: SSLContext context = new SSLContext(ctxload);
1014: context.init(kmf.getKeyManagers(), tmf.getTrustManagers(),
1015: new SecureRandom());
1016: return context;
1017: }
1018:
1019: /**
1020: * method for intializing this factory
1021: * @param server the daemon of this factory
1022: */
1023: public void initialize(httpd server) {
1024: super .initialize(server);
1025: daemon = server;
1026: daemon.registerPropertySet(new SSLProperties(daemon));
1027: ObservableProperties props = daemon.getProperties();
1028:
1029: try {
1030:
1031: // Providers or protocols are switched by default
1032: // in a compatible way as postulated by the JDK 1.4 policies
1033: String provider;
1034: provider = props.getString(
1035: SSLProperties.SECURITY_PROVIDER_P,
1036: (supportsOptionalClientAuth ? null
1037: : SSLProperties.DEFAULT_SECURITY_PROVIDER));
1038: addProvider(provider);
1039:
1040: String handler = props.getString(
1041: SSLProperties.PROTOCOL_HANDLER_P,
1042: (supportsOptionalClientAuth ? null
1043: : SSLProperties.DEFAULT_PROTOCOL_HANDLER));
1044: setHandler(handler);
1045:
1046: context = createContext(props);
1047:
1048: String bindAddrName = props.getString(BINDADDR_P, null);
1049: if (bindAddrName != null) {
1050: try {
1051: bindAddr = InetAddress.getByName(bindAddrName);
1052: } catch (Exception ex) {
1053: bindAddr = null;
1054: }
1055: } else {
1056: bindAddr = null;
1057: }
1058:
1059: maxClients = props.getInteger(MAXCLIENTS_P, MAXCLIENTS);
1060:
1061: } catch (Exception ex) {
1062: String mes;
1063: mes = "Unable to initialize secure socket provider";
1064: daemon.fatal(ex, mes);
1065: if (debug) {
1066: System.err.println(mes);
1067: ex.printStackTrace();
1068: }
1069: RuntimeException rex;
1070: rex = new RuntimeException(mes);
1071: SSLAdapter.fillInStackTrace(rex, ex);
1072: throw rex;
1073: }
1074: }
1075:
1076: /**
1077: * method for handling a dynamic property modification
1078: * @param name the name of the property modified
1079: * @return true if and only if the modification has been handled
1080: * successfully
1081: */
1082: public boolean propertyChanged(String name) {
1083: if (super .propertyChanged(name)) {
1084: ObservableProperties props = daemon.getProperties();
1085:
1086: try {
1087: if (name.equals(MAXCLIENTS_P)) {
1088: int newmax = props.getInteger(MAXCLIENTS_P, -1);
1089: if (newmax > maxClients) {
1090: for (int i = maxClients - newmax; --i >= 0;) {
1091: addClient(true);
1092: }
1093: } else if (newmax > 0) {
1094: maxClients = newmax;
1095: }
1096: return true;
1097: } else if (name.equals(BINDADDR_P)) {
1098: bindAddr = InetAddress.getByName(props.getString(
1099: BINDADDR_P, null));
1100: return true;
1101: } else if ((name
1102: .equals(SSLProperties.KEYSTORE_GENERIC_P))
1103: || (name.equals(SSLProperties.KEYSTORE_PATH_P))
1104: || (name.equals(SSLProperties.KEYSTORE_TYPE_P))
1105: || (name
1106: .equals(SSLProperties.KEYSTORE_PASSWORD_P))
1107: || (name
1108: .equals(SSLProperties.TRUSTSTORE_GENERIC_P))
1109: || (name
1110: .equals(SSLProperties.TRUSTSTORE_PATH_P))
1111: || (name
1112: .equals(SSLProperties.TRUSTSTORE_TYPE_P))
1113: || (name
1114: .equals(SSLProperties.TRUSTSTORE_PASSWORD_P))
1115: || (name.equals(SSLProperties.PROTOCOL_NAME_P))) {
1116:
1117: context = createContext(props);
1118: return true;
1119: } else {
1120: return true;
1121: }
1122: } catch (Exception ex) {
1123: String mes;
1124: mes = "Unable to re-initialize secure socket provider";
1125: daemon.fatal(ex, mes);
1126: if (debug) {
1127: System.err.println(mes);
1128: ex.printStackTrace();
1129: }
1130: // RuntimeException rex;
1131: // rex = new RuntimeException(sub);
1132: // SSLAdapter.fillInStackTrace(rex, cause);
1133: // throw rex;
1134: return false;
1135: }
1136: } else {
1137: return false;
1138: }
1139: }
1140:
1141: /**
1142: * server sockt factory creation
1143: * @return the secure server socket factory
1144: * @throws java.io.IOException due to factory creation problems
1145: */
1146: private ServerSocketFactory getFactory() throws SSLKeyException {
1147: ServerSocketFactory factory;
1148: factory = ((null != context) ? context.getServerSocketFactory()
1149: :
1150: // make best effort to obtain a factory
1151: SSLServerSocketFactory.getDefault());
1152:
1153: String[] supported = ((SSLServerSocketFactory) factory)
1154: .getSupportedCipherSuites();
1155: if (debug) {
1156: System.out.println("Supported suites:");
1157: for (int i = 0; i < supported.length; i++) {
1158: System.out.println(" " + supported[i]);
1159: }
1160: String[] enabled = ((SSLServerSocketFactory) factory)
1161: .getDefaultCipherSuites();
1162: System.out.println("Enabled suites:");
1163: for (int i = 0; i < enabled.length; i++) {
1164: System.out.println(" " + enabled[i]);
1165: }
1166: }
1167:
1168: if (supported.length < 1) {
1169: SSLKeyException ex = new SSLKeyException(
1170: "No cipher suites supported by this "
1171: + "SSL socket factory. "
1172: + "Please check your factory, key store, "
1173: + "store password and cerificates");
1174: daemon.fatal(ex, ex.getMessage());
1175: if (debug) {
1176: ex.printStackTrace();
1177: }
1178: throw ex;
1179: }
1180: return factory;
1181: }
1182:
1183: /**
1184: * Factory for creating a new client for this pool.
1185: * @param server the target http daemon
1186: * @param state the client state holder
1187: * @return a new socket client
1188: */
1189: protected SocketClient createClient(httpd server,
1190: SocketClientState state) {
1191: return new SSLSocketClient(server, this, state);
1192: }
1193: }
|