WaarpBC.java

/*
 * This file is part of Waarp Project (named also Waarp or GG).
 *
 *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
 *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
 * individual contributors.
 *
 *  All Waarp Project is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along with
 * Waarp . If not, see <http://www.gnu.org/licenses/>.
 */

package org.waarp.common.digest;

import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.List;

public class WaarpBC {
  public static final String PROTOCOL = "TLS";
  public static final long DEFAULT_SESSIONCACHE_TIMEOUTSEC = 60;
  public static final long DEFAULT_SESSIONCACHE_SIZE = 20480;
  private static volatile boolean initialized = false;
  private static boolean specialSecureRandom = false;

  static {
    initializedTlsContext();
  }

  public static void initializedTlsContext() {
    try {
      if (!initialized) {
        addBcProvider();
        registerRandomSecure();
        initialized = true;
      }
    } catch (final Throwable throwable) {//NOSONAR
      throwable.printStackTrace();//NOSONAR
      System.err //NOSONAR
                 .println("Error occurs at startup: " +//NOSONAR
                          throwable.getMessage());//NOSONAR
    }
  }

  /**
   * Called at first
   */
  private static void addBcProvider() {
    OpenSsl.isAvailable();
  }

  /**
   * To fix issue on SecureRandom using bad algotithm
   * </br>
   * Called at second place
   */
  private static void registerRandomSecure() {
    if (System.getProperty("os.name").contains("Windows")) {
      final Provider provider = Security.getProvider("SunMSCAPI");
      if (provider != null) {
        Security.removeProvider(provider.getName());
        Security.insertProviderAt(provider, 1);
        specialSecureRandom = true;
      }
    } else {
      System.setProperty("java.security.egd", "file:/dev/./urandom");
      final Provider provider = Security.getProvider("SUN");
      final String type = "SecureRandom";
      final String alg = "NativePRNGNonBlocking";
      if (provider != null) {
        final String name = String.format("%s.%s", type, alg);
        final Provider.Service service = provider.getService(type, alg);
        if (service != null) {
          Security.insertProviderAt(
              new Provider(name, provider.getVersion(), "Waarp quick fix for SecureRandom using urandom") {
                private static final long serialVersionUID = 1001L;

                {
                  System.setProperty(name, service.getClassName());
                }

              }, 1);
          specialSecureRandom = true;
        }
      }
    }
  }

  public static SecureRandom getSecureRandom() {
    if (!specialSecureRandom) {
      return new SecureRandom();
    }
    if (System.getProperty("os.name").contains("Windows")) {
      try {
        return SecureRandom.getInstance("Windows-PRNG", "SunMSCAPI");
      } catch (final NoSuchAlgorithmException e) {
        return new SecureRandom();
      } catch (final NoSuchProviderException e) {
        return new SecureRandom();
      }
    } else {
      try {
        return SecureRandom.getInstance("NativePRNGNonBlocking", "SUN");
      } catch (final NoSuchAlgorithmException e) {
        return new SecureRandom();
      } catch (final NoSuchProviderException e) {
        return new SecureRandom();
      }
    }
  }

  public static SslContext getInstanceForServer(final KeyManagerFactory keyManagerFactory,
                                                final X509Certificate[] x509Certificates,
                                                final boolean clientNeedAuthentication,
                                                final boolean startTls, final List<String> ciphers,
                                                final String... protocols) throws SSLException {
    final SslContextBuilder builder =
        SslContextBuilder.forServer(keyManagerFactory).sslProvider(SslContext.defaultServerProvider());
    if (x509Certificates != null) {
      builder.trustManager(x509Certificates);
    }
    builder.clientAuth(clientNeedAuthentication? ClientAuth.REQUIRE : ClientAuth.NONE);
    if (ciphers != null && !ciphers.isEmpty()) {
      builder.ciphers(ciphers);
    }
    if (protocols != null && protocols.length > 0) {
      builder.protocols(protocols);
    }
    builder.sessionCacheSize(DEFAULT_SESSIONCACHE_SIZE).sessionTimeout(DEFAULT_SESSIONCACHE_TIMEOUTSEC)
           .startTls(startTls);
    return builder.build();
  }

  public static SslContext getInstanceForClient(final KeyManagerFactory keyManagerFactory,
                                                final X509Certificate[] x509Certificates)
      throws NoSuchAlgorithmException, NoSuchProviderException, SSLException {
    final SslContextBuilder builder =
        SslContextBuilder.forClient().sslProvider(SslContext.defaultClientProvider())
                         .keyManager(keyManagerFactory);
    if (x509Certificates != null) {
      builder.trustManager(x509Certificates);
    }
    builder.sessionCacheSize(DEFAULT_SESSIONCACHE_SIZE).sessionTimeout(DEFAULT_SESSIONCACHE_TIMEOUTSEC);
    return builder.build();
  }

  public static SSLContext getInstanceJDK() throws NoSuchAlgorithmException {
    return SSLContext.getInstance(PROTOCOL);
  }

  private WaarpBC() {
    // Nothing
  }
}