All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.generallycloud.baseio.component.ssl.SslContext Maven / Gradle / Ivy

/*
 * Copyright 2015-2017 GenerallyCloud.com
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */ 
package com.generallycloud.baseio.component.ssl;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyException;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.List;

import javax.crypto.Cipher;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.TrustManagerFactory;

import com.generallycloud.baseio.component.ByteArrayInputStream;
import com.generallycloud.baseio.component.SocketChannelContext;
import com.generallycloud.baseio.component.ssl.ApplicationProtocolConfig.Protocol;
import com.generallycloud.baseio.component.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
import com.generallycloud.baseio.component.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;

public abstract class SslContext {
	static final CertificateFactory	X509_CERT_FACTORY;
	static {
		try {
			X509_CERT_FACTORY = CertificateFactory.getInstance("X.509");
		} catch (CertificateException e) {
			throw new IllegalStateException("unable to instance X.509 CertificateFactory", e);
		}
	}

	/**
	 * Returns the default server-side implementation provider currently in
	 * use.
	 *
	 * @return {@link SslProvider#OPENSSL} if OpenSSL is available.
	 *         {@link SslProvider#JDK} otherwise.
	 */
	public static SslProvider defaultServerProvider() {
		return defaultProvider();
	}

	/**
	 * Returns the default client-side implementation provider currently in
	 * use.
	 *
	 * @return {@link SslProvider#OPENSSL} if OpenSSL is available.
	 *         {@link SslProvider#JDK} otherwise.
	 */
	public static SslProvider defaultClientProvider() {
		return defaultProvider();
	}

	private static SslProvider defaultProvider() {
		if (OpenSsl.isAvailable()) {
			return SslProvider.OPENSSL;
		} else {
			return SslProvider.JDK;
		}
	}

	static SslContext newServerContextInternal(SslProvider provider, X509Certificate[] trustCertCollection,
			TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain, PrivateKey key,
			String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers,
			CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, long sessionCacheSize,
			long sessionTimeout, ClientAuth clientAuth, boolean startTls) throws SSLException {

		if (provider == null) {
			provider = defaultServerProvider();
		}

		switch (provider) {
		case JDK:
			return new JdkSslServerContext(trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword,
					keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, clientAuth,
					startTls);
		case OPENSSL:
			return null;
		case OPENSSL_REFCNT:
			return null;
		default:
			throw new Error(provider.toString());
		}
	}

	static SslContext newClientContextInternal(SslProvider provider, X509Certificate[] trustCert,
			TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain, PrivateKey key,
			String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers,
			CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout)
			throws SSLException {
		if (provider == null) {
			provider = defaultClientProvider();
		}
		switch (provider) {
		case JDK:
			return new JdkSslClientContext(trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
					keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
		case OPENSSL:
			return null;
		case OPENSSL_REFCNT:
			return null;
		default:
			throw new Error(provider.toString());
		}
	}

	static ApplicationProtocolConfig toApplicationProtocolConfig(Iterable nextProtocols) {
		ApplicationProtocolConfig apn;
		if (nextProtocols == null) {
			apn = ApplicationProtocolConfig.DISABLED;
		} else {
			apn = new ApplicationProtocolConfig(Protocol.NPN_AND_ALPN,
					SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL, SelectedListenerFailureBehavior.ACCEPT,
					nextProtocols);
		}
		return apn;
	}

	/**
	 * Returns {@code true} if and only if this context is for server-side.
	 */
	public final boolean isServer() {
		return !isClient();
	}

	/**
	 * Returns the {@code true} if and only if this context is for client-side.
	 */
	public abstract boolean isClient();

	/**
	 * Returns the list of enabled cipher suites, in the order of preference.
	 */
	public abstract List cipherSuites();

	/**
	 * Returns the size of the cache used for storing SSL session objects.
	 */
	public abstract long sessionCacheSize();

	/**
	 * Returns the timeout for the cached SSL session objects, in seconds.
	 */
	public abstract long sessionTimeout();

	/**
	 * Returns the object responsible for negotiating application layer
	 * protocols for the TLS NPN/ALPN extensions.
	 */
	public abstract ApplicationProtocolNegotiator applicationProtocolNegotiator();

	public abstract SSLEngine newEngine();

	public abstract SSLEngine newEngine(String peerHost, int peerPort);

	/**
	 * Returns the {@link SSLSessionContext} object held by this context.
	 */
	public abstract SSLSessionContext sessionContext();

	/**
	 * Generates a key specification for an (encrypted) private key.
	 *
	 * @param password
	 *             characters, if {@code null} an unencrypted key is assumed
	 * @param key
	 *             bytes of the DER encoded private key
	 *
	 * @return a key specification
	 *
	 * @throws IOException
	 *              if parsing {@code key} fails
	 * @throws NoSuchAlgorithmException
	 *              if the algorithm used to encrypt {@code key} is unkown
	 * @throws NoSuchPaddingException
	 *              if the padding scheme specified in the decryption algorithm
	 *              is unkown
	 * @throws InvalidKeySpecException
	 *              if the decryption key based on {@code password} cannot be
	 *              generated
	 * @throws InvalidKeyException
	 *              if the decryption key based on {@code password} cannot be
	 *              used to decrypt {@code key}
	 * @throws InvalidAlgorithmParameterException
	 *              if decryption algorithm parameters are somehow faulty
	 */
	protected static PKCS8EncodedKeySpec generateKeySpec(char[] password, byte[] key) throws IOException,
			NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, InvalidKeyException,
			InvalidAlgorithmParameterException {

		if (password == null) {
			return new PKCS8EncodedKeySpec(key);
		}

		EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(key);
		SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName());
		PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
		SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);

		Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName());
		cipher.init(Cipher.DECRYPT_MODE, pbeKey, encryptedPrivateKeyInfo.getAlgParameters());

		return encryptedPrivateKeyInfo.getKeySpec(cipher);
	}

	/**
	 * Generates a new {@link KeyStore}.
	 *
	 * @param certChain
	 *             a X.509 certificate chain
	 * @param key
	 *             a PKCS#8 private key
	 * @param keyPasswordChars
	 *             the password of the {@code keyFile}. {@code null} if it's
	 *             not password-protected.
	 * @return generated {@link KeyStore}.
	 */
	static KeyStore buildKeyStore(X509Certificate[] certChain, PrivateKey key, char[] keyPasswordChars)
			throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
		KeyStore ks = KeyStore.getInstance("JKS");
		ks.load(null, null);
		ks.setKeyEntry("key", key, keyPasswordChars, certChain);
		return ks;
	}

	static PrivateKey toPrivateKey(File keyFile, String keyPassword) throws NoSuchAlgorithmException,
			NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException, KeyException,
			IOException {
		if (keyFile == null) {
			return null;
		}
		return getPrivateKeyFromByteBuffer(PemReader.readPrivateKey(keyFile), keyPassword);
	}

	static PrivateKey toPrivateKey(InputStream keyInputStream, String keyPassword) throws NoSuchAlgorithmException,
			NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException, KeyException,
			IOException {
		if (keyInputStream == null) {
			return null;
		}
		return getPrivateKeyFromByteBuffer(PemReader.readPrivateKey(keyInputStream), keyPassword);
	}

	private static PrivateKey getPrivateKeyFromByteBuffer(byte[] encodedKey, String keyPassword)
			throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
			InvalidAlgorithmParameterException, KeyException, IOException {

		PKCS8EncodedKeySpec encodedKeySpec = generateKeySpec(keyPassword == null ? null : keyPassword.toCharArray(),
				encodedKey);
		try {
			return KeyFactory.getInstance("RSA").generatePrivate(encodedKeySpec);
		} catch (InvalidKeySpecException ignore) {
			try {
				return KeyFactory.getInstance("DSA").generatePrivate(encodedKeySpec);
			} catch (InvalidKeySpecException ignore2) {
				try {
					return KeyFactory.getInstance("EC").generatePrivate(encodedKeySpec);
				} catch (InvalidKeySpecException e) {
					throw new InvalidKeySpecException("Neither RSA, DSA nor EC worked", e);
				}
			}
		}
	}
	
	public SslHandler newSslHandler(SocketChannelContext context) {
		return new SslHandler(context);
	}

	static X509Certificate[] toX509Certificates(File file) throws CertificateException {
		if (file == null) {
			return null;
		}
		return getCertificatesFromBuffers(PemReader.readCertificates(file));
	}

	static X509Certificate[] toX509Certificates(InputStream in) throws CertificateException {
		if (in == null) {
			return null;
		}
		return getCertificatesFromBuffers(PemReader.readCertificates(in));
	}

	private static X509Certificate[] getCertificatesFromBuffers(byte[][] certs) throws CertificateException {
		CertificateFactory cf = CertificateFactory.getInstance("X.509");
		X509Certificate[] x509Certs = new X509Certificate[certs.length];

		for (int i = 0; i < certs.length; i++) {
			x509Certs[i] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certs[i]));
		}
		return x509Certs;
	}

	static TrustManagerFactory buildTrustManagerFactory(X509Certificate[] certCollection,
			TrustManagerFactory trustManagerFactory) throws NoSuchAlgorithmException, CertificateException,
			KeyStoreException, IOException {
		KeyStore ks = KeyStore.getInstance("JKS");
		ks.load(null, null);

		int i = 1;
		for (X509Certificate cert : certCollection) {
			String alias = Integer.toString(i);
			ks.setCertificateEntry(alias, cert);
			i++;
		}

		// Set up trust manager factory to use our key store.
		if (trustManagerFactory == null) {
			trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
		}
		trustManagerFactory.init(ks);

		return trustManagerFactory;
	}

	static PrivateKey toPrivateKeyInternal(File keyFile, String keyPassword) throws SSLException {
		try {
			return toPrivateKey(keyFile, keyPassword);
		} catch (Exception e) {
			throw new SSLException(e);
		}
	}

	static X509Certificate[] toX509CertificatesInternal(File file) throws SSLException {
		try {
			return toX509Certificates(file);
		} catch (CertificateException e) {
			throw new SSLException(e);
		}
	}

	static KeyManagerFactory buildKeyManagerFactory(X509Certificate[] certChain, PrivateKey key, String keyPassword,
			KeyManagerFactory kmf) throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
			CertificateException, IOException {
		String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
		if (algorithm == null) {
			algorithm = "SunX509";
		}
		return buildKeyManagerFactory(certChain, algorithm, key, keyPassword, kmf);
	}

	static KeyManagerFactory buildKeyManagerFactory(X509Certificate[] certChainFile, String keyAlgorithm,
			PrivateKey key, String keyPassword, KeyManagerFactory kmf) throws KeyStoreException,
			NoSuchAlgorithmException, IOException, CertificateException, UnrecoverableKeyException {
		char[] keyPasswordChars = keyPassword == null ? "".toCharArray() : keyPassword.toCharArray();
		KeyStore ks = buildKeyStore(certChainFile, key, keyPasswordChars);
		// Set up key manager factory to use our key store
		if (kmf == null) {
			kmf = KeyManagerFactory.getInstance(keyAlgorithm);
		}
		kmf.init(ks, keyPasswordChars);

		return kmf;
	}

	static KeyManagerFactory buildDefaultKeyManagerFactory() throws NoSuchAlgorithmException, KeyStoreException,
			UnrecoverableKeyException {
		KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
		keyManagerFactory.init(null, null);
		return keyManagerFactory;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy