web-dev-qa-db-fra.com

En Java, quelle est la manière la plus simple de créer un SSLContext avec juste un fichier PEM?

J'ai utilisé CertBot de LetsEncrypt pour générer des fichiers PEM gratuitement. Dans d'autres langues, il est facile de démarrer un serveur HTTPS en utilisant seulement quelques lignes de code et les fichiers PEM/clé. Les solutions que j'ai trouvées jusqu'à présent dans Java sont trop complexes et je cherche quelque chose de plus simple.

  1. Je ne veux pas utiliser la ligne de commande "keytool" de Java. Je veux juste faire glisser et déposer mes fichiers PEM/clé dans mon Eclipse et démarrer par programme un serveur HTTPS en utilisant un SSLContext.
  2. Je ne veux pas inclure d'énormes bibliothèques externes comme BouncyCastle. Voir le lien suivant pour une solution supposée utilisant BouncyCastle: Comment construire un SSLSocketFactory à partir d'un certificat et d'une clé PEM sans convertir en magasin de clés?

Existe-t-il un moyen meilleur/plus facile de le faire?

15
satnam

Ma solution complète que j'utilise actuellement:

  1. Utilisez certbot sur votre serveur pour générer le certificat. J'utilise la commande "certbot certonly -d myawesomedomain.com"
  2. J'utilise le code suivant pour convertir ce certificat certbot en un Java SSLContext: https://github.com/mirraj2/bowser/blob/master/src/bowser/SSLUtils). Java
package bowser;

import static com.google.common.base.Preconditions.checkState;
import static ox.util.Utils.propagate;

import Java.io.File;
import Java.security.KeyStore;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

import com.google.common.base.Splitter;

import ox.IO;
import ox.Log;

public class SSLUtils {

  public static SSLContext createContext(String domain) {
    String pass = "spamspam";

    File dir = new File("/etc/letsencrypt/live/" + domain);
    if (!dir.exists()) {
      Log.warn("Could not find letsencrypt dir: " + dir);
      return null;
    }

    File keystoreFile = new File(dir, "keystore.jks");
    File pemFile = new File(dir, "fullchain.pem");

    boolean generateKeystore = false;

    if (keystoreFile.exists()) {
      if (keystoreFile.lastModified() < pemFile.lastModified()) {
        Log.info("SSUtils: It looks like a new PEM file was created. Regenerating the keystore.");
        keystoreFile.delete();
        generateKeystore = true;
      }
    } else {
      generateKeystore = true;
    }

    if (generateKeystore) {
      Splitter splitter = Splitter.on(' ');
      try {
        String command = "openssl pkcs12 -export -out keystore.pkcs12 -in fullchain.pem -inkey privkey.pem -passout pass:"
            + pass;
        Log.debug(command);
        Process process = new ProcessBuilder(splitter.splitToList(command))
            .directory(dir).inheritIO().start();
        checkState(process.waitFor() == 0);

        command = "keytool -importkeystore -srckeystore keystore.pkcs12 -srcstoretype PKCS12 -destkeystore keystore.jks -srcstorepass "
            + pass + " -deststorepass " + pass;
        Log.debug(command);
        process = new ProcessBuilder(splitter.splitToList(command))
            .directory(dir).inheritIO().start();
        checkState(process.waitFor() == 0);

        new File(dir, "keystore.pkcs12").delete();// cleanup
      } catch (Exception e) {
        throw propagate(e);
      }
    }

    try {
      KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
      keystore.load(IO.from(keystoreFile).asStream(), pass.toCharArray());

      KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
      keyManagerFactory.init(keystore, pass.toCharArray());

      SSLContext ret = SSLContext.getInstance("TLSv1.2");
      TrustManagerFactory factory = TrustManagerFactory.getInstance(
          TrustManagerFactory.getDefaultAlgorithm());
      factory.init(keystore);
      ret.init(keyManagerFactory.getKeyManagers(), factory.getTrustManagers(), null);

      return ret;
    } catch (Exception e) {
      throw propagate(e);
    }
  }

}

0
satnam

Le code suivant montre en général comment créer un SSLContext pour un serveur HTTPS en analysant un fichier PEM qui a plusieurs entrées, par exemple plusieurs certificats et un RSA PRIVATE KEY. Cependant, il est incomplet car plain Java 8 est incapable d'analyser les données de clé privée PKCS # 1 RSA. Par conséquent, il semble que votre souhait de le faire sans aucune bibliothèque ne soit pas possible. Au moins BouncyCastle for l'analyse des données PKCS # 1 est requise (et l'analyseur PEM de BouncyCastle pourrait également être utilisé).

private SSLContext createSslContext() throws Exception {
    URL url = getClass().getResource("/a.pem");
    InputStream in = url.openStream();
    String pem = new String(in.readAllBytes(), StandardCharsets.UTF_8);
    Pattern parse = Pattern.compile("(?m)(?s)^---*BEGIN ([^-]+)---*$([^-]+)^---*END[^-]+-+$");
    Matcher m = parse.matcher(pem);
    CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
    Decoder decoder = Base64.getMimeDecoder();
    List<Certificate> certList = new ArrayList<>(); // Java.security.cert.Certificate

    PrivateKey privateKey = null;

    int start = 0;
    while (m.find(start)) {
        String type = m.group(1);
        String base64Data = m.group(2);
        byte[] data = decoder.decode(base64Data);
        start += m.group(0).length();
        type = type.toUpperCase();
        if (type.contains("CERTIFICATE")) {
            Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(data));
            certList.add(cert);
        } else if (type.contains("RSA PRIVATE KEY")) {
            // TODO: load and parse PKCS1 data structure to get the RSA private key  
            privateKey = ...
        } else {
            System.err.println("Unsupported type: " + type);
        }

    }
    if (privateKey == null)
        throw new RuntimeException("RSA private key not found in PEM file");

    char[] keyStorePassword = new char[0];

    KeyStore keyStore = KeyStore.getInstance("JKS");
    keyStore.load(null, null);

    int count = 0;
    for (Certificate cert : certList) {
        keyStore.setCertificateEntry("cert" + count, cert);
        count++;
    }
    Certificate[] chain = certList.toArray(new Certificate[certList.size()]);
    keyStore.setKeyEntry("key", privateKey, keyStorePassword, chain);

    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(keyStore);
    KeyManagerFactory kmf = KeyManagerFactory.getInstance("RSA");
    kmf.init(keyStore, keyStorePassword);
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
    return sslContext;
}
2
Robert