Skip to content

Commit

Permalink
X.509 certificates are generated dynamically during tests. (#2096)
Browse files Browse the repository at this point in the history
* X.509 certificates are generated dynamically during tests.

Signed-off-by: Lukasz Soszynski <[email protected]>
Signed-off-by: Lukasz Soszynski <[email protected]>
  • Loading branch information
lukasz-soszynski-eliatra authored Sep 28, 2022
1 parent 2a00e2b commit 00f152b
Show file tree
Hide file tree
Showing 11 changed files with 1,048 additions and 182 deletions.
5 changes: 4 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ dependencies {
implementation 'com.google.guava:guava:30.0-jre'
implementation 'org.greenrobot:eventbus:3.2.0'
implementation 'commons-cli:commons-cli:1.3.1'
implementation 'org.bouncycastle:bcprov-jdk15on:1.67'
implementation "org.bouncycastle:bcprov-jdk15on:${versions.bouncycastle}"
implementation 'org.ldaptive:ldaptive:1.2.3'
implementation 'org.apache.httpcomponents:httpclient-cache:4.5.13'
implementation 'io.jsonwebtoken:jjwt-api:0.10.8'
Expand Down Expand Up @@ -328,6 +328,7 @@ dependencies {

testImplementation 'org.apache.camel:camel-xmlsecurity:3.14.2'


implementation 'net.shibboleth.utilities:java-support:7.5.1'
implementation 'org.opensaml:opensaml-core:3.4.5'
implementation 'org.opensaml:opensaml-security-impl:3.4.5'
Expand Down Expand Up @@ -427,6 +428,8 @@ dependencies {
integrationTestImplementation 'org.apache.logging.log4j:log4j-jul:2.17.1'
integrationTestImplementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.17.1'
integrationTestImplementation 'org.hamcrest:hamcrest:2.2'
integrationTestImplementation "org.bouncycastle:bcpkix-jdk15on:${versions.bouncycastle}"
integrationTestImplementation "org.bouncycastle:bcutil-jdk15on:${versions.bouncycastle}"
}

jar {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
*/
/*
* Copyright 2021 floragunn GmbH
*
* 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 org.opensearch.test.framework.certificate;

import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.spec.ECGenParameterSpec;
import java.util.function.Supplier;

import com.google.common.base.Strings;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import static java.util.Objects.requireNonNull;

/**
* The class determines cryptographic algorithms used for certificate creation. To create certificate it is necessary to generate public
* and private key, so-called key pair. The class encapsulates the process of key pairs creation ({@link #generateKeyPair()}),
* thus determines algorithm used for key pair creation. Additionally, class defines also algorithms used to digitally sign a certificate.
* Please see {@link #getSignatureAlgorithmName()}
*/
class AlgorithmKit {

private static final Logger log = LogManager.getLogger(AlgorithmKit.class);
public static final String SIGNATURE_ALGORITHM_SHA_256_WITH_RSA = "SHA256withRSA";
public static final String SIGNATURE_ALGORITHM_SHA_256_WITH_ECDSA = "SHA256withECDSA";

private final String signatureAlgorithmName;
private final Supplier<KeyPair> keyPairSupplier;

private AlgorithmKit(String signatureAlgorithmName, Supplier<KeyPair> keyPairSupplier) {
notEmptyAlgorithmName(signatureAlgorithmName);
this.signatureAlgorithmName = signatureAlgorithmName;
this.keyPairSupplier = requireNonNull(keyPairSupplier, "Key pair supplier is required.");
}

private static void notEmptyAlgorithmName(String signatureAlgorithmName) {
if(Strings.isNullOrEmpty(signatureAlgorithmName)){
throw new RuntimeException("Algorithm name is required.");
}
}

/**
* Static factory method. ECDSA algorithm used for key pair creation. Signature algorithm is defined by field
* {@link #SIGNATURE_ALGORITHM_SHA_256_WITH_ECDSA}
*
* @param securityProvider determines cryptographic algorithm implementation
* @param ellipticCurve
* @return new instance of class {@link AlgorithmKit}
*/
public static AlgorithmKit ecdsaSha256withEcdsa(Provider securityProvider, String ellipticCurve) {
notEmptyAlgorithmName(ellipticCurve);
Supplier<KeyPair> supplier = ecdsaKeyPairSupplier(requireNonNull(securityProvider, "Security provider is required"), ellipticCurve);
return new AlgorithmKit(SIGNATURE_ALGORITHM_SHA_256_WITH_ECDSA, supplier);
}

/**
* Static factory method. It creates object of {@link AlgorithmKit} which enforces usage of RSA algorithm for key pair generation.
* Signature algorithm is defined by {@link #SIGNATURE_ALGORITHM_SHA_256_WITH_RSA}
*
* @param securityProvider determines cryptographic algorithm implementation
* @param keySize defines key size for RSA algorithm
* @return new instance of class {@link AlgorithmKit}
*/
public static AlgorithmKit rsaSha256withRsa(Provider securityProvider, int keySize) {
positiveKeySize(keySize);
Supplier<KeyPair> supplier = rsaKeyPairSupplier(securityProvider, keySize);
return new AlgorithmKit(SIGNATURE_ALGORITHM_SHA_256_WITH_RSA, supplier);
}

private static void positiveKeySize(int keySize) {
if(keySize <= 0) {
throw new RuntimeException("Key size must be a positive integer value, provided: " + keySize);
}
}

/**
* It determines algorithm used for digital signature
* @return algorithm name
*/
public String getSignatureAlgorithmName(){
return signatureAlgorithmName;
}

/**
* It creates new private and public key pair
* @return new pair of keys
*/
public KeyPair generateKeyPair(){
return keyPairSupplier.get();
}
private static Supplier<KeyPair> rsaKeyPairSupplier(Provider securityProvider, int keySize) {
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", securityProvider);
log.info("Initialize key pair generator with keySize: {}", keySize);
generator.initialize(keySize);
return generator::generateKeyPair;
} catch (NoSuchAlgorithmException e) {
String message = "Error while initializing RSA asymmetric key generator.";
log.error(message, e);
throw new RuntimeException(message, e);
}
}

private static Supplier<KeyPair> ecdsaKeyPairSupplier(Provider securityProvider, String ellipticCurve) {
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance("EC", securityProvider);
log.info("Initialize key pair generator with elliptic curve: {}", ellipticCurve);
ECGenParameterSpec ecsp = new ECGenParameterSpec(ellipticCurve);
generator.initialize(ecsp);
return generator::generateKeyPair;
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
String message = "Error while initializing ECDSA asymmetric key generator.";
log.error(message, e);
throw new RuntimeException(message, e);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
*/
/*
* Copyright 2021 floragunn GmbH
*
* 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 org.opensearch.test.framework.certificate;

import java.security.KeyPair;

import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.X509CertificateHolder;

/**
* The class contains all data related to Certificate including private key which is considered to be a secret.
*/
class CertificateData {

private final X509CertificateHolder certificate;
private final KeyPair keyPair;

public CertificateData(X509CertificateHolder certificate, KeyPair keyPair) {
this.certificate = certificate;
this.keyPair = keyPair;
}

/**
* The method returns X.509 certificate encoded in PEM format. PEM format is defined by
* <a href="https://www.rfc-editor.org/rfc/rfc1421.txt">RFC 1421</a>.
* @return Certificate in PEM format
*/
public String certificateInPemFormat() {
return PemConverter.toPem(certificate);
}

/**
* It returns the private key associated with certificate encoded in PEM format. PEM format is defined by
* <a href="https://www.rfc-editor.org/rfc/rfc1421.txt">RFC 1421</a>.
* @param privateKeyPassword password used for private key encryption. <code>null</code> for unencrypted key.
* @return private key encoded in PEM format
*/
public String privateKeyInPemFormat(String privateKeyPassword) {
return PemConverter.toPem(keyPair.getPrivate(), privateKeyPassword);
}

X500Name getCertificateSubject() {
return certificate.getSubject();
}

KeyPair getKeyPair() {
return keyPair;
}
}
Loading

0 comments on commit 00f152b

Please sign in to comment.