001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.core.net.ssl; 015 016import java.security.KeyManagementException; 017import java.security.KeyStore; 018import java.security.KeyStoreException; 019import java.security.NoSuchAlgorithmException; 020import java.security.NoSuchProviderException; 021import java.security.SecureRandom; 022import java.security.UnrecoverableKeyException; 023import java.security.cert.CertificateException; 024 025import javax.net.ssl.KeyManager; 026import javax.net.ssl.KeyManagerFactory; 027import javax.net.ssl.SSLContext; 028import javax.net.ssl.TrustManager; 029import javax.net.ssl.TrustManagerFactory; 030 031import ch.qos.logback.core.spi.ContextAware; 032 033/** 034 * A factory bean for a JSSE {@link SSLContext}. 035 * <p> 036 * This object holds the configurable properties for an SSL context and uses 037 * them to create an {@link SSLContext} instance. 038 * 039 * @author Carl Harris 040 */ 041public class SSLContextFactoryBean { 042 043 private static final String JSSE_KEY_STORE_PROPERTY = "javax.net.ssl.keyStore"; 044 private static final String JSSE_TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore"; 045 046 private KeyStoreFactoryBean keyStore; 047 private KeyStoreFactoryBean trustStore; 048 private SecureRandomFactoryBean secureRandom; 049 private KeyManagerFactoryFactoryBean keyManagerFactory; 050 private TrustManagerFactoryFactoryBean trustManagerFactory; 051 private String protocol; 052 private String provider; 053 054 /** 055 * Creates a new {@link SSLContext} using the receiver's configuration. 056 * 057 * @param context context for status messages 058 * @return {@link SSLContext} object 059 * @throws NoSuchProviderException if a provider specified for one of the JCA 060 * or JSSE components utilized in creating the 061 * context is not known to the platform 062 * @throws NoSuchAlgorithmException if a JCA or JSSE algorithm, protocol, or 063 * type name specified for one of the 064 * context's components is not known to a 065 * given provider (or platform default 066 * provider for the component) 067 * @throws KeyManagementException if an error occurs in creating a 068 * {@link KeyManager} for the context 069 * @throws UnrecoverableKeyException if a private key needed by a 070 * {@link KeyManager} cannot be obtained from 071 * a key store 072 * @throws KeyStoreException if an error occurs in reading the contents 073 * of a key store 074 * @throws CertificateException if an error occurs in reading the contents 075 * of a certificate 076 */ 077 public SSLContext createContext(ContextAware context) throws NoSuchProviderException, NoSuchAlgorithmException, 078 KeyManagementException, UnrecoverableKeyException, KeyStoreException, CertificateException { 079 080 SSLContext sslContext = getProvider() != null ? SSLContext.getInstance(getProtocol(), getProvider()) 081 : SSLContext.getInstance(getProtocol()); 082 083 context.addInfo("SSL protocol '" + sslContext.getProtocol() + "' provider '" + sslContext.getProvider() + "'"); 084 085 KeyManager[] keyManagers = createKeyManagers(context); 086 TrustManager[] trustManagers = createTrustManagers(context); 087 SecureRandom secureRandom = createSecureRandom(context); 088 sslContext.init(keyManagers, trustManagers, secureRandom); 089 return sslContext; 090 } 091 092 /** 093 * Creates key managers using the receiver's key store configuration. 094 * 095 * @param context context for status messages 096 * @return an array of key managers or {@code null} if no key store 097 * configuration was provided 098 * @throws NoSuchProviderException if a provider specified for one of the key 099 * manager components is not known to the 100 * platform 101 * @throws NoSuchAlgorithmException if an algorithm specified for one of the key 102 * manager components is not known to the 103 * relevant provider 104 * @throws KeyStoreException if an error occurs in reading a key store 105 */ 106 private KeyManager[] createKeyManagers(ContextAware context) 107 throws NoSuchProviderException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException { 108 109 if (getKeyStore() == null) 110 return null; 111 112 KeyStore keyStore = getKeyStore().createKeyStore(); 113 context.addInfo("key store of type '" + keyStore.getType() + "' provider '" + keyStore.getProvider() + "': " 114 + getKeyStore().getLocation()); 115 116 KeyManagerFactory kmf = getKeyManagerFactory().createKeyManagerFactory(); 117 context.addInfo("key manager algorithm '" + kmf.getAlgorithm() + "' provider '" + kmf.getProvider() + "'"); 118 119 char[] passphrase = getKeyStore().getPassword().toCharArray(); 120 kmf.init(keyStore, passphrase); 121 return kmf.getKeyManagers(); 122 } 123 124 /** 125 * Creates trust managers using the receiver's trust store configuration. 126 * 127 * @param context context for status messages 128 * @return an array of trust managers or {@code null} if no trust store 129 * configuration was provided 130 * @throws NoSuchProviderException if a provider specified for one of the trust 131 * manager components is not known to the 132 * platform 133 * @throws NoSuchAlgorithmException if an algorithm specified for one of the 134 * trust manager components is not known to the 135 * relevant provider 136 * @throws KeyStoreException if an error occurs in reading a key store 137 * containing trust anchors 138 */ 139 private TrustManager[] createTrustManagers(ContextAware context) 140 throws NoSuchProviderException, NoSuchAlgorithmException, KeyStoreException { 141 142 if (getTrustStore() == null) 143 return null; 144 145 KeyStore trustStore = getTrustStore().createKeyStore(); 146 context.addInfo("trust store of type '" + trustStore.getType() + "' provider '" + trustStore.getProvider() 147 + "': " + getTrustStore().getLocation()); 148 149 TrustManagerFactory tmf = getTrustManagerFactory().createTrustManagerFactory(); 150 context.addInfo("trust manager algorithm '" + tmf.getAlgorithm() + "' provider '" + tmf.getProvider() + "'"); 151 152 tmf.init(trustStore); 153 return tmf.getTrustManagers(); 154 } 155 156 private SecureRandom createSecureRandom(ContextAware context) 157 throws NoSuchProviderException, NoSuchAlgorithmException { 158 159 SecureRandom secureRandom = getSecureRandom().createSecureRandom(); 160 context.addInfo("secure random algorithm '" + secureRandom.getAlgorithm() + "' provider '" 161 + secureRandom.getProvider() + "'"); 162 163 return secureRandom; 164 } 165 166 /** 167 * Gets the key store configuration. 168 * 169 * @return key store factory bean or {@code null} if no key store configuration 170 * was provided 171 */ 172 public KeyStoreFactoryBean getKeyStore() { 173 if (keyStore == null) { 174 keyStore = keyStoreFromSystemProperties(JSSE_KEY_STORE_PROPERTY); 175 } 176 return keyStore; 177 } 178 179 /** 180 * Sets the key store configuration. 181 * 182 * @param keyStore the key store factory bean to set 183 */ 184 public void setKeyStore(KeyStoreFactoryBean keyStore) { 185 this.keyStore = keyStore; 186 } 187 188 /** 189 * Gets the trust store configuration. 190 * 191 * @return trust store factory bean or {@code null} if no trust store 192 * configuration was provided 193 */ 194 public KeyStoreFactoryBean getTrustStore() { 195 if (trustStore == null) { 196 trustStore = keyStoreFromSystemProperties(JSSE_TRUST_STORE_PROPERTY); 197 } 198 return trustStore; 199 } 200 201 /** 202 * Sets the trust store configuration. 203 * 204 * @param trustStore the trust store factory bean to set 205 */ 206 public void setTrustStore(KeyStoreFactoryBean trustStore) { 207 this.trustStore = trustStore; 208 } 209 210 /** 211 * Constructs a key store factory bean using JSSE system properties. 212 * 213 * @param property base property name (e.g. {@code javax.net.ssl.keyStore}) 214 * @return key store or {@code null} if no value is defined for the base system 215 * property name 216 */ 217 private KeyStoreFactoryBean keyStoreFromSystemProperties(String property) { 218 if (System.getProperty(property) == null) 219 return null; 220 KeyStoreFactoryBean keyStore = new KeyStoreFactoryBean(); 221 keyStore.setLocation(locationFromSystemProperty(property)); 222 keyStore.setProvider(System.getProperty(property + "Provider")); 223 keyStore.setPassword(System.getProperty(property + "Password")); 224 keyStore.setType(System.getProperty(property + "Type")); 225 return keyStore; 226 } 227 228 /** 229 * Constructs a resource location from a JSSE system property. 230 * 231 * @param name property name (e.g. {@code javax.net.ssl.keyStore}) 232 * @return URL for the location specified in the property or {@code null} if no 233 * value is defined for the property 234 */ 235 private String locationFromSystemProperty(String name) { 236 String location = System.getProperty(name); 237 if (location != null && !location.startsWith("file:")) { 238 location = "file:" + location; 239 } 240 return location; 241 } 242 243 /** 244 * Gets the secure random generator configuration. 245 * 246 * @return secure random factory bean; if no secure random generator 247 * configuration has been set, a default factory bean is returned 248 */ 249 public SecureRandomFactoryBean getSecureRandom() { 250 if (secureRandom == null) { 251 return new SecureRandomFactoryBean(); 252 } 253 return secureRandom; 254 } 255 256 /** 257 * Sets the secure random generator configuration. 258 * 259 * @param secureRandom the secure random factory bean to set 260 */ 261 public void setSecureRandom(SecureRandomFactoryBean secureRandom) { 262 this.secureRandom = secureRandom; 263 } 264 265 /** 266 * Gets the key manager factory configuration. 267 * 268 * @return factory bean; if no key manager factory configuration has been set, a 269 * default factory bean is returned 270 */ 271 public KeyManagerFactoryFactoryBean getKeyManagerFactory() { 272 if (keyManagerFactory == null) { 273 return new KeyManagerFactoryFactoryBean(); 274 } 275 return keyManagerFactory; 276 } 277 278 /** 279 * Sets the key manager factory configuration. 280 * 281 * @param keyManagerFactory the key manager factory bean to set 282 */ 283 public void setKeyManagerFactory(KeyManagerFactoryFactoryBean keyManagerFactory) { 284 this.keyManagerFactory = keyManagerFactory; 285 } 286 287 /** 288 * Gets the trust manager factory configuration. 289 * 290 * @return factory bean; if no trust manager factory configuration has been set, 291 * a default factory bean is returned 292 */ 293 public TrustManagerFactoryFactoryBean getTrustManagerFactory() { 294 if (trustManagerFactory == null) { 295 return new TrustManagerFactoryFactoryBean(); 296 } 297 return trustManagerFactory; 298 } 299 300 /** 301 * Sets the trust manager factory configuration. 302 * 303 * @param trustManagerFactory the factory bean to set 304 */ 305 public void setTrustManagerFactory(TrustManagerFactoryFactoryBean trustManagerFactory) { 306 this.trustManagerFactory = trustManagerFactory; 307 } 308 309 /** 310 * Gets the secure transport protocol name. 311 * 312 * @return protocol name (e.g. {@code SSL}, {@code TLS}); the 313 * {@link SSL#DEFAULT_PROTOCOL} is returned if no protocol has been 314 * configured 315 */ 316 public String getProtocol() { 317 if (protocol == null) { 318 return SSL.DEFAULT_PROTOCOL; 319 } 320 return protocol; 321 } 322 323 /** 324 * Sets the secure transport protocol name. 325 * 326 * @param protocol a protocol name, which must be recognized by the provider 327 * specified by {@link #setProvider(String)} or by the 328 * platform's default provider if no platform was specified. 329 */ 330 public void setProtocol(String protocol) { 331 this.protocol = protocol; 332 } 333 334 /** 335 * Gets the JSSE provider name for the SSL context. 336 * 337 * @return JSSE provider name 338 */ 339 public String getProvider() { 340 return provider; 341 } 342 343 /** 344 * Sets the JSSE provider name for the SSL context. 345 * 346 * @param provider name of the JSSE provider to use in creating the SSL context 347 */ 348 public void setProvider(String provider) { 349 this.provider = provider; 350 } 351 352}