Skip to content

Commit

Permalink
TLS peer certs
Browse files Browse the repository at this point in the history
  • Loading branch information
danielkec committed Jan 31, 2024
1 parent 43ef5f8 commit fb95852
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
* Copyright (c) 2022, 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -20,22 +20,21 @@
import java.security.Principal;
import java.security.cert.Certificate;
import java.util.Optional;
import java.util.function.Supplier;

import io.helidon.common.LazyValue;

class PeerInfoImpl implements PeerInfo {
private final LazyValue<SocketAddress> socketAddress;
private final LazyValue<String> host;
private final LazyValue<Integer> port;
private final Supplier<Optional<Principal>> principalSupplier;
private final Supplier<Optional<Certificate[]>> certificateSupplier;
private final LazyValue<Optional<Principal>> principalSupplier;
private final LazyValue<Optional<Certificate[]>> certificateSupplier;

private PeerInfoImpl(LazyValue<SocketAddress> socketAddress,
LazyValue<String> host,
LazyValue<Integer> port,
Supplier<Optional<Principal>> principalSupplier,
Supplier<Optional<Certificate[]>> certificateSupplier) {
LazyValue<Optional<Principal>> principalSupplier,
LazyValue<Optional<Certificate[]>> certificateSupplier) {
this.socketAddress = socketAddress;
this.host = host;
this.port = port;
Expand All @@ -47,36 +46,34 @@ static PeerInfo createLocal(PlainSocket socket) {
return new PeerInfoImpl(LazyValue.create(socket::localSocketAddress),
LazyValue.create(socket::localHost),
LazyValue.create(socket::localPort),
Optional::empty,
Optional::empty);
LazyValue.create(Optional.empty()),
LazyValue.create(Optional.empty()));
}

static PeerInfoImpl createLocal(TlsSocket socket) {
// remote socket - all lazy values, as they cannot change (and they require creating another object)
// tls related - there can be re-negotiation of tls, to be safe I use a supplier
return new PeerInfoImpl(LazyValue.create(socket::localSocketAddress),
LazyValue.create(socket::localHost),
LazyValue.create(socket::localPort),
socket::tlsPrincipal,
socket::tlsCertificates);
LazyValue.create(socket::tlsPrincipal),
LazyValue.create(socket::tlsCertificates));
}

static PeerInfoImpl createRemote(TlsSocket socket) {
// remote socket - all lazy values, as they cannot change (and they require creating another object)
// tls related - there can be re-negotiation of tls, to be safe I use a supplier
return new PeerInfoImpl(LazyValue.create(socket::remoteSocketAddress),
LazyValue.create(socket::remoteHost),
LazyValue.create(socket::remotePort),
socket::tlsPeerPrincipal,
socket::tlsPeerCertificates);
LazyValue.create(socket::tlsPeerPrincipal),
LazyValue.create(socket::tlsPeerCertificates));
}

static PeerInfoImpl createRemote(PlainSocket socket) {
return new PeerInfoImpl(LazyValue.create(socket::remoteSocketAddress),
LazyValue.create(socket::remoteHost),
LazyValue.create(socket::remotePort),
Optional::empty,
Optional::empty);
LazyValue.create(Optional.empty()),
LazyValue.create(Optional.empty()));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
* Copyright (c) 2022, 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,6 +18,7 @@

import java.security.Principal;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.Optional;

import javax.net.ssl.SSLPeerUnverifiedException;
Expand All @@ -28,11 +29,15 @@
*/
public final class TlsSocket extends PlainSocket {
private final SSLSocket sslSocket;
private volatile PeerInfo localPeer;
private volatile PeerInfo remotePeer;
private volatile byte[] lastSslSessionId;

private TlsSocket(SSLSocket socket, String channelId, String serverChannelId) {
super(socket, channelId, serverChannelId);

this.sslSocket = socket;
this.lastSslSessionId = socket.getSession().getId();
}

/**
Expand Down Expand Up @@ -63,12 +68,20 @@ public static TlsSocket client(SSLSocket delegate,

@Override
public PeerInfo remotePeer() {
return PeerInfoImpl.createRemote(this);
if (remotePeer == null || renegotiated()) {
this.localPeer = null;
this.remotePeer = PeerInfoImpl.createRemote(this);
}
return this.remotePeer;
}

@Override
public PeerInfo localPeer() {
return PeerInfoImpl.createLocal(this);
if (localPeer == null || renegotiated()) {
this.remotePeer = null;
this.localPeer = PeerInfoImpl.createLocal(this);
}
return this.localPeer;
}

@Override
Expand Down Expand Up @@ -110,4 +123,22 @@ Optional<Principal> tlsPrincipal() {
Optional<Certificate[]> tlsCertificates() {
return Optional.of(sslSocket.getSession().getLocalCertificates());
}

/**
* Check if TLS renegotiation happened,
* if so ssl session id would have changed.
*
* @return true if tls was renegotiated
*/
boolean renegotiated() {
byte[] currentSessionId = sslSocket.getSession().getId();

// Intentionally avoiding locking and MessageDigest.isEqual
if (Arrays.equals(currentSessionId, lastSslSessionId)) {
return false;
}

lastSslSessionId = currentSessionId;
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.function.Supplier;
Expand All @@ -36,6 +37,7 @@
import io.helidon.http.DateTime;
import io.helidon.http.DirectHandler;
import io.helidon.http.DirectHandler.EventType;
import io.helidon.http.Header;
import io.helidon.http.HeaderNames;
import io.helidon.http.HeaderValues;
import io.helidon.http.HttpPrologue;
Expand Down Expand Up @@ -140,6 +142,9 @@ public void handle(Semaphore requestSemaphore) throws InterruptedException {
try {
// look for protocol data
ProxyProtocolData proxyProtocolData = ctx.proxyProtocolData().orElse(null);
Optional<Header> xHelidonCn = ctx.remotePeer().tlsCertificates()
.flatMap(TlsUtils::parseCn)
.map(name -> HeaderValues.create(X_HELIDON_CN, name));

// handle connection until an exception (or explicit connection close)
while (canRun) {
Expand All @@ -153,9 +158,8 @@ public void handle(Semaphore requestSemaphore) throws InterruptedException {
currentEntitySizeRead = 0;

WritableHeaders<?> headers = http1headers.readHeaders(prologue);
ctx.remotePeer().tlsCertificates()
.flatMap(TlsUtils::parseCn)
.ifPresent(name -> headers.set(X_HELIDON_CN, name));
xHelidonCn.ifPresent(headers::set);

recvListener.headers(ctx, headers);

// proxy protocol related headers X-Forwarded-For and X-Forwarded-Port
Expand Down

0 comments on commit fb95852

Please sign in to comment.