001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.security;
007:
008: import java.io.ByteArrayInputStream;
009: import java.io.ByteArrayOutputStream;
010: import java.io.IOException;
011: import java.io.InputStream;
012: import java.io.OutputStream;
013: import java.net.InetAddress;
014: import java.net.ServerSocket;
015: import java.net.Socket;
016: import java.security.KeyFactory;
017: import java.security.KeyStore;
018: import java.security.PrivateKey;
019: import java.security.cert.Certificate;
020: import java.security.cert.CertificateFactory;
021: import java.security.spec.PKCS8EncodedKeySpec;
022: import java.sql.SQLException;
023: import java.util.Properties;
024:
025: import javax.net.ServerSocketFactory;
026: import javax.net.ssl.SSLServerSocket;
027: import javax.net.ssl.SSLServerSocketFactory;
028: import javax.net.ssl.SSLSocket;
029: import javax.net.ssl.SSLSocketFactory;
030:
031: import org.h2.message.Message;
032: import org.h2.util.ByteUtils;
033: import org.h2.util.FileUtils;
034: import org.h2.util.IOUtils;
035: import org.h2.util.NetUtils;
036:
037: /**
038: * A factory to create encrypted sockets. To generate new keystore, use the
039: * SecureKeyStoreBuilder tool.
040: */
041: public class SecureSocketFactory {
042:
043: private static final String KEYSTORE = ".h2.keystore";
044: private static final String KEYSTORE_KEY = "javax.net.ssl.keyStore";
045: private static final String KEYSTORE_PASSWORD_KEY = "javax.net.ssl.keyStorePassword";
046: public static final String KEYSTORE_PASSWORD = "h2pass";
047:
048: // TODO security / SSL: need a way to disable anonymous ssl
049: private static final boolean ENABLE_ANONYMOUS_SSL = true;
050: private static SecureSocketFactory factory;
051: private static final String ANONYMOUS_CIPHER_SUITE = "SSL_DH_anon_WITH_RC4_128_MD5";
052:
053: private static void setFactory(SecureSocketFactory f) {
054: factory = f;
055: }
056:
057: public static SecureSocketFactory getInstance() {
058: if (factory == null) {
059: setFactory(new SecureSocketFactory());
060: }
061: return factory;
062: }
063:
064: public Socket createSocket(InetAddress address, int port)
065: throws IOException {
066: Socket socket = null;
067: //#ifdef JDK14
068: setKeystore();
069: SSLSocketFactory f = (SSLSocketFactory) SSLSocketFactory
070: .getDefault();
071: SSLSocket secureSocket = (SSLSocket) f.createSocket(address,
072: port);
073: if (ENABLE_ANONYMOUS_SSL) {
074: String[] list = secureSocket.getEnabledCipherSuites();
075: list = addAnonymous(list);
076: secureSocket.setEnabledCipherSuites(list);
077: }
078: socket = secureSocket;
079: //#endif
080: return socket;
081: }
082:
083: public ServerSocket createServerSocket(int port)
084: throws IOException, SQLException {
085: ServerSocket socket = null;
086: //#ifdef JDK14
087: setKeystore();
088: ServerSocketFactory f = SSLServerSocketFactory.getDefault();
089: SSLServerSocket secureSocket;
090: InetAddress bindAddress = NetUtils.getBindAddress();
091: if (bindAddress == null) {
092: secureSocket = (SSLServerSocket) f.createServerSocket(port);
093: } else {
094: secureSocket = (SSLServerSocket) f.createServerSocket(port,
095: 0, bindAddress);
096: }
097: if (ENABLE_ANONYMOUS_SSL) {
098: String[] list = secureSocket.getEnabledCipherSuites();
099: list = addAnonymous(list);
100: secureSocket.setEnabledCipherSuites(list);
101: }
102: socket = secureSocket;
103: //#endif
104: return socket;
105: }
106:
107: //#ifdef JDK14
108: private static byte[] getBytes(String hex) throws SQLException {
109: return ByteUtils.convertStringToBytes(hex);
110: }
111:
112: private static byte[] getKeyStoreBytes(KeyStore store,
113: String password) throws IOException {
114: ByteArrayOutputStream bout = new ByteArrayOutputStream();
115: try {
116: store.store(bout, KEYSTORE_PASSWORD.toCharArray());
117: } catch (Exception e) {
118: throw Message.convertToIOException(e);
119: }
120: return bout.toByteArray();
121: }
122:
123: public static KeyStore getKeyStore(String password)
124: throws IOException {
125: try {
126: // The following source code can be re-generated
127: // if you have a keystore file.
128: // This code is (hopefully) more Java version independent
129: // than using keystores directly. See also:
130: // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4887561
131: // (1.4.2 cannot read keystore written with 1.4.1)
132: // --- generated code start ---
133:
134: KeyStore store = KeyStore.getInstance(KeyStore
135: .getDefaultType());
136:
137: store.load(null, password.toCharArray());
138: KeyFactory keyFactory = KeyFactory.getInstance("RSA");
139: store.load(null, password.toCharArray());
140: PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(
141: getBytes("30820277020100300d06092a864886f70d0101010500048202613082025d02010002818100dc0a13c602b7141110eade2f051b54777b060d0f74e6a110f9cce81159f271ebc88d8e8aa1f743b505fc2e7dfe38d33b8d3f64d1b363d1af4d877833897954cbaec2fa384c22a415498cf306bb07ac09b76b001cd68bf77ea0a628f5101959cf2993a9c23dbee79b19305977f8715ae78d023471194cc900b231eecb0aaea98d02030100010281810099aa4ff4d0a09a5af0bd953cb10c4d08c3d98df565664ac5582e494314d5c3c92dddedd5d316a32a206be4ec084616fe57be15e27cad111aa3c21fa79e32258c6ca8430afc69eddd52d3b751b37da6b6860910b94653192c0db1d02abcfd6ce14c01f238eec7c20bd3bb750940004bacba2880349a9494d10e139ecb2355d101024100ffdc3defd9c05a2d377ef6019fa62b3fbd5b0020a04cc8533bca730e1f6fcf5dfceea1b044fbe17d9eababfbc7d955edad6bc60f9be826ad2c22ba77d19a9f65024100dc28d43fdbbc93852cc3567093157702bc16f156f709fb7db0d9eec028f41fd0edcd17224c866e66be1744141fb724a10fd741c8a96afdd9141b36d67fff6309024077b1cddbde0f69604bdcfe33263fb36ddf24aa3b9922327915b890f8a36648295d0139ecdf68c245652c4489c6257b58744fbdd961834a4cab201801a3b1e52d024100b17142e8991d1b350a0802624759d48ae2b8071a158ff91fabeb6a8f7c328e762143dc726b8529f42b1fab6220d1c676fdc27ba5d44e847c72c52064afd351a902407c6e23fe35bcfcd1a662aa82a2aa725fcece311644d5b6e3894853fd4ce9fe78218c957b1ff03fc9e5ef8ffeb6bd58235f6a215c97d354fdace7e781e4a63e8b"));
142: PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
143: Certificate[] certs = new Certificate[] { CertificateFactory
144: .getInstance("X.509")
145: .generateCertificate(
146: new ByteArrayInputStream(
147: getBytes("3082018b3081f502044295ce6b300d06092a864886f70d0101040500300d310b3009060355040313024832301e170d3035303532363133323630335a170d3337303933303036353734375a300d310b300906035504031302483230819f300d06092a864886f70d010101050003818d0030818902818100dc0a13c602b7141110eade2f051b54777b060d0f74e6a110f9cce81159f271ebc88d8e8aa1f743b505fc2e7dfe38d33b8d3f64d1b363d1af4d877833897954cbaec2fa384c22a415498cf306bb07ac09b76b001cd68bf77ea0a628f5101959cf2993a9c23dbee79b19305977f8715ae78d023471194cc900b231eecb0aaea98d0203010001300d06092a864886f70d01010405000381810083f4401a279453701bef9a7681a5b8b24f153f7d18c7c892133d97bd5f13736be7505290a445a7d5ceb75522403e5097515cd966ded6351ff60d5193de34cd36e5cb04d380398e66286f99923fd92296645fd4ada45844d194dfd815e6cd57f385c117be982809028bba1116c85740b3d27a55b1a0948bf291ddba44bed337b9"))), };
148: store.setKeyEntry("h2", privateKey, password.toCharArray(),
149: certs);
150: // --- generated code end ---
151: return store;
152: } catch (Exception e) {
153: throw Message.convertToIOException(e);
154: }
155: }
156:
157: private void setKeystore() throws IOException {
158: Properties p = System.getProperties();
159: if (p.getProperty(KEYSTORE_KEY) == null) {
160: String fileName = FileUtils.getFileInUserHome(KEYSTORE);
161: byte[] data = getKeyStoreBytes(
162: getKeyStore(KEYSTORE_PASSWORD), KEYSTORE_PASSWORD);
163: boolean needWrite = true;
164: if (FileUtils.exists(fileName)
165: && FileUtils.length(fileName) == data.length) {
166: // don't need to overwrite the file if it did not change
167: InputStream fin = FileUtils
168: .openFileInputStream(fileName);
169: byte[] now = IOUtils.readBytesAndClose(fin, 0);
170: if (now != null
171: && ByteUtils.compareNotNull(data, now) == 0) {
172: needWrite = false;
173: }
174: }
175: if (needWrite) {
176: try {
177: OutputStream out = FileUtils.openFileOutputStream(
178: fileName, false);
179: out.write(data);
180: out.close();
181: } catch (SQLException e) {
182: throw Message.convertToIOException(e);
183: }
184: }
185: String absolutePath = FileUtils.getAbsolutePath(fileName);
186: System.setProperty(KEYSTORE_KEY, absolutePath);
187: }
188: if (p.getProperty(KEYSTORE_PASSWORD_KEY) == null) {
189: System
190: .setProperty(KEYSTORE_PASSWORD_KEY,
191: KEYSTORE_PASSWORD);
192: }
193: }
194:
195: private String[] addAnonymous(String[] list) {
196: String[] newList = new String[list.length + 1];
197: System.arraycopy(list, 0, newList, 1, list.length);
198: newList[0] = ANONYMOUS_CIPHER_SUITE;
199: return newList;
200: }
201:
202: //#endif
203:
204: }
|