Skip to content

Commit

Permalink
if certificate chain is available add it to Certificate event (#985)
Browse files Browse the repository at this point in the history
When Certificate chain is available as part of oracle cloud certificate it should be used for building ssl context
  • Loading branch information
n0tl3ss committed Sep 19, 2024
1 parent e4dfe71 commit 65f379b
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@

import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.List;

/**
* Event used to alert when a new Oracle Cloud certificate is ready for use.
*
* @param privateKey private key
* @param certificate X509 certificate file
* @param intermediate X509 certificate intermediate files
*/
public record CertificateEvent(@NonNull PrivateKey privateKey, @NonNull X509Certificate certificate) {
public record CertificateEvent(@NonNull PrivateKey privateKey, @NonNull X509Certificate certificate, @NonNull List<X509Certificate> intermediate) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
* Service to contact an Oracle Cloud Certificate service and setup a certificate on a given basis.
Expand Down Expand Up @@ -92,9 +95,22 @@ protected Optional<CertificateEvent> getCertificateEvent() {
.certificateBundleType(GetCertificateBundleRequest.CertificateBundleType.CertificateContentWithPrivateKey)
.build());

List<X509Certificate> intermediate = Collections.emptyList();

if (certificateBundle.getCertificateBundle().getCertChainPem() != null) {
intermediate = cf.generateCertificates(
new ByteArrayInputStream(
certificateBundle
.getCertificateBundle()
.getCertChainPem()
.getBytes())).stream().map(cert -> ((X509Certificate) cert))
.collect(Collectors.toList());
}

CertificateEvent certificateEvent = new CertificateEvent(
getPrivateKey(certificateBundle),
(X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certificateBundle.getCertificateBundle().getCertificatePem().getBytes()))
(X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certificateBundle.getCertificateBundle().getCertificatePem().getBytes())),
intermediate
);

return Optional.of(certificateEvent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
Expand Down Expand Up @@ -61,8 +64,12 @@ void onNewCertificate(CertificateEvent certificateEvent) {
if (LOG.isDebugEnabled()) {
LOG.debug("New certificate received and replaced the proxied SSL context");
}
List<X509Certificate> chain = new ArrayList<>();
chain.add(certificateEvent.certificate());
chain.addAll(certificateEvent.intermediate());

SslContext sslContext = SslContextBuilder
.forServer(certificateEvent.privateKey(), certificateEvent.certificate())
.forServer(certificateEvent.privateKey(), chain.toArray(new X509Certificate[]{}))
.build();
delegatedSslContext.setNewSslContext(sslContext);
} catch (SSLException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.oracle.bmc.certificates.model.CertificateBundleWithPrivateKey
import com.oracle.bmc.certificates.model.Validity
import com.oracle.bmc.certificates.responses.GetCertificateBundleResponse
import io.micronaut.context.event.ApplicationEventPublisher
import io.micronaut.oraclecloud.certificates.events.CertificateEvent
import io.micronaut.oraclecloud.certificates.services.OracleCloudCertificateService
import spock.lang.Specification

Expand Down Expand Up @@ -71,7 +72,137 @@ nzsQ7KiJuJc/zsKCgscZ/bQocbz3Pn9jPZTBktCdqsCvVOqHB0RuaclQxWfKsRN4
IgQuEdz+6WvdabYC1igIWN9od6fnoNI3NSRwuttvnJVWX4FkVnhu1YRdGdNkGg==
-----END CERTIFICATE-----"""

public static String CERTIFICATE_CHAIN_STRING = """-----BEGIN CERTIFICATE-----
MIIFajCCBFKgAwIBAgIUdqm3iigytoj0A4qjzeqqcyUUiQQwDQYJKoZIhvcNAQEL
BQAwEDEOMAwGA1UEAwwFdGVzdDEwHhcNMjMwNTMwMTcyMDQyWhcNMjMwODE1MDAw
MDAwWjAUMRIwEAYDVQQDDAlNaWNyb25hdXQwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQDaz3fk1bp7xmAY7wRYdu6998ioDEkwemwvr3/8OR5Ij+56tGcO
gSAu7HALtcGHYB91NCO3UlDgnauBZ5Crm7HMFdBLUCxfBoxn4s+oNO5EocPbPqru
n++V5FMcXCmj5FagO+TshSzyBl/QMC1SqnIiQ7VUlO/Zhsr2q4DKiCz1v6eT5WbF
ly8eH36M3//q7sb2qFEvOUaj4NhjG+LdbQll04EGUPhIPeBkuWS714EDjKtUv35+
Ncvd285q6MfPihku3mLQRLD6nHT97mPi0VMnOp1A3VTDnTx7kjXGJsY9oSE2X0PO
xACWnyV5a61HqAa8TpE4h2MBksI9nuRFz7dxAgMBAAGjggK2MIICsjAOBgNVHQ8B
Af8EBAMCBaAwDAYDVR0TAQH/BAIwADAgBgNVHSUBAf8EFjAUBggrBgEFBQcDAgYI
KwYBBQUHAwEwggEzBgNVHQ4EggEqBIIBJjCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBANrPd+TVunvGYBjvBFh27r33yKgMSTB6bC+vf/w5HkiP7nq0Zw6B
IC7scAu1wYdgH3U0I7dSUOCdq4FnkKubscwV0EtQLF8GjGfiz6g07kShw9s+qu6f
75XkUxxcKaPkVqA75OyFLPIGX9AwLVKqciJDtVSU79mGyvargMqILPW/p5PlZsWX
Lx4ffozf/+ruxvaoUS85RqPg2GMb4t1tCWXTgQZQ+Eg94GS5ZLvXgQOMq1S/fn41
y93bzmrox8+KGS7eYtBEsPqcdP3uY+LRUyc6nUDdVMOdPHuSNcYmxj2hITZfQ87E
AJafJXlrrUeoBrxOkTiHYwGSwj2e5EXPt3ECAwEAATCCATcGA1UdIwSCAS4wggEq
gIIBJjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANHglJHO6O56lMIp
DUCff+vMWhukXlnZTq4kS7wpiJdMSTYFzN8kOKLDRujLneAfyrYDW9Tm4xFYBZRX
uRiCq7H7ck+HFR0Xq5fuKzp5j/KaGxMbloSGOWqdcWvHOSMwAr4mmGui4le14nXI
fUYZcZuXwooEHSHOMkVrZUa/6M0lNNoTWvUCj74HAXJNAEu7D+hfcKOQ0E7WbSYR
Yol+Kjw3ij6HTGPIjUpY8emDd4tWkJx3pzuTUIABCAcqz9al/Ok3ac2ZEPQbM83N
4JbWrioj+ic8nkA+xvd8NYmdbfU9fiuUXOeQQMmUeAqlgmE3nC+zfSihLYaLJ61A
skTL7a0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAIswvhJX2y0YZiiFJy5b6KPID
SJuD+UZmIiDs61ZL6IK3iNRMnJX0IgTQMi5Ema1svvFCMM6502fMm5ktDSEr/GXY
KvqeJ4VmcgfCAxSE5tDek5/KpKOw1X57jqdoMJ25xLWTneN4YeTJDeTAG56T5/Ci
nzsQ7KiJuJc/zsKCgscZ/bQocbz3Pn9jPZTBktCdqsCvVOqHB0RuaclQxWfKsRN4
52Xov7EGYbm0xfifdg2HLnZFUzOnVAEQNsnKXW095IGFgUYlonuQ+jotRfLEf3hm
IgQuEdz+6WvdabYC1igIWN9od6fnoNI3NSRwuttvnJVWX4FkVnhu1YRdGdNkGg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFajCCBFKgAwIBAgIUdqm3iigytoj0A4qjzeqqcyUUiQQwDQYJKoZIhvcNAQEL
BQAwEDEOMAwGA1UEAwwFdGVzdDEwHhcNMjMwNTMwMTcyMDQyWhcNMjMwODE1MDAw
MDAwWjAUMRIwEAYDVQQDDAlNaWNyb25hdXQwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQDaz3fk1bp7xmAY7wRYdu6998ioDEkwemwvr3/8OR5Ij+56tGcO
gSAu7HALtcGHYB91NCO3UlDgnauBZ5Crm7HMFdBLUCxfBoxn4s+oNO5EocPbPqru
n++V5FMcXCmj5FagO+TshSzyBl/QMC1SqnIiQ7VUlO/Zhsr2q4DKiCz1v6eT5WbF
ly8eH36M3//q7sb2qFEvOUaj4NhjG+LdbQll04EGUPhIPeBkuWS714EDjKtUv35+
Ncvd285q6MfPihku3mLQRLD6nHT97mPi0VMnOp1A3VTDnTx7kjXGJsY9oSE2X0PO
xACWnyV5a61HqAa8TpE4h2MBksI9nuRFz7dxAgMBAAGjggK2MIICsjAOBgNVHQ8B
Af8EBAMCBaAwDAYDVR0TAQH/BAIwADAgBgNVHSUBAf8EFjAUBggrBgEFBQcDAgYI
KwYBBQUHAwEwggEzBgNVHQ4EggEqBIIBJjCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBANrPd+TVunvGYBjvBFh27r33yKgMSTB6bC+vf/w5HkiP7nq0Zw6B
IC7scAu1wYdgH3U0I7dSUOCdq4FnkKubscwV0EtQLF8GjGfiz6g07kShw9s+qu6f
75XkUxxcKaPkVqA75OyFLPIGX9AwLVKqciJDtVSU79mGyvargMqILPW/p5PlZsWX
Lx4ffozf/+ruxvaoUS85RqPg2GMb4t1tCWXTgQZQ+Eg94GS5ZLvXgQOMq1S/fn41
y93bzmrox8+KGS7eYtBEsPqcdP3uY+LRUyc6nUDdVMOdPHuSNcYmxj2hITZfQ87E
AJafJXlrrUeoBrxOkTiHYwGSwj2e5EXPt3ECAwEAATCCATcGA1UdIwSCAS4wggEq
gIIBJjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANHglJHO6O56lMIp
DUCff+vMWhukXlnZTq4kS7wpiJdMSTYFzN8kOKLDRujLneAfyrYDW9Tm4xFYBZRX
uRiCq7H7ck+HFR0Xq5fuKzp5j/KaGxMbloSGOWqdcWvHOSMwAr4mmGui4le14nXI
fUYZcZuXwooEHSHOMkVrZUa/6M0lNNoTWvUCj74HAXJNAEu7D+hfcKOQ0E7WbSYR
Yol+Kjw3ij6HTGPIjUpY8emDd4tWkJx3pzuTUIABCAcqz9al/Ok3ac2ZEPQbM83N
4JbWrioj+ic8nkA+xvd8NYmdbfU9fiuUXOeQQMmUeAqlgmE3nC+zfSihLYaLJ61A
skTL7a0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAIswvhJX2y0YZiiFJy5b6KPID
SJuD+UZmIiDs61ZL6IK3iNRMnJX0IgTQMi5Ema1svvFCMM6502fMm5ktDSEr/GXY
KvqeJ4VmcgfCAxSE5tDek5/KpKOw1X57jqdoMJ25xLWTneN4YeTJDeTAG56T5/Ci
nzsQ7KiJuJc/zsKCgscZ/bQocbz3Pn9jPZTBktCdqsCvVOqHB0RuaclQxWfKsRN4
52Xov7EGYbm0xfifdg2HLnZFUzOnVAEQNsnKXW095IGFgUYlonuQ+jotRfLEf3hm
IgQuEdz+6WvdabYC1igIWN9od6fnoNI3NSRwuttvnJVWX4FkVnhu1YRdGdNkGg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFajCCBFKgAwIBAgIUdqm3iigytoj0A4qjzeqqcyUUiQQwDQYJKoZIhvcNAQEL
BQAwEDEOMAwGA1UEAwwFdGVzdDEwHhcNMjMwNTMwMTcyMDQyWhcNMjMwODE1MDAw
MDAwWjAUMRIwEAYDVQQDDAlNaWNyb25hdXQwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQDaz3fk1bp7xmAY7wRYdu6998ioDEkwemwvr3/8OR5Ij+56tGcO
gSAu7HALtcGHYB91NCO3UlDgnauBZ5Crm7HMFdBLUCxfBoxn4s+oNO5EocPbPqru
n++V5FMcXCmj5FagO+TshSzyBl/QMC1SqnIiQ7VUlO/Zhsr2q4DKiCz1v6eT5WbF
ly8eH36M3//q7sb2qFEvOUaj4NhjG+LdbQll04EGUPhIPeBkuWS714EDjKtUv35+
Ncvd285q6MfPihku3mLQRLD6nHT97mPi0VMnOp1A3VTDnTx7kjXGJsY9oSE2X0PO
xACWnyV5a61HqAa8TpE4h2MBksI9nuRFz7dxAgMBAAGjggK2MIICsjAOBgNVHQ8B
Af8EBAMCBaAwDAYDVR0TAQH/BAIwADAgBgNVHSUBAf8EFjAUBggrBgEFBQcDAgYI
KwYBBQUHAwEwggEzBgNVHQ4EggEqBIIBJjCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBANrPd+TVunvGYBjvBFh27r33yKgMSTB6bC+vf/w5HkiP7nq0Zw6B
IC7scAu1wYdgH3U0I7dSUOCdq4FnkKubscwV0EtQLF8GjGfiz6g07kShw9s+qu6f
75XkUxxcKaPkVqA75OyFLPIGX9AwLVKqciJDtVSU79mGyvargMqILPW/p5PlZsWX
Lx4ffozf/+ruxvaoUS85RqPg2GMb4t1tCWXTgQZQ+Eg94GS5ZLvXgQOMq1S/fn41
y93bzmrox8+KGS7eYtBEsPqcdP3uY+LRUyc6nUDdVMOdPHuSNcYmxj2hITZfQ87E
AJafJXlrrUeoBrxOkTiHYwGSwj2e5EXPt3ECAwEAATCCATcGA1UdIwSCAS4wggEq
gIIBJjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANHglJHO6O56lMIp
DUCff+vMWhukXlnZTq4kS7wpiJdMSTYFzN8kOKLDRujLneAfyrYDW9Tm4xFYBZRX
uRiCq7H7ck+HFR0Xq5fuKzp5j/KaGxMbloSGOWqdcWvHOSMwAr4mmGui4le14nXI
fUYZcZuXwooEHSHOMkVrZUa/6M0lNNoTWvUCj74HAXJNAEu7D+hfcKOQ0E7WbSYR
Yol+Kjw3ij6HTGPIjUpY8emDd4tWkJx3pzuTUIABCAcqz9al/Ok3ac2ZEPQbM83N
4JbWrioj+ic8nkA+xvd8NYmdbfU9fiuUXOeQQMmUeAqlgmE3nC+zfSihLYaLJ61A
skTL7a0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAIswvhJX2y0YZiiFJy5b6KPID
SJuD+UZmIiDs61ZL6IK3iNRMnJX0IgTQMi5Ema1svvFCMM6502fMm5ktDSEr/GXY
KvqeJ4VmcgfCAxSE5tDek5/KpKOw1X57jqdoMJ25xLWTneN4YeTJDeTAG56T5/Ci
nzsQ7KiJuJc/zsKCgscZ/bQocbz3Pn9jPZTBktCdqsCvVOqHB0RuaclQxWfKsRN4
52Xov7EGYbm0xfifdg2HLnZFUzOnVAEQNsnKXW095IGFgUYlonuQ+jotRfLEf3hm
IgQuEdz+6WvdabYC1igIWN9od6fnoNI3NSRwuttvnJVWX4FkVnhu1YRdGdNkGg==
-----END CERTIFICATE-----"""

def "refresh certificate with chain"() {
CertificateEvent firedEvent

given:
def oracleCloudCertificationsConfiguration = new OracleCloudCertificationsConfiguration("testId", 0, "testName", true)
def mockCertificates = Mock(Certificates)
def mockApplicationEventPublisher = Mock(ApplicationEventPublisher)

def service = new OracleCloudCertificateService(
oracleCloudCertificationsConfiguration, mockCertificates, mockApplicationEventPublisher)

when:
service.refreshCertificate()

then:
1 * mockCertificates.getCertificateBundle(*_) >> GetCertificateBundleResponse.builder()
.certificateBundle(
CertificateBundleWithPrivateKey.builder()
.privateKeyPem(PRIVATE_KEY)
.certificateId("testId")
.serialNumber("test")
.timeCreated(new Date())
.certChainPem(CERTIFICATE_CHAIN_STRING)
.validity(Validity.builder().timeOfValidityNotBefore(new Date()).timeOfValidityNotAfter(new Date()).build())
.certificatePem(CERTIFICATE_STRING).build())
.build()

1 * mockApplicationEventPublisher.publishEvent(*_) >> {arguments -> firedEvent=arguments[0]}
firedEvent != null
firedEvent.privateKey() != null
firedEvent.intermediate() != null
firedEvent.intermediate().size() == 3
firedEvent.certificate() != null
}

def "refresh certificate"() {
CertificateEvent firedEvent
given:
def oracleCloudCertificationsConfiguration = new OracleCloudCertificationsConfiguration("testId", 0, "testName", true)
def mockCertificates = Mock(Certificates)
Expand All @@ -95,7 +226,12 @@ IgQuEdz+6WvdabYC1igIWN9od6fnoNI3NSRwuttvnJVWX4FkVnhu1YRdGdNkGg==
.certificatePem(CERTIFICATE_STRING).build())
.build()

1 * mockApplicationEventPublisher.publishEvent(*_)
1 * mockApplicationEventPublisher.publishEvent(*_) >> {arguments -> firedEvent=arguments[0]}
firedEvent != null
firedEvent.privateKey() != null
firedEvent.intermediate() != null
firedEvent.intermediate().size() == 0
firedEvent.certificate() != null
}

def "refresh certificate with invalid private key"() {
Expand Down

0 comments on commit 65f379b

Please sign in to comment.