diff --git a/distribution/archives/build.gradle b/distribution/archives/build.gradle index f6e4ac4d73e7a..e5c1f3e3fb7cb 100644 --- a/distribution/archives/build.gradle +++ b/distribution/archives/build.gradle @@ -50,7 +50,7 @@ CopySpec archiveFiles(CopySpec modulesFiles, String distributionType, boolean os return copySpec { into("elasticsearch-${version}") { into('lib') { - with libFiles + with libFiles(oss) } into('config') { dirMode 0750 diff --git a/distribution/build.gradle b/distribution/build.gradle index ff3a06b4dc577..0dd2620adf37e 100644 --- a/distribution/build.gradle +++ b/distribution/build.gradle @@ -226,16 +226,24 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { /***************************************************************************** * Common files in all distributions * *****************************************************************************/ - libFiles = copySpec { - // delay by using closures, since they have not yet been configured, so no jar task exists yet - from { project(':server').jar } - from { project(':server').configurations.runtime } - from { project(':libs:plugin-classloader').jar } - from { project(':distribution:tools:java-version-checker').jar } - from { project(':distribution:tools:launchers').jar } - into('tools/plugin-cli') { - from { project(':distribution:tools:plugin-cli').jar } - from { project(':distribution:tools:plugin-cli').configurations.runtime } + libFiles = { oss -> + copySpec { + // delay by using closures, since they have not yet been configured, so no jar task exists yet + from { project(':server').jar } + from { project(':server').configurations.runtime } + from { project(':libs:plugin-classloader').jar } + from { project(':distribution:tools:java-version-checker').jar } + from { project(':distribution:tools:launchers').jar } + into('tools/plugin-cli') { + from { project(':distribution:tools:plugin-cli').jar } + from { project(':distribution:tools:plugin-cli').configurations.runtime } + } + if (oss == false) { + into('tools/security-cli') { + from { project(':x-pack:plugin:security:cli').jar } + from { project(':x-pack:plugin:security:cli').configurations.compile } + } + } } } diff --git a/distribution/packages/build.gradle b/distribution/packages/build.gradle index c50403da84763..6d19aca60537d 100644 --- a/distribution/packages/build.gradle +++ b/distribution/packages/build.gradle @@ -125,7 +125,7 @@ Closure commonPackageConfig(String type, boolean oss) { fileMode 0644 } into('lib') { - with libFiles + with libFiles(oss) } into('modules') { with modulesFiles(oss) diff --git a/qa/vagrant/src/main/java/org/elasticsearch/packaging/test/ArchiveTestCase.java b/qa/vagrant/src/main/java/org/elasticsearch/packaging/test/ArchiveTestCase.java index 3aada7837d8ae..2056111554225 100644 --- a/qa/vagrant/src/main/java/org/elasticsearch/packaging/test/ArchiveTestCase.java +++ b/qa/vagrant/src/main/java/org/elasticsearch/packaging/test/ArchiveTestCase.java @@ -57,6 +57,7 @@ import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.isEmptyString; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeThat; import static org.junit.Assume.assumeTrue; @@ -302,5 +303,26 @@ public void test80RelativePathConf() throws IOException { } } + public void test90SecurityCliPackaging() { + assumeThat(installation, is(notNullValue())); + + final Installation.Executables bin = installation.executables(); + final Shell sh = new Shell(); + + if (distribution().equals(Distribution.DEFAULT_TAR) || distribution().equals(Distribution.DEFAULT_ZIP)) { + assertTrue(Files.exists(installation.lib.resolve("tools").resolve("security-cli"))); + Platforms.onLinux(() -> { + final Result result = sh.run(bin.elasticsearchCertutil + " help"); + assertThat(result.stdout, containsString("Simplifies certificate creation for use with the Elastic Stack")); + }); + + Platforms.onWindows(() -> { + final Result result = sh.run(bin.elasticsearchCertutil + " help"); + assertThat(result.stdout, containsString("Simplifies certificate creation for use with the Elastic Stack")); + }); + } else if (distribution().equals(Distribution.OSS_TAR) || distribution().equals(Distribution.OSS_ZIP)) { + assertFalse(Files.exists(installation.lib.resolve("tools").resolve("security-cli"))); + } + } } diff --git a/qa/vagrant/src/main/java/org/elasticsearch/packaging/util/Installation.java b/qa/vagrant/src/main/java/org/elasticsearch/packaging/util/Installation.java index 40dc546f2306d..8bc3fc6e14d3b 100644 --- a/qa/vagrant/src/main/java/org/elasticsearch/packaging/util/Installation.java +++ b/qa/vagrant/src/main/java/org/elasticsearch/packaging/util/Installation.java @@ -101,6 +101,7 @@ public class Executables { public final Path elasticsearchPlugin = platformExecutable("elasticsearch-plugin"); public final Path elasticsearchKeystore = platformExecutable("elasticsearch-keystore"); public final Path elasticsearchTranslog = platformExecutable("elasticsearch-translog"); + public final Path elasticsearchCertutil = platformExecutable("elasticsearch-certutil"); private Path platformExecutable(String name) { final String platformExecutableName = Platforms.WINDOWS diff --git a/x-pack/plugin/core/build.gradle b/x-pack/plugin/core/build.gradle index 28f55408f7b1f..ea830b9022541 100644 --- a/x-pack/plugin/core/build.gradle +++ b/x-pack/plugin/core/build.gradle @@ -21,7 +21,6 @@ esplugin { } dependencyLicenses { - mapping from: /bc.*/, to: 'bouncycastle' mapping from: /http.*/, to: 'httpclient' // pulled in by rest client mapping from: /commons-.*/, to: 'commons' // pulled in by rest client } @@ -39,8 +38,6 @@ dependencies { // security deps shadow 'com.unboundid:unboundid-ldapsdk:3.2.0' - shadow 'org.bouncycastle:bcprov-jdk15on:1.59' - shadow 'org.bouncycastle:bcpkix-jdk15on:1.59' shadow project(path: ':modules:transport-netty4', configuration: 'runtime') testCompile 'org.elasticsearch:securemock:1.2' diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/CertParsingUtils.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/CertParsingUtils.java index 1617a92b550ac..6503f686b64f1 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/CertParsingUtils.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/CertParsingUtils.java @@ -63,7 +63,7 @@ static Path resolvePath(String path, @Nullable Environment environment) { return PathUtils.get(path).normalize(); } - static KeyStore readKeyStore(Path path, String type, char[] password) + public static KeyStore readKeyStore(Path path, String type, char[] password) throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException { try (InputStream in = Files.newInputStream(path)) { KeyStore store = KeyStore.getInstance(type); @@ -108,7 +108,7 @@ public static X509Certificate[] readX509Certificates(List certPaths) throw return certificates.toArray(new X509Certificate[0]); } - static List readCertificates(InputStream input) throws CertificateException, IOException { + public static List readCertificates(InputStream input) throws CertificateException, IOException { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); Collection certificates = (Collection) certFactory.generateCertificates(input); return new ArrayList<>(certificates); @@ -140,7 +140,7 @@ public static Map readPkcs12KeyPairs(Path path, char[] passwor /** * Creates a {@link KeyStore} from a PEM encoded certificate and key file */ - static KeyStore getKeyStoreFromPEM(Path certificatePath, Path keyPath, char[] keyPassword) + public static KeyStore getKeyStoreFromPEM(Path certificatePath, Path keyPath, char[] keyPassword) throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException { final PrivateKey key = PemUtils.readPrivateKey(keyPath, () -> keyPassword); final Certificate[] certificates = readCertificates(Collections.singletonList(certificatePath)); @@ -168,7 +168,7 @@ private static KeyStore getKeyStore(Certificate[] certificateChain, PrivateKey p /** * Returns a {@link X509ExtendedKeyManager} that is built from the provided keystore */ - static X509ExtendedKeyManager keyManager(KeyStore keyStore, char[] password, String algorithm) + public static X509ExtendedKeyManager keyManager(KeyStore keyStore, char[] password, String algorithm) throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException { KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm); kmf.init(keyStore, password); @@ -271,7 +271,7 @@ public static X509ExtendedTrustManager trustManager(String trustStorePath, Strin /** * Creates a {@link X509ExtendedTrustManager} based on the trust material in the provided {@link KeyStore} */ - static X509ExtendedTrustManager trustManager(KeyStore keyStore, String algorithm) + public static X509ExtendedTrustManager trustManager(KeyStore keyStore, String algorithm) throws NoSuchAlgorithmException, KeyStoreException { TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm); tmf.init(keyStore); diff --git a/x-pack/plugin/security/build.gradle b/x-pack/plugin/security/build.gradle index fdc4d9f1c789b..b8b1e324af7ee 100644 --- a/x-pack/plugin/security/build.gradle +++ b/x-pack/plugin/security/build.gradle @@ -21,8 +21,8 @@ dependencies { testCompile project(path: xpackModule('core'), configuration: 'testArtifacts') compile 'com.unboundid:unboundid-ldapsdk:3.2.0' - compile 'org.bouncycastle:bcprov-jdk15on:1.59' - compile 'org.bouncycastle:bcpkix-jdk15on:1.59' + compileOnly 'org.bouncycastle:bcprov-jdk15on:1.59' + compileOnly 'org.bouncycastle:bcpkix-jdk15on:1.59' // the following are all SAML dependencies - might as well download the whole internet compile "org.opensaml:opensaml-core:3.3.0" @@ -139,7 +139,6 @@ sourceSets.test.resources { srcDir '../core/src/test/resources' } dependencyLicenses { - mapping from: /bc.*/, to: 'bouncycastle' mapping from: /java-support|opensaml-.*/, to: 'shibboleth' mapping from: /http.*/, to: 'httpclient' } diff --git a/x-pack/plugin/security/cli/build.gradle b/x-pack/plugin/security/cli/build.gradle new file mode 100644 index 0000000000000..19b86dd91c78b --- /dev/null +++ b/x-pack/plugin/security/cli/build.gradle @@ -0,0 +1,20 @@ +apply plugin: 'elasticsearch.build' + +archivesBaseName = 'elasticsearch-security-cli' + +dependencies { + compileOnly "org.elasticsearch:elasticsearch:${version}" + compileOnly project(path: xpackModule('core'), configuration: 'shadow') + compile 'org.bouncycastle:bcprov-jdk15on:1.59' + compile 'org.bouncycastle:bcpkix-jdk15on:1.59' + testImplementation 'com.google.jimfs:jimfs:1.1' + testCompile "junit:junit:${versions.junit}" + testCompile "org.hamcrest:hamcrest-all:${versions.hamcrest}" + testCompile 'org.elasticsearch:securemock:1.2' + testCompile "org.elasticsearch.test:framework:${version}" + testCompile project(path: xpackModule('core'), configuration: 'testArtifacts') +} + +dependencyLicenses { + mapping from: /bc.*/, to: 'bouncycastle' +} diff --git a/x-pack/plugin/core/licenses/bcpkix-jdk15on-1.59.jar.sha1 b/x-pack/plugin/security/cli/licenses/bcpkix-jdk15on-1.59.jar.sha1 similarity index 100% rename from x-pack/plugin/core/licenses/bcpkix-jdk15on-1.59.jar.sha1 rename to x-pack/plugin/security/cli/licenses/bcpkix-jdk15on-1.59.jar.sha1 diff --git a/x-pack/plugin/core/licenses/bcprov-jdk15on-1.59.jar.sha1 b/x-pack/plugin/security/cli/licenses/bcprov-jdk15on-1.59.jar.sha1 similarity index 100% rename from x-pack/plugin/core/licenses/bcprov-jdk15on-1.59.jar.sha1 rename to x-pack/plugin/security/cli/licenses/bcprov-jdk15on-1.59.jar.sha1 diff --git a/x-pack/plugin/core/licenses/bouncycastle-LICENSE.txt b/x-pack/plugin/security/cli/licenses/bouncycastle-LICENSE.txt similarity index 100% rename from x-pack/plugin/core/licenses/bouncycastle-LICENSE.txt rename to x-pack/plugin/security/cli/licenses/bouncycastle-LICENSE.txt diff --git a/x-pack/plugin/core/licenses/bouncycastle-NOTICE.txt b/x-pack/plugin/security/cli/licenses/bouncycastle-NOTICE.txt similarity index 100% rename from x-pack/plugin/core/licenses/bouncycastle-NOTICE.txt rename to x-pack/plugin/security/cli/licenses/bouncycastle-NOTICE.txt diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/CertGenUtils.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertGenUtils.java similarity index 93% rename from x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/CertGenUtils.java rename to x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertGenUtils.java index 6273456aca2cd..0b88f3da40a14 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/CertGenUtils.java +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertGenUtils.java @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.xpack.core.ssl; +package org.elasticsearch.xpack.security.cli; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1ObjectIdentifier; @@ -78,7 +78,7 @@ private CertGenUtils() { * Generates a CA certificate */ public static X509Certificate generateCACertificate(X500Principal x500Principal, KeyPair keyPair, int days) - throws OperatorCreationException, CertificateException, CertIOException, NoSuchAlgorithmException { + throws OperatorCreationException, CertificateException, CertIOException, NoSuchAlgorithmException { return generateSignedCertificate(x500Principal, null, keyPair, null, null, true, days, null); } @@ -100,7 +100,7 @@ public static X509Certificate generateCACertificate(X500Principal x500Principal, */ public static X509Certificate generateSignedCertificate(X500Principal principal, GeneralNames subjectAltNames, KeyPair keyPair, X509Certificate caCert, PrivateKey caPrivKey, int days) - throws OperatorCreationException, CertificateException, CertIOException, NoSuchAlgorithmException { + throws OperatorCreationException, CertificateException, CertIOException, NoSuchAlgorithmException { return generateSignedCertificate(principal, subjectAltNames, keyPair, caCert, caPrivKey, false, days, null); } @@ -125,7 +125,7 @@ public static X509Certificate generateSignedCertificate(X500Principal principal, public static X509Certificate generateSignedCertificate(X500Principal principal, GeneralNames subjectAltNames, KeyPair keyPair, X509Certificate caCert, PrivateKey caPrivKey, int days, String signatureAlgorithm) - throws OperatorCreationException, CertificateException, CertIOException, NoSuchAlgorithmException { + throws OperatorCreationException, CertificateException, CertIOException, NoSuchAlgorithmException { return generateSignedCertificate(principal, subjectAltNames, keyPair, caCert, caPrivKey, false, days, signatureAlgorithm); } @@ -150,7 +150,7 @@ public static X509Certificate generateSignedCertificate(X500Principal principal, private static X509Certificate generateSignedCertificate(X500Principal principal, GeneralNames subjectAltNames, KeyPair keyPair, X509Certificate caCert, PrivateKey caPrivKey, boolean isCa, int days, String signatureAlgorithm) - throws NoSuchAlgorithmException, CertificateException, CertIOException, OperatorCreationException { + throws NoSuchAlgorithmException, CertificateException, CertIOException, OperatorCreationException { Objects.requireNonNull(keyPair, "Key-Pair must not be null"); final DateTime notBefore = new DateTime(DateTimeZone.UTC); if (days < 1) { @@ -175,8 +175,8 @@ private static X509Certificate generateSignedCertificate(X500Principal principal } JcaX509v3CertificateBuilder builder = - new JcaX509v3CertificateBuilder(issuer, serial, - new Time(notBefore.toDate(), Locale.ROOT), new Time(notAfter.toDate(), Locale.ROOT), subject, keyPair.getPublic()); + new JcaX509v3CertificateBuilder(issuer, serial, + new Time(notBefore.toDate(), Locale.ROOT), new Time(notAfter.toDate(), Locale.ROOT), subject, keyPair.getPublic()); builder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(keyPair.getPublic())); builder.addExtension(Extension.authorityKeyIdentifier, false, authorityKeyIdentifier); @@ -187,8 +187,8 @@ private static X509Certificate generateSignedCertificate(X500Principal principal PrivateKey signingKey = caPrivKey != null ? caPrivKey : keyPair.getPrivate(); ContentSigner signer = new JcaContentSignerBuilder( - (Strings.isNullOrEmpty(signatureAlgorithm)) ? getDefaultSignatureAlgorithm(signingKey) : signatureAlgorithm) - .setProvider(CertGenUtils.BC_PROV).build(signingKey); + (Strings.isNullOrEmpty(signatureAlgorithm)) ? getDefaultSignatureAlgorithm(signingKey) : signatureAlgorithm) + .setProvider(CertGenUtils.BC_PROV).build(signingKey); X509CertificateHolder certificateHolder = builder.build(signer); return new JcaX509CertificateConverter().getCertificate(certificateHolder); } @@ -214,7 +214,7 @@ private static String getDefaultSignatureAlgorithm(PrivateKey key) { break; default: throw new IllegalArgumentException("Unsupported algorithm : " + key.getAlgorithm() - + " for signature, allowed values for private key algorithm are [RSA, DSA, EC]"); + + " for signature, allowed values for private key algorithm are [RSA, DSA, EC]"); } return signatureAlgorithm; } @@ -229,7 +229,7 @@ private static String getDefaultSignatureAlgorithm(PrivateKey key) { * @return a certificate signing request */ static PKCS10CertificationRequest generateCSR(KeyPair keyPair, X500Principal principal, GeneralNames sanList) - throws IOException, OperatorCreationException { + throws IOException, OperatorCreationException { Objects.requireNonNull(keyPair, "Key-Pair must not be null"); Objects.requireNonNull(keyPair.getPublic(), "Public-Key must not be null"); Objects.requireNonNull(principal, "Principal must not be null"); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/CertificateGenerateTool.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateGenerateTool.java similarity index 90% rename from x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/CertificateGenerateTool.java rename to x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateGenerateTool.java index 5515978c3cab1..809e4a6d30524 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/CertificateGenerateTool.java +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateGenerateTool.java @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.xpack.core.ssl; +package org.elasticsearch.xpack.security.cli; import joptsimple.ArgumentAcceptingOptionSpec; import joptsimple.OptionSet; @@ -34,6 +34,8 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.env.Environment; +import org.elasticsearch.xpack.core.ssl.CertParsingUtils; +import org.elasticsearch.xpack.core.ssl.PemUtils; import javax.security.auth.x500.X500Principal; @@ -68,6 +70,7 @@ /** * CLI tool to make generation of certificates or certificate requests easier for users + * * @deprecated Replaced by {@link CertificateTool} */ @Deprecated @@ -81,7 +84,7 @@ public class CertificateGenerateTool extends EnvironmentAwareCommand { private static final int FILE_EXTENSION_LENGTH = 4; static final int MAX_FILENAME_LENGTH = 255 - FILE_EXTENSION_LENGTH; private static final Pattern ALLOWED_FILENAME_CHAR_PATTERN = - Pattern.compile("[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1," + MAX_FILENAME_LENGTH + "}"); + Pattern.compile("[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1," + MAX_FILENAME_LENGTH + "}"); private static final int DEFAULT_KEY_SIZE = 2048; private static final BouncyCastleProvider BC_PROV = new BouncyCastleProvider(); @@ -96,11 +99,11 @@ private static class InputFileParser { // of the parser in this class so that we can defer initialization until after logging has been initialized static { @SuppressWarnings("unchecked") final ConstructingObjectParser instanceParser = - new ConstructingObjectParser<>( - "instances", - a -> new CertificateInformation( - (String) a[0], (String) (a[1] == null ? a[0] : a[1]), - (List) a[2], (List) a[3], (List) a[4])); + new ConstructingObjectParser<>( + "instances", + a -> new CertificateInformation( + (String) a[0], (String) (a[1] == null ? a[0] : a[1]), + (List) a[2], (List) a[3], (List) a[4])); instanceParser.declareString(ConstructingObjectParser.constructorArg(), new ParseField("name")); instanceParser.declareString(ConstructingObjectParser.optionalConstructorArg(), new ParseField("filename")); instanceParser.declareStringArray(ConstructingObjectParser.optionalConstructorArg(), new ParseField("ip")); @@ -125,29 +128,29 @@ private static class InputFileParser { CertificateGenerateTool() { super(DESCRIPTION); outputPathSpec = parser.accepts("out", "path of the zip file that the output should be written to") - .withRequiredArg(); + .withRequiredArg(); csrSpec = parser.accepts("csr", "only generate certificate signing requests"); caCertPathSpec = parser.accepts("cert", "path to an existing ca certificate").availableUnless(csrSpec).withRequiredArg(); caKeyPathSpec = parser.accepts("key", "path to an existing ca private key") - .availableIf(caCertPathSpec) - .requiredIf(caCertPathSpec) - .withRequiredArg(); + .availableIf(caCertPathSpec) + .requiredIf(caCertPathSpec) + .withRequiredArg(); caPasswordSpec = parser.accepts("pass", "password for an existing ca private key or the generated ca private key") - .availableUnless(csrSpec) - .withOptionalArg(); + .availableUnless(csrSpec) + .withOptionalArg(); caDnSpec = parser.accepts("dn", "distinguished name to use for the generated ca. defaults to " + AUTO_GEN_CA_DN) - .availableUnless(caCertPathSpec) - .availableUnless(csrSpec) - .withRequiredArg(); + .availableUnless(caCertPathSpec) + .availableUnless(csrSpec) + .withRequiredArg(); keysizeSpec = parser.accepts("keysize", "size in bits of RSA keys").withRequiredArg().ofType(Integer.class); inputFileSpec = parser.accepts("in", "file containing details of the instances in yaml format").withRequiredArg(); daysSpec = parser.accepts("days", "number of days that the generated certificates are valid") - .availableUnless(csrSpec) - .withRequiredArg() - .ofType(Integer.class); + .availableUnless(csrSpec) + .withRequiredArg() + .ofType(Integer.class); p12Spec = parser.accepts("p12", "output a p12 (PKCS#12) version for each certificate/key pair, with optional password") - .availableUnless(csrSpec) - .withOptionalArg(); + .availableUnless(csrSpec) + .withOptionalArg(); } public static void main(String[] args) throws Exception { @@ -178,7 +181,7 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th p12Password = null; } CAInfo caInfo = getCAInfo(terminal, dn, caCertPathSpec.value(options), caKeyPathSpec.value(options), keyPass, prompt, env, - keysize, days); + keysize, days); Collection certificateInformations = getCertificateInformationList(terminal, inputFile); generateAndWriteSignedCertificates(outputFile, certificateInformations, caInfo, keysize, days, p12Password); } @@ -197,7 +200,7 @@ protected void printAdditionalHelp(Terminal terminal) { /** * Checks for output file in the user specified options or prompts the user for the output file * - * @param terminal terminal to communicate with a user + * @param terminal terminal to communicate with a user * @param outputPath user specified output file, may be {@code null} * @return a {@link Path} to the output file */ @@ -223,12 +226,13 @@ private static Path resolvePath(String pathStr) { /** * This method handles the collection of information about each instance that is necessary to generate a certificate. The user may * be prompted or the information can be gathered from a file - * @param terminal the terminal to use for user interaction + * + * @param terminal the terminal to use for user interaction * @param inputFile an optional file that will be used to load the instance information * @return a {@link Collection} of {@link CertificateInformation} that represents each instance */ static Collection getCertificateInformationList(Terminal terminal, String inputFile) - throws Exception { + throws Exception { if (inputFile != null) { return parseAndValidateFile(terminal, resolvePath(inputFile).toAbsolutePath()); } @@ -239,7 +243,7 @@ static Collection getCertificateInformationList(Terminal if (name.isEmpty() == false) { final boolean isNameValidFilename = Name.isValidFilename(name); String filename = terminal.readText("Enter name for directories and files " + (isNameValidFilename ? "[" + name + "]" : "") - + ": " ); + + ": "); if (filename.isEmpty() && isNameValidFilename) { filename = name; } @@ -267,7 +271,7 @@ static Collection getCertificateInformationList(Terminal } String exit = terminal.readText("Would you like to specify another instance? Press 'y' to continue entering instance " + - "information: "); + "information: "); if ("y".equals(exit) == false) { done = true; } @@ -283,7 +287,7 @@ static Collection parseAndValidateFile(Terminal terminal if (errors.size() > 0) { hasError = true; terminal.println(Terminal.Verbosity.SILENT, "Configuration for instance " + certInfo.name.originalName - + " has invalid details"); + + " has invalid details"); for (String message : errors) { terminal.println(Terminal.Verbosity.SILENT, " * " + message); } @@ -298,6 +302,7 @@ static Collection parseAndValidateFile(Terminal terminal /** * Parses the input file to retrieve the certificate information + * * @param file the file to parse * @return a collection of certificate information */ @@ -305,22 +310,23 @@ static Collection parseFile(Path file) throws Exception try (Reader reader = Files.newBufferedReader(file)) { // EMPTY is safe here because we never use namedObject XContentParser xContentParser = XContentType.YAML.xContent() - .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, reader); + .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, reader); return InputFileParser.PARSER.parse(xContentParser, new ArrayList<>(), null); } } /** * Generates certificate signing requests and writes them out to the specified file in zip format + * * @param outputFile the file to write the output to. This file must not already exist - * @param certInfo the details to use in the certificate signing requests + * @param certInfo the details to use in the certificate signing requests */ static void generateAndWriteCsrs(Path outputFile, Collection certInfo, int keysize) throws Exception { fullyWriteFile(outputFile, (outputStream, pemWriter) -> { for (CertificateInformation certificateInformation : certInfo) { KeyPair keyPair = CertGenUtils.generateKeyPair(keysize); GeneralNames sanList = getSubjectAlternativeNamesValue(certificateInformation.ipAddresses, certificateInformation.dnsNames, - certificateInformation.commonNames); + certificateInformation.commonNames); PKCS10CertificationRequest csr = CertGenUtils.generateCSR(keyPair, certificateInformation.name.x500Principal, sanList); final String dirName = certificateInformation.name.filename + "/"; @@ -347,15 +353,15 @@ static void generateAndWriteCsrs(Path outputFile, Collection certificateInformations, CAInfo caInfo, int keysize, int days, char[] pkcs12Password) throws Exception { @@ -403,9 +410,9 @@ static void generateAndWriteSignedCertificates(Path outputFile, Collection passwordReference = new AtomicReference<>(password); try { return PemUtils.readPrivateKey(resolvePath(path), () -> { @@ -682,7 +692,7 @@ static Name fromUserProvidedName(String name, String filename) { } } catch (IllegalArgumentException e) { String error = "[" + name + "] could not be converted to a valid DN\n" + e.getMessage() + "\n" - + ExceptionsHelper.stackTrace(e); + + ExceptionsHelper.stackTrace(e); return new Name(name, null, null, error); } @@ -695,15 +705,15 @@ static Name fromUserProvidedName(String name, String filename) { static boolean isValidFilename(String name) { return ALLOWED_FILENAME_CHAR_PATTERN.matcher(name).matches() - && ALLOWED_FILENAME_CHAR_PATTERN.matcher(resolvePath(name).toString()).matches() - && name.startsWith(".") == false; + && ALLOWED_FILENAME_CHAR_PATTERN.matcher(resolvePath(name).toString()).matches() + && name.startsWith(".") == false; } @Override public String toString() { return getClass().getSimpleName() - + "{original=[" + originalName + "] principal=[" + x500Principal - + "] file=[" + filename + "] err=[" + error + "]}"; + + "{original=[" + originalName + "] principal=[" + x500Principal + + "] file=[" + filename + "] err=[" + error + "]}"; } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/CertificateTool.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java similarity index 92% rename from x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/CertificateTool.java rename to x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java index dd90df4dd6a38..a966cac9109d9 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/CertificateTool.java +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.xpack.core.ssl; +package org.elasticsearch.xpack.security.cli; import joptsimple.OptionParser; import joptsimple.OptionSet; @@ -39,6 +39,8 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.env.Environment; +import org.elasticsearch.xpack.core.ssl.CertParsingUtils; +import org.elasticsearch.xpack.core.ssl.PemUtils; import javax.security.auth.x500.X500Principal; @@ -101,7 +103,7 @@ public class CertificateTool extends LoggingAwareMultiCommand { private static final int FILE_EXTENSION_LENGTH = 4; static final int MAX_FILENAME_LENGTH = 255 - FILE_EXTENSION_LENGTH; private static final Pattern ALLOWED_FILENAME_CHAR_PATTERN = - Pattern.compile("[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1," + MAX_FILENAME_LENGTH + "}"); + Pattern.compile("[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1," + MAX_FILENAME_LENGTH + "}"); private static final int DEFAULT_KEY_SIZE = 2048; /** @@ -115,11 +117,11 @@ private static class CertificateToolParser { // of the parser in this class so that we can defer initialization until after logging has been initialized static { @SuppressWarnings("unchecked") final ConstructingObjectParser instanceParser = - new ConstructingObjectParser<>( - "instances", - a -> new CertificateInformation( - (String) a[0], (String) (a[1] == null ? a[0] : a[1]), - (List) a[2], (List) a[3], (List) a[4])); + new ConstructingObjectParser<>( + "instances", + a -> new CertificateInformation( + (String) a[0], (String) (a[1] == null ? a[0] : a[1]), + (List) a[2], (List) a[3], (List) a[4])); instanceParser.declareString(ConstructingObjectParser.constructorArg(), new ParseField("name")); instanceParser.declareString(ConstructingObjectParser.optionalConstructorArg(), new ParseField("filename")); instanceParser.declareStringArray(ConstructingObjectParser.optionalConstructorArg(), new ParseField("ip")); @@ -144,28 +146,28 @@ public static void main(String[] args) throws Exception { static final String INTRO_TEXT = "This tool assists you in the generation of X.509 certificates and certificate\n" + - "signing requests for use with SSL/TLS in the Elastic stack."; + "signing requests for use with SSL/TLS in the Elastic stack."; static final String INSTANCE_EXPLANATION = - " * An instance is any piece of the Elastic Stack that requires a SSL certificate.\n" + - " Depending on your configuration, Elasticsearch, Logstash, Kibana, and Beats\n" + - " may all require a certificate and private key.\n" + - " * The minimum required value for each instance is a name. This can simply be the\n" + - " hostname, which will be used as the Common Name of the certificate. A full\n" + - " distinguished name may also be used.\n" + - " * A filename value may be required for each instance. This is necessary when the\n" + - " name would result in an invalid file or directory name. The name provided here\n" + - " is used as the directory name (within the zip) and the prefix for the key and\n" + - " certificate files. The filename is required if you are prompted and the name\n" + - " is not displayed in the prompt.\n" + - " * IP addresses and DNS names are optional. Multiple values can be specified as a\n" + - " comma separated string. If no IP addresses or DNS names are provided, you may\n" + - " disable hostname verification in your SSL configuration."; + " * An instance is any piece of the Elastic Stack that requires a SSL certificate.\n" + + " Depending on your configuration, Elasticsearch, Logstash, Kibana, and Beats\n" + + " may all require a certificate and private key.\n" + + " * The minimum required value for each instance is a name. This can simply be the\n" + + " hostname, which will be used as the Common Name of the certificate. A full\n" + + " distinguished name may also be used.\n" + + " * A filename value may be required for each instance. This is necessary when the\n" + + " name would result in an invalid file or directory name. The name provided here\n" + + " is used as the directory name (within the zip) and the prefix for the key and\n" + + " certificate files. The filename is required if you are prompted and the name\n" + + " is not displayed in the prompt.\n" + + " * IP addresses and DNS names are optional. Multiple values can be specified as a\n" + + " comma separated string. If no IP addresses or DNS names are provided, you may\n" + + " disable hostname verification in your SSL configuration."; static final String CA_EXPLANATION = - " * All certificates generated by this tool will be signed by a certificate authority (CA).\n" + - " * The tool can automatically generate a new CA for you, or you can provide your own with the\n" + - " -ca or -ca-cert command line options."; + " * All certificates generated by this tool will be signed by a certificate authority (CA).\n" + + " * The tool can automatically generate a new CA for you, or you can provide your own with the\n" + + " -ca or -ca-cert command line options."; abstract static class CertificateCommand extends EnvironmentAwareCommand { @@ -202,32 +204,32 @@ abstract static class CertificateCommand extends EnvironmentAwareCommand { final void acceptCertificateGenerationOptions() { pemFormatSpec = parser.accepts("pem", "output certificates and keys in PEM format instead of PKCS#12"); daysSpec = parser.accepts("days", "number of days that the generated certificates are valid") - .withRequiredArg().ofType(Integer.class); + .withRequiredArg().ofType(Integer.class); } final void acceptsCertificateAuthority() { caPkcs12PathSpec = parser.accepts("ca", "path to an existing ca key pair (in PKCS#12 format)").withRequiredArg(); caCertPathSpec = parser.accepts("ca-cert", "path to an existing ca certificate") - .availableUnless(caPkcs12PathSpec) - .withRequiredArg(); + .availableUnless(caPkcs12PathSpec) + .withRequiredArg(); caKeyPathSpec = parser.accepts("ca-key", "path to an existing ca private key") - .availableIf(caCertPathSpec) - .requiredIf(caCertPathSpec) - .withRequiredArg(); + .availableIf(caCertPathSpec) + .requiredIf(caCertPathSpec) + .withRequiredArg(); keepCaKeySpec = parser.accepts("keep-ca-key", "retain the CA private key for future use") - .availableUnless(caPkcs12PathSpec) - .availableUnless(caCertPathSpec); + .availableUnless(caPkcs12PathSpec) + .availableUnless(caCertPathSpec); caPasswordSpec = parser.accepts("ca-pass", "password for an existing ca private key or the generated ca private key") - .withOptionalArg(); + .withOptionalArg(); acceptsCertificateAuthorityName(); } void acceptsCertificateAuthorityName() { OptionSpecBuilder builder = parser.accepts("ca-dn", - "distinguished name to use for the generated ca. defaults to " + AUTO_GEN_CA_DN); + "distinguished name to use for the generated ca. defaults to " + AUTO_GEN_CA_DN); if (caPkcs12PathSpec != null) { builder = builder.availableUnless(caPkcs12PathSpec); } @@ -336,11 +338,11 @@ private CAInfo loadPkcs12CA(Terminal terminal, OptionSet options, Environment en char[] passwordOption = getChars(caPasswordSpec.value(options)); Map keys = withPassword("CA (" + path + ")", passwordOption, - terminal, password -> CertParsingUtils.readPkcs12KeyPairs(path, password, a -> password)); + terminal, password -> CertParsingUtils.readPkcs12KeyPairs(path, password, a -> password)); if (keys.size() != 1) { throw new IllegalArgumentException("expected a single key in file [" + path.toAbsolutePath() + "] but found [" + - keys.size() + "]"); + keys.size() + "]"); } final Map.Entry pair = keys.entrySet().iterator().next(); return new CAInfo((X509Certificate) pair.getKey(), (PrivateKey) pair.getValue()); @@ -358,7 +360,7 @@ private CAInfo loadPemCA(Terminal terminal, OptionSet options, Environment env) Certificate[] certificates = CertParsingUtils.readCertificates(Collections.singletonList(resolvedCaCertPath), env); if (certificates.length != 1) { throw new IllegalArgumentException("expected a single certificate in file [" + resolvedCaCertPath + "] but found [" + - certificates.length + "]"); + certificates.length + "]"); } X509Certificate caCert = (X509Certificate) certificates[0]; PrivateKey privateKey = readPrivateKey(key, getChars(password), terminal); @@ -391,7 +393,7 @@ CAInfo generateCA(Terminal terminal, OptionSet options) throws Exception { * @return a {@link Collection} of {@link CertificateInformation} that represents each instance */ Collection getCertificateInformationList(Terminal terminal, OptionSet options) - throws Exception { + throws Exception { final Path input = resolvePath(options, inputFileSpec); if (input != null) { return parseAndValidateFile(terminal, input.toAbsolutePath()); @@ -456,7 +458,7 @@ static Collection readMultipleCertificateInformation(Ter } String exit = terminal.readText("Would you like to specify another instance? Press 'y' to continue entering instance " + - "information: "); + "information: "); if ("y".equals(exit) == false) { done = true; } @@ -468,7 +470,7 @@ private static String requestFileName(Terminal terminal, String certName) { final boolean isNameValidFilename = Name.isValidFilename(certName); while (true) { String filename = terminal.readText("Enter name for directories and files of " + certName + - (isNameValidFilename ? " [" + certName + "]" : "") + ": "); + (isNameValidFilename ? " [" + certName + "]" : "") + ": "); if (filename.isEmpty() && isNameValidFilename) { return certName; } @@ -490,7 +492,7 @@ private static String requestFileName(Terminal terminal, String certName) { * @param includeKey if true, write the CA key in PEM format */ static void writeCAInfo(ZipOutputStream outputStream, JcaPEMWriter pemWriter, CAInfo info, boolean includeKey) - throws Exception { + throws Exception { final String caDirName = createCaDirectory(outputStream); outputStream.putNextEntry(new ZipEntry(caDirName + "ca.crt")); pemWriter.writeObject(info.certAndKey.cert); @@ -546,7 +548,7 @@ static void writePkcs12(String fileName, OutputStream output, String alias, Cert pkcs12.load(null); withPassword(fileName, password, terminal, p12Password -> { if (isAscii(p12Password)) { - pkcs12.setKeyEntry(alias, pair.key, p12Password, new Certificate[] { pair.cert }); + pkcs12.setKeyEntry(alias, pair.key, p12Password, new Certificate[]{pair.cert}); if (caCert != null) { pkcs12.setCertificateEntry("ca", caCert); } @@ -574,7 +576,7 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th terminal.println("The 'csr' mode generates certificate signing requests that can be sent to"); terminal.println("a trusted certificate authority"); terminal.println(" * By default, this generates a single CSR for a single instance."); - terminal.println(" * You can use the '-multiple' option to generate CSRs for multiple" ); + terminal.println(" * You can use the '-multiple' option to generate CSRs for multiple"); terminal.println(" instances, each with their own private key."); terminal.println(" * The '-in' option allows for the CSR generation to be automated"); terminal.println(" by describing the details of each instance in a YAML file"); @@ -616,7 +618,7 @@ void generateAndWriteCsrs(Path output, int keySize, Collection certs, CAInfo caInfo, Terminal terminal) - throws Exception { + throws Exception { checkDirectory(output, terminal); @@ -805,7 +807,7 @@ void generateAndWriteSignedCertificates(Path output, boolean writeZipFile, Optio final String fileName = entryBase + ".p12"; outputStream.putNextEntry(new ZipEntry(fileName)); writePkcs12(fileName, outputStream, certificateInformation.name.originalName, pair, caInfo.certAndKey.cert, - outputPassword, terminal); + outputPassword, terminal); outputStream.closeEntry(); } } @@ -815,7 +817,7 @@ void generateAndWriteSignedCertificates(Path output, boolean writeZipFile, Optio CertificateInformation certificateInformation = certs.iterator().next(); CertificateAndKey pair = generateCertificateAndKey(certificateInformation, caInfo, keySize, days); fullyWriteFile(output, stream -> writePkcs12(output.getFileName().toString(), stream, - certificateInformation.name.originalName, pair, caInfo.certAndKey.cert, outputPassword, terminal)); + certificateInformation.name.originalName, pair, caInfo.certAndKey.cert, outputPassword, terminal)); } } @@ -823,9 +825,9 @@ private CertificateAndKey generateCertificateAndKey(CertificateInformation certi int keySize, int days) throws Exception { KeyPair keyPair = CertGenUtils.generateKeyPair(keySize); Certificate certificate = CertGenUtils.generateSignedCertificate(certificateInformation.name.x500Principal, - getSubjectAlternativeNamesValue(certificateInformation.ipAddresses, certificateInformation.dnsNames, - certificateInformation.commonNames), - keyPair, caInfo.certAndKey.cert, caInfo.certAndKey.key, days); + getSubjectAlternativeNamesValue(certificateInformation.ipAddresses, certificateInformation.dnsNames, + certificateInformation.commonNames), + keyPair, caInfo.certAndKey.cert, caInfo.certAndKey.key, days); return new CertificateAndKey((X509Certificate) certificate, keyPair.getPrivate()); } @@ -872,7 +874,7 @@ private void writeCertificateAuthority(Path output, CAInfo caInfo, boolean write } else { final String fileName = output.getFileName().toString(); fullyWriteFile(output, outputStream -> - writePkcs12(fileName, outputStream, "ca", caInfo.certAndKey, null, caInfo.password, terminal)); + writePkcs12(fileName, outputStream, "ca", caInfo.certAndKey, null, caInfo.password, terminal)); } } } @@ -912,7 +914,7 @@ static Collection parseFile(Path file) throws Exception try (Reader reader = Files.newBufferedReader(file)) { // EMPTY is safe here because we never use namedObject XContentParser xContentParser = XContentType.YAML.xContent() - .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, reader); + .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, reader); return CertificateToolParser.PARSER.parse(xContentParser, new ArrayList<>(), null); } } @@ -1015,7 +1017,7 @@ private static void fullyWriteFile(Path file, CheckedConsumer passwordReference = new AtomicReference<>(password); try { return PemUtils.readPrivateKey(path, () -> { @@ -1125,7 +1127,7 @@ static Name fromUserProvidedName(String name, String filename) { } } catch (IllegalArgumentException e) { String error = "[" + name + "] could not be converted to a valid DN\n" + e.getMessage() + "\n" - + ExceptionsHelper.stackTrace(e); + + ExceptionsHelper.stackTrace(e); return new Name(name, null, null, error); } @@ -1138,15 +1140,15 @@ static Name fromUserProvidedName(String name, String filename) { static boolean isValidFilename(String name) { return ALLOWED_FILENAME_CHAR_PATTERN.matcher(name).matches() - && ALLOWED_FILENAME_CHAR_PATTERN.matcher(resolvePath(name).toString()).matches() - && name.startsWith(".") == false; + && ALLOWED_FILENAME_CHAR_PATTERN.matcher(resolvePath(name).toString()).matches() + && name.startsWith(".") == false; } @Override public String toString() { return getClass().getSimpleName() - + "{original=[" + originalName + "] principal=[" + x500Principal - + "] file=[" + filename + "] err=[" + error + "]}"; + + "{original=[" + originalName + "] principal=[" + x500Principal + + "] file=[" + filename + "] err=[" + error + "]}"; } } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/CertGenUtilsTests.java b/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/CertGenUtilsTests.java similarity index 97% rename from x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/CertGenUtilsTests.java rename to x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/CertGenUtilsTests.java index 20259144b420b..bb1ed014b9c17 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/CertGenUtilsTests.java +++ b/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/CertGenUtilsTests.java @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.xpack.core.ssl; +package org.elasticsearch.xpack.security.cli; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralNames; @@ -12,6 +12,7 @@ import org.elasticsearch.common.network.InetAddresses; import org.elasticsearch.common.network.NetworkAddress; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.security.cli.CertGenUtils; import org.junit.BeforeClass; import java.math.BigInteger; diff --git a/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/core/ssl/CertificateGenerateToolTests.java b/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/CertificateGenerateToolTests.java similarity index 98% rename from x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/core/ssl/CertificateGenerateToolTests.java rename to x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/CertificateGenerateToolTests.java index dde0b7645df13..91fd55933c554 100644 --- a/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/core/ssl/CertificateGenerateToolTests.java +++ b/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/CertificateGenerateToolTests.java @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.xpack.core.ssl; +package org.elasticsearch.xpack.security.cli; import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; @@ -33,9 +33,11 @@ import org.elasticsearch.env.TestEnvironment; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.SecuritySettingsSourceField; -import org.elasticsearch.xpack.core.ssl.CertificateGenerateTool.CAInfo; -import org.elasticsearch.xpack.core.ssl.CertificateGenerateTool.CertificateInformation; -import org.elasticsearch.xpack.core.ssl.CertificateGenerateTool.Name; +import org.elasticsearch.xpack.security.cli.CertificateGenerateTool.CAInfo; +import org.elasticsearch.xpack.security.cli.CertificateGenerateTool.CertificateInformation; +import org.elasticsearch.xpack.security.cli.CertificateGenerateTool.Name; +import org.elasticsearch.xpack.core.ssl.CertParsingUtils; +import org.elasticsearch.xpack.core.ssl.PemUtils; import org.hamcrest.Matchers; import org.junit.After; import org.junit.BeforeClass; @@ -359,8 +361,8 @@ public void testGeneratingSignedCertificates() throws Exception { public void testGetCAInfo() throws Exception { Environment env = TestEnvironment.newEnvironment(Settings.builder().put("path.home", createTempDir()).build()); - Path testNodeCertPath = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt"); - Path testNodeKeyPath = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.pem"); + Path testNodeCertPath = getDataPath("/org/elasticsearch/xpack/security/cli/testnode.crt"); + Path testNodeKeyPath = getDataPath("/org/elasticsearch/xpack/security/cli/testnode.pem"); final boolean passwordPrompt = randomBoolean(); MockTerminal terminal = new MockTerminal(); if (passwordPrompt) { diff --git a/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/core/ssl/CertificateToolTests.java b/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/CertificateToolTests.java similarity index 98% rename from x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/core/ssl/CertificateToolTests.java rename to x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/CertificateToolTests.java index 706d5dbab5f5a..9e970ea559ad7 100644 --- a/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/core/ssl/CertificateToolTests.java +++ b/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/CertificateToolTests.java @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.xpack.core.ssl; +package org.elasticsearch.xpack.security.cli; import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; @@ -39,12 +39,14 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.SecuritySettingsSourceField; import org.elasticsearch.test.TestMatchers; -import org.elasticsearch.xpack.core.ssl.CertificateTool.CAInfo; -import org.elasticsearch.xpack.core.ssl.CertificateTool.CertificateAuthorityCommand; -import org.elasticsearch.xpack.core.ssl.CertificateTool.CertificateCommand; -import org.elasticsearch.xpack.core.ssl.CertificateTool.CertificateInformation; -import org.elasticsearch.xpack.core.ssl.CertificateTool.GenerateCertificateCommand; -import org.elasticsearch.xpack.core.ssl.CertificateTool.Name; +import org.elasticsearch.xpack.security.cli.CertificateTool.CAInfo; +import org.elasticsearch.xpack.security.cli.CertificateTool.CertificateAuthorityCommand; +import org.elasticsearch.xpack.security.cli.CertificateTool.CertificateCommand; +import org.elasticsearch.xpack.security.cli.CertificateTool.CertificateInformation; +import org.elasticsearch.xpack.security.cli.CertificateTool.GenerateCertificateCommand; +import org.elasticsearch.xpack.security.cli.CertificateTool.Name; +import org.elasticsearch.xpack.core.ssl.CertParsingUtils; +import org.elasticsearch.xpack.core.ssl.PemUtils; import org.hamcrest.Matchers; import org.junit.After; import org.junit.BeforeClass; @@ -387,8 +389,8 @@ public void testGeneratingSignedPemCertificates() throws Exception { public void testGetCAInfo() throws Exception { Environment env = TestEnvironment.newEnvironment(Settings.builder().put("path.home", createTempDir()).build()); - Path testNodeCertPath = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt"); - Path testNodeKeyPath = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.pem"); + Path testNodeCertPath = getDataPath("/org/elasticsearch/xpack/security/cli/testnode.crt"); + Path testNodeKeyPath = getDataPath("/org/elasticsearch/xpack/security/cli/testnode.pem"); final boolean passwordPrompt = randomBoolean(); MockTerminal terminal = new MockTerminal(); if (passwordPrompt) { diff --git a/x-pack/plugin/security/cli/src/test/resources/org/elasticsearch/xpack/security/cli/testnode.crt b/x-pack/plugin/security/cli/src/test/resources/org/elasticsearch/xpack/security/cli/testnode.crt new file mode 100644 index 0000000000000..08c160bcea5ff --- /dev/null +++ b/x-pack/plugin/security/cli/src/test/resources/org/elasticsearch/xpack/security/cli/testnode.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID0zCCArugAwIBAgIJALi5bDfjMszLMA0GCSqGSIb3DQEBCwUAMEgxDDAKBgNV +BAoTA29yZzEWMBQGA1UECxMNZWxhc3RpY3NlYXJjaDEgMB4GA1UEAxMXRWxhc3Rp +Y3NlYXJjaCBUZXN0IE5vZGUwHhcNMTUwOTIzMTg1MjU3WhcNMTkwOTIyMTg1MjU3 +WjBIMQwwCgYDVQQKEwNvcmcxFjAUBgNVBAsTDWVsYXN0aWNzZWFyY2gxIDAeBgNV +BAMTF0VsYXN0aWNzZWFyY2ggVGVzdCBOb2RlMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA3rGZ1QbsW0+MuyrSLmMfDFKtLBkIFW8V0gRuurFg1PUKKNR1 +Mq2tMVwjjYETAU/UY0iKZOzjgvYPKhDTYBTte/WHR1ZK4CYVv7TQX/gtFQG/ge/c +7u0sLch9p7fbd+/HZiLS/rBEZDIohvgUvzvnA8+OIYnw4kuxKo/5iboAIS41klMg +/lATm8V71LMY68inht71/ZkQoAHKgcR9z4yNYvQ1WqKG8DG8KROXltll3sTrKbl5 +zJhn660es/1ZnR6nvwt6xnSTl/mNHMjkfv1bs4rJ/py3qPxicdoSIn/KyojUcgHV +F38fuAy2CQTdjVG5fWj9iz+mQvLm3+qsIYQdFwIDAQABo4G/MIG8MAkGA1UdEwQC +MAAwHQYDVR0OBBYEFEMMWLWQi/g83PzlHYqAVnty5L7HMIGPBgNVHREEgYcwgYSC +CWxvY2FsaG9zdIIVbG9jYWxob3N0LmxvY2FsZG9tYWluggpsb2NhbGhvc3Q0ghds +b2NhbGhvc3Q0LmxvY2FsZG9tYWluNIIKbG9jYWxob3N0NoIXbG9jYWxob3N0Ni5s +b2NhbGRvbWFpbjaHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQEL +BQADggEBAMjGGXT8Nt1tbl2GkiKtmiuGE2Ej66YuZ37WSJViaRNDVHLlg87TCcHe +k2rdO+6sFqQbbzEfwQ05T7xGmVu7tm54HwKMRugoQ3wct0bQC5wEWYN+oMDvSyO6 +M28mZwWb4VtR2IRyWP+ve5DHwTM9mxWa6rBlGzsQqH6YkJpZojzqk/mQTug+Y8aE +mVoqRIPMHq9ob+S9qd5lp09+MtYpwPfTPx/NN+xMEooXWW/ARfpGhWPkg/FuCu4z +1tFmCqHgNcWirzMm3dQpF78muE9ng6OB2MXQwL4VgnVkxmlZNHbkR2v/t8MyZJxC +y4g6cTMM3S/UMt5/+aIB2JAuMKyuD+A= +-----END CERTIFICATE----- diff --git a/x-pack/plugin/security/cli/src/test/resources/org/elasticsearch/xpack/security/cli/testnode.pem b/x-pack/plugin/security/cli/src/test/resources/org/elasticsearch/xpack/security/cli/testnode.pem new file mode 100644 index 0000000000000..5a67e1033440d --- /dev/null +++ b/x-pack/plugin/security/cli/src/test/resources/org/elasticsearch/xpack/security/cli/testnode.pem @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,9D867F7E0C94D013 + +dVoVCjPeg1wgS7rVtOvGfQcrZyLkx393aWRnFq45tbjKBVuITtJ9vI7o4QXOV/15 +Gnb6WhXGIdWrzsxEAd46K6hIuNSISd4Emsx6c2Q5hTqWXXfexbOZBNfTtXtdJPnJ +1jAaikhtztLo3JSLTKNY5sNxd+XbaQyYVUWvueK6zOaIIMETvB+VPVFd9i1ROibk +Sgdtyj01KjkoalifqK/tA0CIYNKL0S6/eoK3UhAlpIprlpV+cnXa940C6bjLeJPt +PMAGGp5RrplxSgrSerw3I9DOWkHGtpqzIka3XneNUXJP8k4HUJ+aZkGH2ZILKS8d +4KMIb+KZSpHEGn+6uGccWLtZZmAjWJrDw56JbQtSHdRYLBRSOjLbTvQoPu/2Hpli +7HOxbotlvjptMunncq5aqK57SHA1dh0cwF7J3LUmGFJ67eoz+VV3b5qMn4MopSeI +mS16Ydd3nGpjSrln/elM0CQxqWfcOAXRZpDpFUQoXcBrLVzvz2DBl/0CrTRLhgzi +CO+5/IVcBWRlYpRNGgjjP7q0j6URID3jk5J06fYQXmBiwQT5j+GZqqzpMCJ9mIy2 +1O9SN1hebJnIcEU+E0njn/MGjlYdPywhaCy8pqElp6Q8TUEJpwLRFO/owCoBet/n +ZmCXUjfCGhc1pWHufFcDEQ6xMgEWWY/tdwCZeSU7EhErTjCbfupg+55A5fpDml0m +3wH4CFcuRjlqyx6Ywixm1ATeitDtJl5HQTw6b8OtEXwSgRmZ0eSqSRVk9QbVS7gu +IpQe09/Zimb5HzjZqZ3fdqHlcW4xax8hyJeyIvF5ZJ57eY8CBvu/wP2GDn26QnvF +xQqdfDbq1H4JmpwUHpbFwBoQK4Q6WFd1z4EA9bRQeo3H9PoqoOwMDjzajwLRF7b7 +q6tYH/n9PyHwdf1c4fFwgSmL1toXGfKlA9hjIaLsRSDD6srT5EdUk78bsnddwI51 +tu7C7P4JG+h1VdRNMNTlqtileWsIE7Nn2A1OkcUxZdF5mamENpDpJcHePLto6c8q +FKiwyFMsxhgsj6HK2HqO+UA4sX5Ni4oHwiPmb//EZLn045M5i1AN26KosJmb8++D +sgR5reWRy+UqJCTYblVg+7Dx++ggUnfxVyQEsWmw5r5f4KU5wXBkvoVMGtPNa9DE +n/uLtObD1qkNL38pRsr2OGRchYCgEoKGqEISBP4knfGXLOlWiW/246j9QzI97r1u +tvy7fKg28G7AUz9l6bpewsPHefBUeRQeieP9eJINaEpxkF/w2RpKDLpQjWxwDDOM +s+D0mrBMJve17AmJ8rMw6dIQPZYNZ88/jz1uQuUwQ2YlbmtZbCG81k9YMFGEU9XS +cyhJxj8hvYnt2PR5Z9/cJPyWOs0m/ufOeeQQ8SnU/lzmrQnpzUd2Z6p5i/B7LdRP +n1kX+l1qynuPnjvBz4nJQE0p6nzW8RyCDSniC9mtYtZmhgC8icqxgbvS7uEOBIYJ +NbK+0bEETTO34iY/JVTIqLOw3iQZYMeUpxpj6Phgx/oooxMTquMecPKNgeVtaBst +qjTNPX0ti1/HYpZqzYi8SV8YjHSJWCVMsZjKPr3W/HIcCKqYoIfgzi83Ha2KMQx6 +-----END RSA PRIVATE KEY----- diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-certgen b/x-pack/plugin/security/src/main/bin/elasticsearch-certgen index 4a192ac3b1633..8e88e845e0242 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-certgen +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-certgen @@ -4,7 +4,8 @@ # or more contributor license agreements. Licensed under the Elastic License; # you may not use this file except in compliance with the Elastic License. -ES_MAIN_CLASS=org.elasticsearch.xpack.core.ssl.CertificateGenerateTool \ +ES_MAIN_CLASS=org.elasticsearch.xpack.security.cli.CertificateGenerateTool \ ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ + ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli \ "`dirname "$0"`"/elasticsearch-cli \ "$@" diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-certgen.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-certgen.bat index b5842b57b168c..bb303f740e5c3 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-certgen.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-certgen.bat @@ -7,8 +7,9 @@ rem you may not use this file except in compliance with the Elastic License. setlocal enabledelayedexpansion setlocal enableextensions -set ES_MAIN_CLASS=org.elasticsearch.xpack.core.ssl.CertificateGenerateTool +set ES_MAIN_CLASS=org.elasticsearch.xpack.security.cli.CertificateGenerateTool set ES_ADDITIONAL_SOURCES=x-pack-env;x-pack-security-env +set ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || exit /b 1 diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-certutil b/x-pack/plugin/security/src/main/bin/elasticsearch-certutil index a13be812f0bc4..6d94344949b9b 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-certutil +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-certutil @@ -4,7 +4,8 @@ # or more contributor license agreements. Licensed under the Elastic License; # you may not use this file except in compliance with the Elastic License. -ES_MAIN_CLASS=org.elasticsearch.xpack.core.ssl.CertificateTool \ +ES_MAIN_CLASS=org.elasticsearch.xpack.security.cli.CertificateTool \ ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ + ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli \ "`dirname "$0"`"/elasticsearch-cli \ "$@" diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-certutil.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-certutil.bat index 2e397190f23a9..34f595824f82d 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-certutil.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-certutil.bat @@ -7,8 +7,9 @@ rem you may not use this file except in compliance with the Elastic License. setlocal enabledelayedexpansion setlocal enableextensions -set ES_MAIN_CLASS=org.elasticsearch.xpack.core.ssl.CertificateTool +set ES_MAIN_CLASS=org.elasticsearch.xpack.security.cli.CertificateTool set ES_ADDITIONAL_SOURCES=x-pack-env;x-pack-security-env +set ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || exit /b 1