Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions spring-cloud-commons-dependencies/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<description>Spring Cloud Commons Dependencies</description>
<properties>
<spring-security-rsa.version>1.0.9.RELEASE</spring-security-rsa.version>
<bouncycastle.version>1.64</bouncycastle.version>
</properties>
<dependencyManagement>
<dependencies>
Expand Down Expand Up @@ -59,6 +60,11 @@
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<profiles>
Expand Down
12 changes: 12 additions & 0 deletions spring-cloud-test-support/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,22 @@
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright 2018-2019 the original author or authors.
*
* 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
*
* https://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.springframework.cloud.test;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.SocketUtils;

public class AppRunner implements AutoCloseable {

private Class<?> appClass;

private Map<String, String> props;

private ConfigurableApplicationContext app;

public AppRunner(Class<?> appClass) {
this.appClass = appClass;
props = new LinkedHashMap<>();
}

public void property(String key, String value) {
props.put(key, value);
}

public void start() {
if (app == null) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(appClass);
builder.properties("spring.jmx.enabled=false");
builder.properties(String.format("server.port=%d", availabeTcpPort()));
builder.properties(props());

app = builder.build().run();
}
}

private int availabeTcpPort() {
return SocketUtils.findAvailableTcpPort();
}

private String[] props() {
List<String> result = new ArrayList<>();

for (String key : props.keySet()) {
String value = props.get(key);
result.add(String.format("%s=%s", key, value));
}

return result.toArray(new String[0]);
}

public void stop() {
if (app != null) {
app.stop();
app = null;
}
}

public ConfigurableApplicationContext app() {
return app;
}

public String getProperty(String key) {
return app.getEnvironment().getProperty(key);
}

public <T> T getBean(Class<T> type) {
return app.getBean(type);
}

public ApplicationContext parent() {
return app.getParent();
}

public <T> Map<String, T> getParentBeans(Class<T> type) {
return parent().getBeansOfType(type);
}

public int port() {
if (app == null) {
throw new RuntimeException("App is not running.");
}
return app.getEnvironment().getProperty("server.port", Integer.class, -1);
}

public String root() {
if (app == null) {
throw new RuntimeException("App is not running.");
}

String protocol = tlsEnabled() ? "https" : "http";
return String.format("%s://localhost:%d/", protocol, port());
}

private boolean tlsEnabled() {
return app.getEnvironment().getProperty("server.ssl.enabled", Boolean.class,
false);
}

@Override
public void close() {
stop();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright 2018-2019 the original author or authors.
*
* 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
*
* https://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.springframework.cloud.test;

import java.security.KeyPair;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;

public class KeyAndCert {

private KeyPair keyPair;

private X509Certificate certificate;

public KeyAndCert(KeyPair keyPair, X509Certificate certificate) {
this.keyPair = keyPair;
this.certificate = certificate;
}

public KeyPair keyPair() {
return keyPair;
}

public PublicKey publicKey() {
return keyPair.getPublic();
}

public PrivateKey privateKey() {
return keyPair.getPrivate();
}

public X509Certificate certificate() {
return certificate;
}

public String subject() {
String dn = certificate.getSubjectDN().getName();
int index = dn.indexOf('=');
return dn.substring(index + 1);
}

public KeyAndCert sign(String subject) throws Exception {
KeyTool tool = new KeyTool();
return tool.signCertificate(subject, this);
}

public KeyAndCert sign(KeyPair keyPair, String subject) throws Exception {
KeyTool tool = new KeyTool();
return tool.signCertificate(keyPair, subject, this);
}

public KeyStore storeKeyAndCert(String keyPassword) throws Exception {
KeyStore result = KeyStore.getInstance("PKCS12");
result.load(null);

result.setKeyEntry(subject(), keyPair.getPrivate(), keyPassword.toCharArray(),
certChain());
return result;
}

private Certificate[] certChain() {
return new Certificate[] { certificate() };
}

public KeyStore storeCert() throws Exception {
return storeCert("PKCS12");
}

public KeyStore storeCert(String storeType) throws Exception {
KeyStore result = KeyStore.getInstance(storeType);
result.load(null);

result.setCertificateEntry(subject(), certificate());
return result;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* Copyright 2018-2019 the original author or authors.
*
* 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
*
* https://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.springframework.cloud.test;

import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Date;

import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

public class KeyTool {

private static final long ONE_DAY = 1000L * 60L * 60L * 24L;

private static final long TEN_YEARS = ONE_DAY * 365L * 10L;

public KeyAndCert createCA(String ca) throws Exception {
KeyPair keyPair = createKeyPair();
X509Certificate certificate = createCert(keyPair, ca);
return new KeyAndCert(keyPair, certificate);
}

public KeyAndCert signCertificate(String subject, KeyAndCert signer)
throws Exception {
return signCertificate(createKeyPair(), subject, signer);
}

public KeyAndCert signCertificate(KeyPair keyPair, String subject, KeyAndCert signer)
throws Exception {
X509Certificate certificate = createCert(keyPair.getPublic(), signer.privateKey(),
signer.subject(), subject);
KeyAndCert result = new KeyAndCert(keyPair, certificate);

return result;
}

public KeyPair createKeyPair() throws Exception {
return createKeyPair(1024);
}

public KeyPair createKeyPair(int keySize) throws Exception {
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
gen.initialize(keySize, new SecureRandom());
return gen.generateKeyPair();
}

public X509Certificate createCert(KeyPair keyPair, String ca) throws Exception {
JcaX509v3CertificateBuilder builder = certBuilder(keyPair.getPublic(), ca, ca);
builder.addExtension(Extension.keyUsage, true,
new KeyUsage(KeyUsage.keyCertSign));
builder.addExtension(Extension.basicConstraints, false,
new BasicConstraints(true));

return signCert(builder, keyPair.getPrivate());
}

public X509Certificate createCert(PublicKey publicKey, PrivateKey privateKey,
String issuer, String subject) throws Exception {
JcaX509v3CertificateBuilder builder = certBuilder(publicKey, issuer, subject);
builder.addExtension(Extension.keyUsage, true,
new KeyUsage(KeyUsage.digitalSignature));
builder.addExtension(Extension.basicConstraints, false,
new BasicConstraints(false));

GeneralName[] names = new GeneralName[] {
new GeneralName(GeneralName.dNSName, "localhost") };
builder.addExtension(Extension.subjectAlternativeName, false,
GeneralNames.getInstance(new DERSequence(names)));

return signCert(builder, privateKey);
}

private JcaX509v3CertificateBuilder certBuilder(PublicKey publicKey, String issuer,
String subject) {
X500Name issuerName = new X500Name(String.format("dc=%s", issuer));
X500Name subjectName = new X500Name(String.format("dc=%s", subject));

long now = System.currentTimeMillis();
BigInteger serialNum = BigInteger.valueOf(now);
Date notBefore = new Date(now - ONE_DAY);
Date notAfter = new Date(now + TEN_YEARS);

return new JcaX509v3CertificateBuilder(issuerName, serialNum, notBefore, notAfter,
subjectName, publicKey);
}

private X509Certificate signCert(JcaX509v3CertificateBuilder builder,
PrivateKey privateKey) throws Exception {
ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSA")
.build(privateKey);
X509CertificateHolder holder = builder.build(signer);

return new JcaX509CertificateConverter().getCertificate(holder);
}

}