From a0064f01331ffc27c753e0b8e13dc1cd2d2819e2 Mon Sep 17 00:00:00 2001 From: Markus Perndorfer Date: Wed, 1 Apr 2026 13:30:25 +0200 Subject: [PATCH] fix ZTIS certificate caching refresh the cache if certificate is close to expiration --- cloudplatform/connectivity-oauth/pom.xml | 10 + .../SecurityLibWorkaroundsTest.java | 114 +++++++++++ cloudplatform/connectivity-ztis/pom.xml | 10 + .../ZeroTrustIdentityService.java | 10 +- .../ZeroTrustIdentityServiceTest.java | 191 +++++++++++++----- .../ZeroTrustIdentityServiceTest/cert.pem | 84 -------- .../ZeroTrustIdentityServiceTest/key.pem | 5 - release_notes.md | 1 + 8 files changed, 283 insertions(+), 142 deletions(-) delete mode 100644 cloudplatform/connectivity-ztis/src/test/resources/ZeroTrustIdentityServiceTest/cert.pem delete mode 100644 cloudplatform/connectivity-ztis/src/test/resources/ZeroTrustIdentityServiceTest/key.pem diff --git a/cloudplatform/connectivity-oauth/pom.xml b/cloudplatform/connectivity-oauth/pom.xml index 4335d9e8e..93fb985c8 100644 --- a/cloudplatform/connectivity-oauth/pom.xml +++ b/cloudplatform/connectivity-oauth/pom.xml @@ -155,6 +155,16 @@ runtime + + org.bouncycastle + bcprov-jdk18on + test + + + org.bouncycastle + bcpkix-jdk18on + test + com.sap.cloud.environment.servicebinding.api java-access-api diff --git a/cloudplatform/connectivity-oauth/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/SecurityLibWorkaroundsTest.java b/cloudplatform/connectivity-oauth/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/SecurityLibWorkaroundsTest.java index 0899cd4c0..180e0a40d 100644 --- a/cloudplatform/connectivity-oauth/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/SecurityLibWorkaroundsTest.java +++ b/cloudplatform/connectivity-oauth/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/SecurityLibWorkaroundsTest.java @@ -3,13 +3,30 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; import java.security.KeyStore; +import java.security.Provider; +import java.security.Security; +import java.security.cert.Certificate; +import java.util.Date; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.junit.jupiter.api.Test; import com.sap.cloud.sdk.cloudplatform.connectivity.SecurityLibWorkarounds.ZtisClientIdentity; import com.sap.cloud.security.config.CredentialType; +import lombok.SneakyThrows; + class SecurityLibWorkaroundsTest { @Test @@ -23,6 +40,103 @@ void testZtisClientIdentityImplementsEquals() assertThat(sut).doesNotHaveSameHashCodeAs(new ZtisClientIdentity("id2", mock(KeyStore.class))); } + @SneakyThrows + @Test + void testZtisClientIdentityEqualityWithRealCertificates() + { + // Two keystores loaded with different certificates must be considered unequal. + // This is what drives cache-key differentiation in OAuth2Service.tokenServiceCache + // when the SVID cert rotates. + final KeyPair keyPair = generateKeyPair(); + final Certificate certA = generateCertificate(keyPair, "CN=cert-a"); + final Certificate certB = generateCertificate(keyPair, "CN=cert-b"); + + final KeyStore ksA = buildKeyStore(keyPair, certA); + final KeyStore ksB = buildKeyStore(keyPair, certB); + final KeyStore ksSameAsA = buildKeyStore(keyPair, certA); + + final ZtisClientIdentity identityA = new ZtisClientIdentity("client", ksA); + final ZtisClientIdentity identityB = new ZtisClientIdentity("client", ksB); + final ZtisClientIdentity identitySameAsA = new ZtisClientIdentity("client", ksSameAsA); + + // Different cert → unequal: a new tokenServiceCache entry would be created after rotation + assertThat(identityA).isNotEqualTo(identityB); + assertThat(identityA).doesNotHaveSameHashCodeAs(identityB); + + // Same cert content → equal: tokenServiceCache hit, HttpClient is reused (expected behaviour) + assertThat(identityA).isEqualTo(identitySameAsA); + assertThat(identityA).hasSameHashCodeAs(identitySameAsA); + } + + @SneakyThrows + @Test + void testOAuth2ServiceTokenCacheKeyChangesWhenCertRotates() + { + // This test documents the self-healing behaviour of OAuth2Service.tokenServiceCache: + // when the SVID cert rotates, getZtisIdentity() produces a new ZtisClientIdentity with a + // different certificate, which hashes to a different cache key, causing tokenServiceCache + // to create a new OAuth2TokenService (and thus a new HttpClient with a fresh SSLContext). + // + // PRECONDITION for this to work: tryGetDestination() (and therefore getZtisIdentity()) + // must be called again after cert rotation. If the HttpDestination is cached at a higher + // level and re-used indefinitely, the ZtisClientIdentity inside OAuth2Service is never + // refreshed and the cache key never changes. + final KeyPair keyPair = generateKeyPair(); + final Certificate certBeforeRotation = generateCertificate(keyPair, "CN=before-rotation"); + final Certificate certAfterRotation = generateCertificate(keyPair, "CN=after-rotation"); + + final KeyStore ksBefore = buildKeyStore(keyPair, certBeforeRotation); + final KeyStore ksAfter = buildKeyStore(keyPair, certAfterRotation); + + final ZtisClientIdentity identityBefore = new ZtisClientIdentity("client", ksBefore); + final ZtisClientIdentity identityAfter = new ZtisClientIdentity("client", ksAfter); + + // Simulate OAuth2Service.getTokenService() cache key computation + final com.sap.cloud.sdk.cloudplatform.cache.CacheKey keyBefore = + com.sap.cloud.sdk.cloudplatform.cache.CacheKey.fromIds(null, null).append(identityBefore); + final com.sap.cloud.sdk.cloudplatform.cache.CacheKey keyAfter = + com.sap.cloud.sdk.cloudplatform.cache.CacheKey.fromIds(null, null).append(identityAfter); + + // Cache keys must differ → cache miss → new HttpClient built with rotated cert + assertThat(keyBefore).isNotEqualTo(keyAfter); + } + + @SneakyThrows + private static KeyPair generateKeyPair() + { + final KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + kpg.initialize(2048); + return kpg.generateKeyPair(); + } + + @SneakyThrows + private static Certificate generateCertificate( final KeyPair keyPair, final String subject ) + { + final long now = System.currentTimeMillis(); + final X500Name name = new X500Name(subject); + final BigInteger serial = new BigInteger(Long.toString(now)); + final Date startDate = new Date(now); + final Date endDate = new Date(now + 3_600_000L); + + final JcaX509v3CertificateBuilder certBuilder = + new JcaX509v3CertificateBuilder(name, serial, startDate, endDate, name, keyPair.getPublic()); + certBuilder.addExtension(new ASN1ObjectIdentifier("2.5.29.19"), true, new BasicConstraints(true)); + + final Provider prov = new BouncyCastleProvider(); + Security.addProvider(prov); + final ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSA").build(keyPair.getPrivate()); + return new JcaX509CertificateConverter().setProvider(prov).getCertificate(certBuilder.build(contentSigner)); + } + + @SneakyThrows + private static KeyStore buildKeyStore( final KeyPair keyPair, final Certificate cert ) + { + final KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(null); + ks.setKeyEntry("spiffe", keyPair.getPrivate(), new char[0], new Certificate[] { cert }); + return ks; + } + @Test void testGetCredentialType() { diff --git a/cloudplatform/connectivity-ztis/pom.xml b/cloudplatform/connectivity-ztis/pom.xml index e807fa422..c5c2570e1 100644 --- a/cloudplatform/connectivity-ztis/pom.xml +++ b/cloudplatform/connectivity-ztis/pom.xml @@ -123,6 +123,16 @@ assertj-core test + + org.bouncycastle + bcprov-jdk18on + test + + + org.bouncycastle + bcpkix-jdk18on + test + org.junit.jupiter junit-jupiter-api diff --git a/cloudplatform/connectivity-ztis/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/ZeroTrustIdentityService.java b/cloudplatform/connectivity-ztis/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/ZeroTrustIdentityService.java index b9433549d..c66b060b3 100644 --- a/cloudplatform/connectivity-ztis/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/ZeroTrustIdentityService.java +++ b/cloudplatform/connectivity-ztis/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/ZeroTrustIdentityService.java @@ -49,6 +49,8 @@ public class ZeroTrustIdentityService private static final String DEFAULT_SOCKET_PATH = "unix:///tmp/spire-agent/public/api.sock"; private static final String SOCKET_ENVIRONMENT_VARIABLE = "SPIFFE_ENDPOINT_SOCKET"; private static final Duration DEFAULT_SOCKET_TIMEOUT = Duration.ofSeconds(10); + // Invalidate the cached KeyStore this long before the SVID expires to tolerate slow SPIRE rotation + static final Duration SVID_EXPIRY_SAFETY_MARGIN = Duration.ofDays(1); @Getter private static final ZeroTrustIdentityService instance = new ZeroTrustIdentityService(); private final Lazy source = Lazy.of(this::initX509Source); @@ -223,8 +225,12 @@ KeyStore loadKeyStore( @Nonnull final X509Svid svid ) boolean isKeyStoreCached( @Nonnull final X509Svid svid ) { - // X509Svid does implement equals, so we don't have to manually compare the certificates - return keyStoreCache != null && svid.equals(keyStoreCache.svid()); + if( keyStoreCache == null || !svid.getSpiffeId().equals(keyStoreCache.svid().getSpiffeId()) ) { + return false; + } + // Treat as not cached if the SVID is already expired or expires within the safety margin. + final Instant expiryWithMargin = svid.getLeaf().getNotAfter().toInstant().minus(SVID_EXPIRY_SAFETY_MARGIN); + return Instant.now().isBefore(expiryWithMargin); } @RequiredArgsConstructor diff --git a/cloudplatform/connectivity-ztis/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/ZeroTrustIdentityServiceTest.java b/cloudplatform/connectivity-ztis/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/ZeroTrustIdentityServiceTest.java index b9e064853..6c0100c39 100644 --- a/cloudplatform/connectivity-ztis/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/ZeroTrustIdentityServiceTest.java +++ b/cloudplatform/connectivity-ztis/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/ZeroTrustIdentityServiceTest.java @@ -1,22 +1,43 @@ package com.sap.cloud.sdk.cloudplatform.connectivity; +import static com.sap.cloud.sdk.cloudplatform.connectivity.ZeroTrustIdentityService.SVID_EXPIRY_SAFETY_MARGIN; import static com.sap.cloud.sdk.cloudplatform.connectivity.ZeroTrustIdentityService.ZTIS_IDENTIFIER; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import java.security.KeyStore; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Provider; +import java.security.Security; import java.security.cert.X509Certificate; import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.Date; import java.util.Map; +import javax.annotation.Nonnull; + +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.ExtendedKeyUsage; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.KeyPurposeId; +import org.bouncycastle.asn1.x509.KeyUsage; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -27,20 +48,31 @@ import com.sap.cloud.sdk.cloudplatform.exception.CloudPlatformException; import io.spiffe.svid.x509svid.X509Svid; +import lombok.SneakyThrows; class ZeroTrustIdentityServiceTest { - private static final ServiceBinding binding = mockBinding(); + private static final String SPIFFE_ID = "spiffe://example.org/workload"; + private static final ServiceBinding BINDING = mockBinding(); + + private static KeyPair keyPair; private ZeroTrustIdentityService sut; - private X509Svid svidMock; + + @BeforeAll + @SneakyThrows + static void setUpClass() + { + Security.addProvider(new BouncyCastleProvider()); + final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC"); + kpg.initialize(256); + keyPair = kpg.generateKeyPair(); + } @BeforeEach void setUp() { - sut = spy(new ZeroTrustIdentityService(binding)); - mockSvid(Instant.now().plusSeconds(300)); - doReturn(mock(KeyStore.class)).when(sut).loadKeyStore(any()); + sut = spy(new ZeroTrustIdentityService(BINDING)); } @Test @@ -48,7 +80,7 @@ void testLazyInitialization() { // it's important here to spy using the class, not on an already existing instance // otherwise the method reference stored in Lazy.of(this::initX509Source) would not point to the mock - sut = spy(ZeroTrustIdentityService.class); + final ZeroTrustIdentityService sut = spy(ZeroTrustIdentityService.class); verify(sut, never()).initX509Source(); assertThatThrownBy(sut::getOrCreateKeyStore).isInstanceOf(CloudPlatformException.class); @@ -61,22 +93,31 @@ void testLazyInitialization() @Test void testKeyStoreCache() { - assertThat(sut.isKeyStoreCached(svidMock)).isFalse(); + // Cache is keyed by SpiffeId, not by SVID object reference. + // Two SVIDs with the same SpiffeId and a valid expiry share the same cache entry. + final X509Svid svid = newSvid(Instant.now().plus(SVID_EXPIRY_SAFETY_MARGIN).plusSeconds(300)); + doReturn(svid).when(sut).getX509Svid(); + + assertThat(sut.isKeyStoreCached(svid)).isFalse(); sut.getOrCreateKeyStore(); - assertThat(sut.isKeyStoreCached(svidMock)).isTrue(); + assertThat(sut.isKeyStoreCached(svid)).isTrue(); + // Second call with the same SVID reference → cache hit, loadKeyStore not called again sut.getOrCreateKeyStore(); - verify(sut, times(1)).loadKeyStore(svidMock); + verify(sut, times(1)).loadKeyStore(svid); - // actually the time doesn't matter here, as the logic depends on the equals comparison of the X509Svid - // however, equals cannot be mocked, so equals compares the mock objects, which would be different even with the same times - // we could make this test more realistic by using real certificate + key objects - mockSvid(Instant.now().plusSeconds(20)); + // New SVID object with the same SpiffeId (simulates SPIRE delivering a rotated cert with the same identity) + // → still a cache HIT as long as the cert is well within the expiry margin. + // The expiry margin is the sole invalidation mechanism; SpiffeId equality means "same workload". + final X509Svid rotated = + newSvid(Instant.now().plus(ZeroTrustIdentityService.SVID_EXPIRY_SAFETY_MARGIN).plusSeconds(300)); + doReturn(rotated).when(sut).getX509Svid(); - assertThat(sut.isKeyStoreCached(svidMock)).isFalse(); + assertThat(sut.isKeyStoreCached(rotated)).isTrue(); sut.getOrCreateKeyStore(); - assertThat(sut.isKeyStoreCached(svidMock)).isTrue(); + // loadKeyStore was not called a second time — cached KeyStore is reused + verify(sut, times(1)).loadKeyStore(any()); } @Test @@ -90,10 +131,42 @@ void testThrowsWithoutBinding() @Test void testCheckForInvalidCertificate() { - mockSvid(Instant.now().minusSeconds(20)); + final X509Svid expired = newSvid(Instant.now().minusSeconds(20)); + doReturn(expired).when(sut).getX509Svid(); assertThatThrownBy(sut::getOrCreateKeyStore).isInstanceOf(IllegalStateException.class); } + @Test + void testCachedKeyStoreIsRejectedWhenCachedSvidHasExpired() + { + final X509Svid svid = newSvid(Instant.now().plus(SVID_EXPIRY_SAFETY_MARGIN).plusSeconds(300)); + doReturn(svid).when(sut).getX509Svid(); + + sut.getOrCreateKeyStore(); + assertThat(sut.isKeyStoreCached(svid)).isTrue(); + + // Simulate SPIRE being slow: a new SVID arrives with the same SpiffeId but its cert is already past notAfter. + final X509Svid expiredSvid = newSvid(Instant.now().plus(SVID_EXPIRY_SAFETY_MARGIN).minusSeconds(1)); + doReturn(expiredSvid).when(sut).getX509Svid(); + + assertThat(sut.isKeyStoreCached(expiredSvid)).isFalse(); + } + + @Test + void testCachedKeyStoreIsRejectedWhenSvidIsAboutToExpire() + { + // SVID expiring within the safety margin must not be considered cached, + // even when it is the same object reference + final X509Svid nearExpiry = newSvid(Instant.now().plus(SVID_EXPIRY_SAFETY_MARGIN).minus(1, ChronoUnit.HOURS)); + doReturn(nearExpiry).when(sut).getX509Svid(); + + // getOrCreateKeyStore() fills the cache (assertSvidNotExpired does not fire — cert is still valid) + sut.getOrCreateKeyStore(); + + // isKeyStoreCached must return false: expiry margin kicks in + assertThat(sut.isKeyStoreCached(nearExpiry)).isFalse(); + } + @Test void testSpiffeId() { @@ -108,7 +181,7 @@ void testAppIdentifier() final DefaultServiceBinding emptyBinding = new DefaultServiceBindingBuilder().withServiceIdentifier(ZTIS_IDENTIFIER).build(); - sut = new ZeroTrustIdentityService(emptyBinding); + sut = spy(new ZeroTrustIdentityService(emptyBinding)); assertThat(sut.getAppIdentifier()).isEmpty(); final DefaultServiceBinding emptyValue = @@ -117,49 +190,65 @@ void testAppIdentifier() .withCredentials(Map.of("parameters", Map.of("app-identifier", ""))) .build(); - sut = new ZeroTrustIdentityService(emptyValue); + sut = spy(new ZeroTrustIdentityService(emptyValue)); assertThat(sut.getAppIdentifier()).isEmpty(); } - @Test - void testCertOnFileSystem() + // -- helpers -- + + /** + * Generates a SPIFFE-compliant X.509 certificate with the given validity window and parses it into an + * {@link X509Svid}. Uses a shared EC key pair so only the certificate (and its expiry) differs between calls. + */ + @SneakyThrows + @Nonnull + static X509Svid newSvid( @Nonnull final Instant notAfter ) { - final ServiceBinding binding = - new DefaultServiceBindingBuilder() - .withServiceIdentifier(ZTIS_IDENTIFIER) - .withCredentials( - Map - .of( - "certPath", - "src/test/resources/ZeroTrustIdentityServiceTest/cert.pem", - "keyPath", - "src/test/resources/ZeroTrustIdentityServiceTest/key.pem")) - .build(); + final long now = System.currentTimeMillis(); + final X500Name subject = new X500Name("CN=test-svid"); + final JcaX509v3CertificateBuilder certBuilder = + new JcaX509v3CertificateBuilder( + subject, + BigInteger.valueOf(now), + new Date(now - 1000), + Date.from(notAfter), + subject, + keyPair.getPublic()); - final ZeroTrustIdentityService sut = new ZeroTrustIdentityService(binding); - final X509Svid svid = sut.getX509Svid(); + // SPIFFE URI SAN — required by X509Svid.parse() + certBuilder + .addExtension( + Extension.subjectAlternativeName, + false, + new GeneralNames(new GeneralName(GeneralName.uniformResourceIdentifier, SPIFFE_ID))); - assertThat(svid.getLeaf().getSubjectX500Principal().getName()).contains("OU=Zero Trust Identity Service"); - assertThat(svid.getPrivateKey()).isNotNull(); - assertThat(svid.getSpiffeId().getTrustDomain().getName()).isEqualTo("0trust.net.sap"); + // leaf certificate requirements imposed by X509Svid.parse(): + // - digitalSignature key usage + // - no keyCertSign / cRLSign + // - BasicConstraints CA=false + certBuilder.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature)); + certBuilder + .addExtension(Extension.extendedKeyUsage, false, new ExtendedKeyUsage(KeyPurposeId.id_kp_clientAuth)); + certBuilder.addExtension(Extension.basicConstraints, true, new BasicConstraints(false)); - assertThat(svid) - .describedAs("Our cache relies on the equals implementation of SVIDs") - .isEqualTo(sut.getX509Svid()); + final Provider bc = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME); + final ContentSigner signer = + new JcaContentSignerBuilder("SHA256withECDSA").setProvider(bc).build(keyPair.getPrivate()); + final X509Certificate cert = + new JcaX509CertificateConverter().setProvider(bc).getCertificate(certBuilder.build(signer)); - assertThatThrownBy(sut::getOrCreateKeyStore) - .describedAs("KeyStore creation should fail since the cert has expired") - .isInstanceOf(IllegalStateException.class); + // SPIFFE library expects PKCS#8 PEM ("PRIVATE KEY"), not SEC1 ("EC PRIVATE KEY") + final byte[] certPem = toPem("CERTIFICATE", cert.getEncoded()); + final byte[] keyPem = toPem("PRIVATE KEY", keyPair.getPrivate().getEncoded()); + return X509Svid.parse(certPem, keyPem); } - private void mockSvid( Instant notAfter ) + @Nonnull + private static byte[] toPem( @Nonnull final String type, @Nonnull final byte[] der ) { - final X509Svid svid = mock(X509Svid.class); - final X509Certificate certificate = mock(X509Certificate.class); - doReturn(Date.from(notAfter)).when(certificate).getNotAfter(); - doReturn(certificate).when(svid).getLeaf(); - doReturn(svid).when(sut).getX509Svid(); - svidMock = svid; + final String base64 = java.util.Base64.getMimeEncoder(64, new byte[] { '\n' }).encodeToString(der); + final String pem = "-----BEGIN " + type + "-----\n" + base64 + "\n-----END " + type + "-----\n"; + return pem.getBytes(java.nio.charset.StandardCharsets.UTF_8); } private static ServiceBinding mockBinding() diff --git a/cloudplatform/connectivity-ztis/src/test/resources/ZeroTrustIdentityServiceTest/cert.pem b/cloudplatform/connectivity-ztis/src/test/resources/ZeroTrustIdentityServiceTest/cert.pem deleted file mode 100644 index b68f4c63f..000000000 --- a/cloudplatform/connectivity-ztis/src/test/resources/ZeroTrustIdentityServiceTest/cert.pem +++ /dev/null @@ -1,84 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDUzCCAvmgAwIBAgIQFrwyv3kg1y6j1gIIHLzY4jAKBggqhkjOPQQDAjCBjzEL -MAkGA1UEBhMCREUxDzANBgNVBAoTBlNBUCBTRTEYMBYGA1UECxMPU0FQIEJUUCBD -bGllbnRzMRMwEQYDVQQLEwpESTpHQ1AtRVUxMRcwFQYDVQQLEw4wdHJ1c3QubmV0 -LnNhcDEnMCUGA1UEAxMeWmVybyBUcnVzdCBJZGVudGl0eSBTZXJ2aWNlIENBMB4X -DTI0MDIxNDE2MTg0NVoXDTI0MDIyMTE2MTg1NVowgdoxCzAJBgNVBAYTAkRFMQ8w -DQYDVQQKEwZTQVAgU0UxGDAWBgNVBAsTD1NBUCBCVFAgQ2xpZW50czETMBEGA1UE -CxMKREk6R0NQLUVVMTEXMBUGA1UECxMOMHRydXN0Lm5ldC5zYXAxJzAlBgNVBAsT -Hlplcm8gVHJ1c3QgSWRlbnRpdHkgU2VydmljZSBDQTFJMEcGA1UEAxNAMzI3ODM1 -MjZlNjdlNzAzMzEyZTljYmU5MDExMDgzMWU3ZjA2ZmRiMmE3NmQxY2M1NmFiMGZl -NTRhNjNkZmEzNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEGEOwzXluGX1HFW -pOqIcEmmHQa8Urz9umagIG6BuxfDUx9sHJsxu98At2XLowQIdAX/19T+hdLarEm3 -i20GX7+jgekwgeYwDgYDVR0PAQH/BAQDAgOoMB0GA1UdJQQWMBQGCCsGAQUFBwMB -BggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBT7q/+3U52s9XdASuaN -xzIAn76o3zAfBgNVHSMEGDAWgBSZLO9xp+WgWo3e6ogW8qqSFo4K7DBnBgNVHREE -YDBehlxzcGlmZmU6Ly8wdHJ1c3QubmV0LnNhcC92MDEvNGI0MDg1ZmZmNGI2MmJk -YzBjZGNhNjdlZGRhMGEyODQzMTQyNGMyYjU0YTU2ODkwNzgwNTRlMWFhYjgxZGEz -NjAKBggqhkjOPQQDAgNIADBFAiBbuf+RTh6VwWWdXUJr3y7ptwyo2wJB8FigXrxh -wXGMtAIhAOsASAJ22PdR+4DWUE/QD7CwBMo5oMjzGtxSZMwusBPq ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIE2jCCAsKgAwIBAgIRAJeqUMBtvrxU18d/8hW71qEwDQYJKoZIhvcNAQELBQAw -YzELMAkGA1UEBhMCREUxDTALBgNVBAcMBEVVMTAxDzANBgNVBAoMBlNBUCBTRTEY -MBYGA1UECwwPU0FQIEJUUCBDbGllbnRzMRowGAYDVQQDDBFTQVAgQlRQIENsaWVu -dCBDQTAeFw0yNDAyMDcwNzM0NDZaFw0yNDAzMjYwODM0NDZaMIGPMQswCQYDVQQG -EwJERTEPMA0GA1UEChMGU0FQIFNFMRgwFgYDVQQLEw9TQVAgQlRQIENsaWVudHMx -EzARBgNVBAsTCkRJOkdDUC1FVTExFzAVBgNVBAsTDjB0cnVzdC5uZXQuc2FwMScw -JQYDVQQDEx5aZXJvIFRydXN0IElkZW50aXR5IFNlcnZpY2UgQ0EwWTATBgcqhkjO -PQIBBggqhkjOPQMBBwNCAAR0OzIA/EZ8KzpfZ7BTWkj45Gg9BS8RtpeMU70EZfiK -q9JZ/ihIvuMGCaioQTMy1xZ1Uf4bz7rbhXYWi6Qb/YTxo4IBJTCCASEwIgYDVR0R -BBswGYYXc3BpZmZlOi8vMHRydXN0Lm5ldC5zYXAwEgYDVR0TAQH/BAgwBgEB/wIB -ADAfBgNVHSMEGDAWgBSfOpEReBQ7+OAn9OlW7j/auhQPfTAdBgNVHQ4EFgQUmSzv -cafloFqN3uqIFvKqkhaOCuwwfgYDVR0fBHcwdTBzoHGgb4ZtaHR0cDovL3NhcC1i -dHAtY2xpZW50LWNhLWV1MTAtY3Jscy5zMy5ldS1jZW50cmFsLTEuYW1hem9uYXdz -LmNvbS9jcmwvOWU1NjM4OWYtMDE2ZS00OTYzLWEwYWUtYjllMzg1NzE4ZjkxLmNy -bDAXBgNVHSAEEDAOMAwGCisGAQQBhTYECgEwDgYDVR0PAQH/BAQDAgEGMA0GCSqG -SIb3DQEBCwUAA4ICAQBUxchZKgegYJX0WN50wrr2MQcDhqJIyptpxI9fQ+YYO1Bn -S55wtYyODff9W7V3gHYDSv48wccYs3ApZTExGGQ2amKiHIcKNZHwDT4rLN5kE9rL -O5CvhvtLA+n88vZNV/LwToFRZN6I9lz3/8MMILfLSGyyQZ5VjGLQx8bycgtd+39J -QWLhmeJphKrcRjBntU8yCvXDW/Wn1palc3aYrXi32Ig5hcF7WK5lTozTsLwlBNir -JosvmpbiH6t1qfBNeidWYZyYN30WYGIdnzohOQaGVj/tJnHIOXyLSpMsut6K27TB -t6G7xJS1ONLFBIwibUV4YCzvON1Vq/BQfsQU0BV2HsUzx+q9FK9Iobbtwab11xKb -kvj9BfYaHV5hVDNdIzkWenkWcUpDo9zyvCOs98wx95UQxJp41u8HBDLvcCSXxRVe -0aYIwkRQQRay12RpvcAhtKBX3rRsaaLZEZ1185bbLjEZDyxNu6vxTymIIGQW11se -guUrnf3ExjIH+4WuXdTFptIf5ubXPlhDzA3OSXjFpUMyP/clpGyD3htALhlsDj4M -0T5a4Lste7XjFZw9j+4iNZzm8eptFY1A5lMSxZMeReQR3mR7MKjeIMN0M58lmQxZ -50T1eeUWFQfK7xHuNt/G8szHpr4kjYIgj3b2Mu6DpkX3C2Mu8PbvCoYupf0OIg== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGSjCCBDKgAwIBAgITcAAAAApMdyh1162sZgAAAAAACjANBgkqhkiG9w0BAQsF -ADBNMQswCQYDVQQGEwJERTERMA8GA1UEBwwIV2FsbGRvcmYxDzANBgNVBAoMBlNB -UCBTRTEaMBgGA1UEAwwRU0FQIENsb3VkIFJvb3QgQ0EwHhcNMjIwNTA5MDgzMzU1 -WhcNMzIwNTA5MDg0MzU1WjBjMQswCQYDVQQGEwJERTENMAsGA1UEBwwERVUxMDEP -MA0GA1UECgwGU0FQIFNFMRgwFgYDVQQLDA9TQVAgQlRQIENsaWVudHMxGjAYBgNV -BAMMEVNBUCBCVFAgQ2xpZW50IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC -CgKCAgEAtVs/vbOU9P8soC0Y3GNzbLwbbP4z1MC38xMYZlbDLDapaj9k3LF9LSXu -kHK+tnOmuKX2aFRouYTusJ/mDL3DsYcgEERPNPDnq1lpp3nLr8aPJLGlj7G8bfGW -dvTVk0Trt9PpwjsbE2J/R0qs5Y0idlb8DOJ2ilRFOK5zsX3YscRKlHknTwlwrNN4 -WjPeNQLMbDoAI+YTs7luCIHItmpb3/ls+bup8hCg7rYOh4mDv3nuHq2OHmc6+2XG -2Rm6ngrgZwz8Yr3bk7qaWfu2iVPn9nFJhH0+r/oCXWNiUwL3+uJ+pLJKfzYJ/bwl -XJblYerj3rgs76zhoXX6RJFQ94LAB4qeARtVde87bpD6xzFqUM1mqEyNmPMOwdRC -4BtOQuYflabIsW/duISQni1e/K27KM/ry59KybdOJav5y+SXT2ZANa7z4+x2nm2y -10BOhClZv//m3XpiCmAp62y5qfJSknsXmOHzol0fmI5OUbvuvB9po3G6A6wD/ME6 -IdacsqCG2/u8KaGkMYjeLU/hX6DQ3IcloZlD6wloZYyJmNHlbm/367q0LLoSPsxg -VU9Y66E/kjDkCg+v4lkuZ6N3LI3BXdOvLmbRAkXM4/0ssBWKmhD4uwEa7V24TIEe -mlizpFO763SyVxBpKWfLI+hUq1ETUdCC697B8vllB7x0otXT+FECAwEAAaOCAQsw -ggEHMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFJ86kRF4FDv44Cf06Vbu -P9q6FA99MB8GA1UdIwQYMBaAFBy8ZisOyo1Ln42TcakPymdGaRMiMEoGA1UdHwRD -MEEwP6A9oDuGOWh0dHA6Ly9jZHAucGtpLmNvLnNhcC5jb20vY2RwL1NBUCUyMENs -b3VkJTIwUm9vdCUyMENBLmNybDBVBggrBgEFBQcBAQRJMEcwRQYIKwYBBQUHMAKG -OWh0dHA6Ly9haWEucGtpLmNvLnNhcC5jb20vYWlhL1NBUCUyMENsb3VkJTIwUm9v -dCUyMENBLmNydDAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAG+f -q3auXEbXpb2d2bzsK1Vm+jqJCToHypZH+L/elxr9wC4+RpT9YOdHWBKghO0c1xAK -L5kfDRn9vtobdh11dfDKdFRmWJU5pwU/Ok+XLE+moCZ2LkJy1uze7eJnpFhMs6/F -s3AgvAROpz7Mi7ChWiGJfEYJQediOE8cmoTJ1KYOjK+ZM02viBA92ShKNL1xk/LK -JZgpZdp2iYrPyqJsZzosaPyT4cBSs5+qrynf7/Z3ENoTD2/yUWwxLBgWOtWhoEXR -D4tlP4ITDo03Vmvp4XZuUSN583npcf5H1afISPrpqamlVHD4T8/NYi6vb0bJW8fD -333Jpz6LEfJtVrTT8s1Xl8oGKp0wu++1Jvg5ojYw8Kbk+DjS9H4hKIbfKtpK+d8R -OJuLMwcdiD+gL5tmeuGgleQZwRk96+oYOGGpPhkgxmAKGuoVDbB1yY0Uinm4eBJ3 -2wCBwDBFXbWAMoKWKIvARpIu822q26UuAuov7BBfDnrTXIoMvBwM73pYsaOS4jRG -hRWyDnBm76CiEkI8av2fJoCZTs8itYebNBbIxwimCpYeU4y8K4z4rwNpCJtNY3YN -pZ7y7TajEf9jXY/2PCXjSOzr9kUi4pPGfSMKXVzIV16upXp1uF2dyzOYAAHQ/UWO -ZeYjiLJ5fLnwllwT7UjJqd0DTRoC7hlKyuxv493p ------END CERTIFICATE----- diff --git a/cloudplatform/connectivity-ztis/src/test/resources/ZeroTrustIdentityServiceTest/key.pem b/cloudplatform/connectivity-ztis/src/test/resources/ZeroTrustIdentityServiceTest/key.pem deleted file mode 100644 index 8cfdc47d4..000000000 --- a/cloudplatform/connectivity-ztis/src/test/resources/ZeroTrustIdentityServiceTest/key.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgOt7smVohjk5hhbL0 -iXozlFrgtoBXVKtVzadX3X7ohv6hRANCAARCv3HNbB6kWJwEwfHwFEO/7VIE8GBF -59iI7p7nZWXmugUfa2Lnc32ijODsVbN7i+XkMWB+b7C3yL2LYBXz07ts ------END PRIVATE KEY----- \ No newline at end of file diff --git a/release_notes.md b/release_notes.md index b65d73ffa..1d6584e46 100644 --- a/release_notes.md +++ b/release_notes.md @@ -22,3 +22,4 @@ ### 🐛 Fixed Issues - Fixed stateful OData request path construction caused by shared `ODataResourcePath` instances being mutated when building count, read-by-key, and function requests. +- Fixed using expired Zero Trust Identity Service certificates