Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.accumulo.test.util; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Security; import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.util.Calendar; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import java.util.function.Predicate; import org.apache.accumulo.core.cli.Help; import org.apache.accumulo.core.client.AccumuloSecurityException; import org.apache.accumulo.core.conf.AccumuloConfiguration; import org.apache.accumulo.core.conf.DefaultConfiguration; import org.apache.accumulo.core.conf.Property; import org.apache.accumulo.core.conf.SiteConfiguration; import org.apache.commons.io.FileExistsException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.style.IETFUtils; import org.bouncycastle.asn1.x500.style.RFC4519Style; import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.KeyUsage; import org.bouncycastle.cert.CertIOException; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.X509CertificateObject; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; public class CertUtils { private static final Logger log = LoggerFactory.getLogger(CertUtils.class); static { Security.addProvider(new BouncyCastleProvider()); } static class Opts extends Help { @Parameter(description = "generate-all | generate-local | generate-self-trusted", required = true, arity = 1) List<String> operation = null; @Parameter(names = { "--local-keystore" }, description = "Target path for generated keystore") String localKeystore = null; @Parameter(names = { "--root-keystore" }, description = "Path to root truststore, generated with generate-all, or used for signing with generate-local") String rootKeystore = null; @Parameter(names = { "--root-truststore" }, description = "Target path for generated public root truststore") String truststore = null; @Parameter(names = { "--keystore-type" }, description = "Type of keystore file to use") String keystoreType = "JKS"; @Parameter(names = { "--root-keystore-password" }, description = "Password for root keystore, falls back to --keystore-password if not provided") String rootKeystorePassword = null; @Parameter(names = { "--keystore-password" }, description = "Password used to encrypt keystores. If omitted, the instance-wide secret will be used. If specified, the password must also be explicitly configured in Accumulo.") String keystorePassword = null; @Parameter(names = { "--truststore-password" }, description = "Password used to encrypt the truststore. If omitted, empty password is used") String truststorePassword = ""; @Parameter(names = { "--key-name-prefix" }, description = "Prefix for names of generated keys") String keyNamePrefix = CertUtils.class.getSimpleName(); @Parameter(names = { "--issuer-rdn" }, description = "RDN string for issuer, for example: 'c=US,o=My Organization,cn=My Name'") String issuerDirString = "o=Apache Accumulo"; @Parameter(names = "--site-file", description = "Load configuration from the given site file") public String siteFile = null; @Parameter(names = "--signing-algorithm", description = "Algorithm used to sign certificates") public String signingAlg = "SHA256WITHRSA"; @Parameter(names = "--encryption-algorithm", description = "Algorithm used to encrypt private keys") public String encryptionAlg = "RSA"; @Parameter(names = "--keysize", description = "Key size used by encryption algorithm") public int keysize = 2048; public AccumuloConfiguration getConfiguration() { if (siteFile == null) { return SiteConfiguration.getInstance(DefaultConfiguration.getInstance()); } else { return new AccumuloConfiguration() { Configuration xml = new Configuration(); { xml.addResource(new Path(siteFile)); } @Override public Iterator<Entry<String, String>> iterator() { TreeMap<String, String> map = new TreeMap<String, String>(); for (Entry<String, String> props : DefaultConfiguration.getInstance()) map.put(props.getKey(), props.getValue()); for (Entry<String, String> props : xml) map.put(props.getKey(), props.getValue()); return map.entrySet().iterator(); } @Override public String get(Property property) { String value = xml.get(property.getKey()); if (value != null) return value; return DefaultConfiguration.getInstance().get(property); } @Override public void getProperties(Map<String, String> props, Predicate<String> filter) { for (Entry<String, String> entry : this) if (filter.test(entry.getKey())) props.put(entry.getKey(), entry.getValue()); } }; } } } public static void main(String[] args) throws Exception { Opts opts = new Opts(); opts.parseArgs(CertUtils.class.getName(), args); String operation = opts.operation.get(0); String keyPassword = opts.keystorePassword; if (keyPassword == null) keyPassword = getDefaultKeyPassword(); String rootKeyPassword = opts.rootKeystorePassword; if (rootKeyPassword == null) { rootKeyPassword = keyPassword; } CertUtils certUtils = new CertUtils(opts.keystoreType, opts.issuerDirString, opts.encryptionAlg, opts.keysize, opts.signingAlg); if ("generate-all".equals(operation)) { certUtils.createAll(new File(opts.rootKeystore), new File(opts.localKeystore), new File(opts.truststore), opts.keyNamePrefix, rootKeyPassword, keyPassword, opts.truststorePassword); } else if ("generate-local".equals(operation)) { certUtils.createSignedCert(new File(opts.localKeystore), opts.keyNamePrefix + "-local", keyPassword, opts.rootKeystore, rootKeyPassword); } else if ("generate-self-trusted".equals(operation)) { certUtils.createSelfSignedCert(new File(opts.truststore), opts.keyNamePrefix + "-selfTrusted", keyPassword); } else { JCommander jcommander = new JCommander(opts); jcommander.setProgramName(CertUtils.class.getName()); jcommander.usage(); System.err.println("Unrecognized operation: " + opts.operation); System.exit(0); } } private static String getDefaultKeyPassword() { return SiteConfiguration.getInstance(DefaultConfiguration.getInstance()).get(Property.INSTANCE_SECRET); } private String issuerDirString; private String keystoreType; private String encryptionAlgorithm; private int keysize; private String signingAlgorithm; public CertUtils(String keystoreType, String issuerDirString, String encryptionAlgorithm, int keysize, String signingAlgorithm) { super(); this.keystoreType = keystoreType; this.issuerDirString = issuerDirString; this.encryptionAlgorithm = encryptionAlgorithm; this.keysize = keysize; this.signingAlgorithm = signingAlgorithm; } public void createAll(File rootKeystoreFile, File localKeystoreFile, File trustStoreFile, String keyNamePrefix, String rootKeystorePassword, String keystorePassword, String truststorePassword) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, OperatorCreationException, AccumuloSecurityException, NoSuchProviderException, UnrecoverableKeyException, FileNotFoundException { createSelfSignedCert(rootKeystoreFile, keyNamePrefix + "-root", rootKeystorePassword); createSignedCert(localKeystoreFile, keyNamePrefix + "-local", keystorePassword, rootKeystoreFile.getAbsolutePath(), rootKeystorePassword); createPublicCert(trustStoreFile, keyNamePrefix + "-public", rootKeystoreFile.getAbsolutePath(), rootKeystorePassword, truststorePassword); } public void createPublicCert(File targetKeystoreFile, String keyName, String rootKeystorePath, String rootKeystorePassword, String truststorePassword) throws NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException, KeyStoreException, UnrecoverableKeyException { KeyStore signerKeystore = KeyStore.getInstance(keystoreType); char[] signerPasswordArray = rootKeystorePassword.toCharArray(); try (FileInputStream fis = new FileInputStream(rootKeystorePath)) { signerKeystore.load(fis, signerPasswordArray); } Certificate rootCert = findCert(signerKeystore); KeyStore keystore = KeyStore.getInstance(keystoreType); keystore.load(null, null); keystore.setCertificateEntry(keyName + "Cert", rootCert); try (FileOutputStream fos = new FileOutputStream(targetKeystoreFile)) { keystore.store(fos, truststorePassword.toCharArray()); } } public void createSignedCert(File targetKeystoreFile, String keyName, String keystorePassword, String signerKeystorePath, String signerKeystorePassword) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, OperatorCreationException, AccumuloSecurityException, UnrecoverableKeyException, NoSuchProviderException { KeyStore signerKeystore = KeyStore.getInstance(keystoreType); char[] signerPasswordArray = signerKeystorePassword.toCharArray(); try (FileInputStream fis = new FileInputStream(signerKeystorePath)) { signerKeystore.load(fis, signerPasswordArray); } Certificate signerCert = findCert(signerKeystore); PrivateKey signerKey = findPrivateKey(signerKeystore, signerPasswordArray); KeyPair kp = generateKeyPair(); X509CertificateObject cert = generateCert(keyName, kp, false, signerCert.getPublicKey(), signerKey); char[] password = keystorePassword.toCharArray(); KeyStore keystore = KeyStore.getInstance(keystoreType); keystore.load(null, null); keystore.setCertificateEntry(keyName + "Cert", cert); keystore.setKeyEntry(keyName + "Key", kp.getPrivate(), password, new Certificate[] { cert, signerCert }); try (FileOutputStream fos = new FileOutputStream(targetKeystoreFile)) { keystore.store(fos, password); } } public void createSelfSignedCert(File targetKeystoreFile, String keyName, String keystorePassword) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, OperatorCreationException, AccumuloSecurityException, NoSuchProviderException { if (targetKeystoreFile.exists()) { throw new FileExistsException(targetKeystoreFile); } KeyPair kp = generateKeyPair(); X509CertificateObject cert = generateCert(keyName, kp, true, kp.getPublic(), kp.getPrivate()); char[] password = keystorePassword.toCharArray(); KeyStore keystore = KeyStore.getInstance(keystoreType); keystore.load(null, null); keystore.setCertificateEntry(keyName + "Cert", cert); keystore.setKeyEntry(keyName + "Key", kp.getPrivate(), password, new Certificate[] { cert }); try (FileOutputStream fos = new FileOutputStream(targetKeystoreFile)) { keystore.store(fos, password); } } private KeyPair generateKeyPair() throws NoSuchAlgorithmException, NoSuchProviderException { KeyPairGenerator gen = KeyPairGenerator.getInstance(encryptionAlgorithm); gen.initialize(keysize); return gen.generateKeyPair(); } private X509CertificateObject generateCert(String keyName, KeyPair kp, boolean isCertAuthority, PublicKey signerPublicKey, PrivateKey signerPrivateKey) throws IOException, CertIOException, OperatorCreationException, CertificateException, NoSuchAlgorithmException { Calendar startDate = Calendar.getInstance(); Calendar endDate = Calendar.getInstance(); endDate.add(Calendar.YEAR, 100); BigInteger serialNumber = BigInteger.valueOf((startDate.getTimeInMillis())); X500Name issuer = new X500Name(IETFUtils.rDNsFromString(issuerDirString, RFC4519Style.INSTANCE)); JcaX509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(issuer, serialNumber, startDate.getTime(), endDate.getTime(), issuer, kp.getPublic()); JcaX509ExtensionUtils extensionUtils = new JcaX509ExtensionUtils(); certGen.addExtension(Extension.subjectKeyIdentifier, false, extensionUtils.createSubjectKeyIdentifier(kp.getPublic())); certGen.addExtension(Extension.basicConstraints, false, new BasicConstraints(isCertAuthority)); certGen.addExtension(Extension.authorityKeyIdentifier, false, extensionUtils.createAuthorityKeyIdentifier(signerPublicKey)); if (isCertAuthority) { certGen.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign)); } X509CertificateHolder cert = certGen .build(new JcaContentSignerBuilder(signingAlgorithm).build(signerPrivateKey)); return new X509CertificateObject(cert.toASN1Structure()); } static Certificate findCert(KeyStore keyStore) throws KeyStoreException { Enumeration<String> aliases = keyStore.aliases(); Certificate cert = null; while (aliases.hasMoreElements()) { String alias = aliases.nextElement(); if (keyStore.isCertificateEntry(alias)) { if (cert == null) { cert = keyStore.getCertificate(alias); } else { log.warn("Found multiple certificates in keystore. Ignoring " + alias); } } } if (cert == null) { throw new KeyStoreException("Could not find cert in keystore"); } return cert; } static PrivateKey findPrivateKey(KeyStore keyStore, char[] keystorePassword) throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException { Enumeration<String> aliases = keyStore.aliases(); PrivateKey key = null; while (aliases.hasMoreElements()) { String alias = aliases.nextElement(); if (keyStore.isKeyEntry(alias)) { if (key == null) { key = (PrivateKey) keyStore.getKey(alias, keystorePassword); } else { log.warn("Found multiple keys in keystore. Ignoring " + alias); } } } if (key == null) { throw new KeyStoreException("Could not find private key in keystore"); } return key; } }