Skip to content

Commit 838caaf

Browse files
committed
Support retrieving token on HTTPS
1 parent a4fda34 commit 838caaf

File tree

8 files changed

+262
-48
lines changed

8 files changed

+262
-48
lines changed

pom.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
<micrometer-docs-generator.version>1.0.4</micrometer-docs-generator.version>
5656
<jose4j.version>0.9.6</jose4j.version>
5757
<commons-lang3.version>3.17.0</commons-lang3.version>
58+
<bouncycastle.version>1.79</bouncycastle.version>
5859
<maven.compiler.plugin.version>3.13.0</maven.compiler.plugin.version>
5960
<maven.dependency.plugin.version>3.8.1</maven.dependency.plugin.version>
6061
<maven-surefire-plugin.version>3.5.2</maven-surefire-plugin.version>
@@ -255,6 +256,14 @@
255256
<groupId>org.apache.commons</groupId>
256257
<artifactId>commons-lang3</artifactId>
257258
<version>${commons-lang3.version}</version>
259+
<scope>test</scope>
260+
</dependency>
261+
262+
<dependency>
263+
<groupId>org.bouncycastle</groupId>
264+
<artifactId>bcpkix-jdk18on</artifactId>
265+
<version>${bouncycastle.version}</version>
266+
<scope>test</scope>
258267
</dependency>
259268

260269
</dependencies>

src/main/java/com/rabbitmq/client/amqp/OAuthSettings.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
1818
package com.rabbitmq.client.amqp;
1919

20+
import javax.net.ssl.SSLContext;
21+
2022
public interface OAuthSettings<T> {
2123

2224
OAuthSettings<T> tokenEndpointUri(String uri);
@@ -31,5 +33,14 @@ public interface OAuthSettings<T> {
3133

3234
OAuthSettings<T> shared(boolean shared);
3335

36+
TlsSettings<? extends T> tls();
37+
3438
T connection();
39+
40+
interface TlsSettings<T> {
41+
42+
TlsSettings<T> sslContext(SSLContext sslContext);
43+
44+
OAuthSettings<T> oauth();
45+
}
3546
}

src/main/java/com/rabbitmq/client/amqp/impl/CredentialsFactory.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
import com.rabbitmq.client.amqp.UsernamePasswordCredentialsProvider;
2222
import com.rabbitmq.client.amqp.oauth.GsonTokenParser;
2323
import com.rabbitmq.client.amqp.oauth.HttpTokenRequester;
24+
import java.net.http.HttpClient;
2425
import java.util.concurrent.locks.Lock;
2526
import java.util.concurrent.locks.ReentrantLock;
27+
import java.util.function.Consumer;
2628

2729
final class CredentialsFactory {
2830

@@ -74,18 +76,20 @@ private Credentials globalOAuthCredentials(DefaultConnectionSettings<?> connecti
7476

7577
private Credentials createOAuthCredentials(DefaultConnectionSettings<?> connectionSettings) {
7678
DefaultConnectionSettings.DefaultOAuthSettings<?> settings = connectionSettings.oauth();
77-
// TODO set TLS configuration on TLS requester
78-
// TODO use pre-configured token requester if any
79+
Consumer<HttpClient.Builder> clientBuilderConsumer;
80+
if (settings.tlsEnabled()) {
81+
clientBuilderConsumer = b -> b.sslContext(settings.tls().sslContext());
82+
} else {
83+
clientBuilderConsumer = ignored -> {};
84+
}
7985
HttpTokenRequester tokenRequester =
8086
new HttpTokenRequester(
8187
settings.tokenEndpointUri(),
8288
settings.clientId(),
8389
settings.clientSecret(),
8490
settings.grantType(),
8591
settings.parameters(),
86-
null,
87-
null,
88-
null,
92+
clientBuilderConsumer,
8993
null,
9094
new GsonTokenParser());
9195
return new TokenCredentials(tokenRequester, environment.scheduledExecutorService());

src/main/java/com/rabbitmq/client/amqp/impl/DefaultConnectionSettings.java

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ void copyTo(DefaultConnectionSettings<?> copy) {
224224
this.affinity.copyTo(copy.affinity);
225225

226226
if (this.oAuthSettings.enabled()) {
227-
this.oAuthSettings.copyTo((DefaultOAuthSettings<?>) copy.oauth());
227+
this.oAuthSettings.copyTo(copy.oauth());
228228
}
229229
}
230230

@@ -502,6 +502,7 @@ void validate() {
502502
static class DefaultOAuthSettings<T> implements OAuthSettings<T> {
503503

504504
private final DefaultConnectionSettings<T> connectionSettings;
505+
private final DefaultOAuthTlsSettings<T> tls = new DefaultOAuthTlsSettings<>(this);
505506
private final Map<String, String> parameters = new HashMap<>();
506507
private String tokenEndpointUri;
507508
private String clientId;
@@ -554,6 +555,12 @@ public OAuthSettings<T> shared(boolean shared) {
554555
return this;
555556
}
556557

558+
@Override
559+
public DefaultOAuthTlsSettings<? extends T> tls() {
560+
this.tls.enable();
561+
return this.tls;
562+
}
563+
557564
@Override
558565
public T connection() {
559566
return this.connectionSettings.toReturn();
@@ -566,6 +573,9 @@ void copyTo(DefaultOAuthSettings<?> copy) {
566573
copy.grantType(this.grantType);
567574
copy.shared(this.shared);
568575
this.parameters.forEach(copy::parameter);
576+
if (this.tls.enabled()) {
577+
this.tls.copyTo(copy.tls());
578+
}
569579
}
570580

571581
String tokenEndpointUri() {
@@ -595,5 +605,48 @@ boolean shared() {
595605
boolean enabled() {
596606
return this.tokenEndpointUri != null;
597607
}
608+
609+
boolean tlsEnabled() {
610+
return this.tls.enabled();
611+
}
612+
}
613+
614+
static class DefaultOAuthTlsSettings<T> implements OAuthSettings.TlsSettings<T> {
615+
616+
private final OAuthSettings<T> oAuthSettings;
617+
private SSLContext sslContext;
618+
private boolean enabled = false;
619+
620+
DefaultOAuthTlsSettings(OAuthSettings<T> oAuthSettings) {
621+
this.oAuthSettings = oAuthSettings;
622+
}
623+
624+
@Override
625+
public OAuthSettings.TlsSettings<T> sslContext(SSLContext sslContext) {
626+
this.sslContext = sslContext;
627+
return this;
628+
}
629+
630+
@Override
631+
public OAuthSettings<T> oauth() {
632+
return this.oAuthSettings;
633+
}
634+
635+
void enable() {
636+
this.enabled = true;
637+
}
638+
639+
boolean enabled() {
640+
return this.enabled;
641+
}
642+
643+
SSLContext sslContext() {
644+
return this.sslContext;
645+
}
646+
647+
void copyTo(DefaultOAuthTlsSettings<?> copy) {
648+
copy.enabled = this.enabled;
649+
copy.sslContext(this.sslContext);
650+
}
598651
}
599652
}

src/main/java/com/rabbitmq/client/amqp/oauth/HttpTokenRequester.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@
3030
import java.util.Base64;
3131
import java.util.Map;
3232
import java.util.function.Consumer;
33-
import javax.net.ssl.HostnameVerifier;
34-
import javax.net.ssl.SSLSocketFactory;
3533

3634
public final class HttpTokenRequester implements TokenRequester {
3735

@@ -45,9 +43,6 @@ public final class HttpTokenRequester implements TokenRequester {
4543

4644
private final Map<String, String> parameters;
4745

48-
private final HostnameVerifier hostnameVerifier;
49-
private final SSLSocketFactory sslSocketFactory;
50-
5146
private final HttpClient client;
5247
private final Consumer<HttpRequest.Builder> requestBuilderConsumer;
5348

@@ -59,8 +54,6 @@ public HttpTokenRequester(
5954
String clientSecret,
6055
String grantType,
6156
Map<String, String> parameters,
62-
HostnameVerifier hostnameVerifier,
63-
SSLSocketFactory sslSocketFactory,
6457
Consumer<HttpClient.Builder> clientBuilderConsumer,
6558
Consumer<HttpRequest.Builder> requestBuilderConsumer,
6659
TokenParser parser) {
@@ -73,8 +66,6 @@ public HttpTokenRequester(
7366
this.clientSecret = clientSecret;
7467
this.grantType = grantType;
7568
this.parameters = Map.copyOf(parameters);
76-
this.hostnameVerifier = hostnameVerifier;
77-
this.sslSocketFactory = sslSocketFactory;
7869
this.parser = parser;
7970
if (requestBuilderConsumer == null) {
8071
this.requestBuilderConsumer =

src/test/java/com/rabbitmq/client/amqp/impl/HttpTestUtils.java

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,91 @@
1919

2020
import com.sun.net.httpserver.HttpHandler;
2121
import com.sun.net.httpserver.HttpServer;
22+
import com.sun.net.httpserver.HttpsConfigurator;
23+
import com.sun.net.httpserver.HttpsServer;
24+
import java.math.BigInteger;
2225
import java.net.InetSocketAddress;
26+
import java.security.*;
27+
import java.security.cert.Certificate;
28+
import java.security.cert.X509Certificate;
29+
import java.security.spec.ECGenParameterSpec;
30+
import java.time.Instant;
31+
import java.time.temporal.ChronoUnit;
32+
import java.util.Date;
33+
import javax.net.ssl.KeyManagerFactory;
34+
import javax.net.ssl.SSLContext;
35+
import org.bouncycastle.asn1.x500.X500NameBuilder;
36+
import org.bouncycastle.asn1.x500.style.BCStyle;
37+
import org.bouncycastle.cert.X509CertificateHolder;
38+
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
39+
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
40+
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
2341

2442
public final class HttpTestUtils {
2543

44+
private static final char[] KEY_STORE_PASSWORD = "password".toCharArray();
45+
2646
private HttpTestUtils() {}
2747

28-
public static HttpServer startHttpServer(int port, String path, HttpHandler handler)
29-
throws Exception {
30-
com.sun.net.httpserver.HttpServer server =
31-
com.sun.net.httpserver.HttpServer.create(new InetSocketAddress(port), 0);
32-
server.createContext(path, handler);
33-
server.start();
34-
return server;
48+
public static HttpServer startServer(int port, String path, HttpHandler handler) {
49+
return startServer(port, path, null, handler);
50+
}
51+
52+
public static HttpServer startServer(
53+
int port, String path, KeyStore keyStore, HttpHandler handler) {
54+
HttpServer server;
55+
try {
56+
if (keyStore != null) {
57+
KeyManagerFactory keyManagerFactory =
58+
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
59+
keyManagerFactory.init(keyStore, KEY_STORE_PASSWORD);
60+
SSLContext sslContext = SSLContext.getInstance("TLS");
61+
sslContext.init(keyManagerFactory.getKeyManagers(), null, null);
62+
server = HttpsServer.create(new InetSocketAddress(port), 0);
63+
((HttpsServer) server).setHttpsConfigurator(new HttpsConfigurator(sslContext));
64+
} else {
65+
server = HttpServer.create(new InetSocketAddress(port), 0);
66+
}
67+
server.createContext(path, handler);
68+
server.start();
69+
return server;
70+
} catch (Exception e) {
71+
throw new RuntimeException(e);
72+
}
73+
}
74+
75+
public static KeyStore generateKeyPair() {
76+
try {
77+
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
78+
keyStore.load(null, KEY_STORE_PASSWORD);
79+
80+
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
81+
ECGenParameterSpec spec = new ECGenParameterSpec("secp521r1");
82+
kpg.initialize(spec);
83+
84+
KeyPair kp = kpg.generateKeyPair();
85+
86+
JcaX509v3CertificateBuilder certificateBuilder =
87+
new JcaX509v3CertificateBuilder(
88+
new X500NameBuilder().addRDN(BCStyle.CN, "localhost").build(),
89+
BigInteger.valueOf(new SecureRandom().nextInt()),
90+
Date.from(Instant.now().minus(10, ChronoUnit.DAYS)),
91+
Date.from(Instant.now().plus(10, ChronoUnit.DAYS)),
92+
new X500NameBuilder().addRDN(BCStyle.CN, "localhost").build(),
93+
kp.getPublic());
94+
95+
X509CertificateHolder certificateHolder =
96+
certificateBuilder.build(
97+
new JcaContentSignerBuilder("SHA512withECDSA").build(kp.getPrivate()));
98+
99+
X509Certificate certificate =
100+
new JcaX509CertificateConverter().getCertificate(certificateHolder);
101+
102+
keyStore.setKeyEntry(
103+
"default", kp.getPrivate(), KEY_STORE_PASSWORD, new Certificate[] {certificate});
104+
return keyStore;
105+
} catch (Exception e) {
106+
throw new RuntimeException(e);
107+
}
35108
}
36109
}

0 commit comments

Comments
 (0)