Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
# Change Log
All notable changes to this project will be documented in this file.

## 4.1.0
## 4.0.8

- Fix IAS proof-token validation regression under Istio / Kyma with `credential-type: X509_GENERATED`
- `SapIdJwtSignatureValidator` was setting the `x-client_cert` request header directly from `X509Certificate#getPEM()`. When the certificate originates from Istio's `x-forwarded-client-cert` (XFCC) header, the PEM includes `-----BEGIN CERTIFICATE-----` / `-----END CERTIFICATE-----` delimiters and CR/LF line breaks
- Since 4.0.0, the token-client HTTP transport uses Java 11's `HttpClient`, which enforces RFC 7230 and rejects any header value containing CR/LF with `IllegalArgumentException` (surfaced as `"Token signature can not be validated because: invalid header value: <PEM>"`). Every authenticated request behind Istio failed proof-token validation as a result
- The header value is now sanitized to bare base64-encoded DER (PEM delimiters and all whitespace stripped) before being placed on the wire. The IAS JWKS endpoint accepts this form. A new `X509Certificate#getPEMHeaderValue()` accessor keeps the sanitization next to the PEM parsing; `getPEM()`'s existing contract is unchanged
- `JavaHttpClientAdapter` additionally fails fast with a message naming the offending header key (not the value, which may be sensitive) if a header value ever contains CR/LF, turning any future regression from a misleading `"invalid header value: <PEM>"` into an actionable transport-layer error
- Update dependencies:
- Spring Boot: 4.0.6 → 4.1.0
- Spring Framework: 7.0.7 → 7.0.8
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ If your application uses Spring Boot 3.x and you cannot immediately upgrade to S
<dependency>
<groupId>com.sap.cloud.security</groupId>
<artifactId>resourceserver-security-spring-boot-3-starter</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
</dependency>
```

Expand Down Expand Up @@ -275,7 +275,7 @@ The SAP Cloud Security Services Integration is published to maven central: https
<dependency>
<groupId>com.sap.cloud.security</groupId>
<artifactId>java-bom</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
<scope>import</scope>
<type>pom</type>
</dependency>
Expand Down
2 changes: 1 addition & 1 deletion bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<groupId>com.sap.cloud.security</groupId>
<artifactId>java-bom</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
<packaging>pom</packaging>
<name>java-bom</name>

Expand Down
2 changes: 1 addition & 1 deletion env/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<parent>
<groupId>com.sap.cloud.security.xsuaa</groupId>
<artifactId>parent</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
</parent>

<groupId>com.sap.cloud.security</groupId>
Expand Down
2 changes: 1 addition & 1 deletion java-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
<dependency>
<groupId>com.sap.cloud.security</groupId>
<artifactId>java-api</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
</dependency>
```
2 changes: 1 addition & 1 deletion java-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<parent>
<groupId>com.sap.cloud.security.xsuaa</groupId>
<artifactId>parent</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
</parent>

<groupId>com.sap.cloud.security</groupId>
Expand Down
2 changes: 1 addition & 1 deletion java-security-test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ It is pre-configured with a security filter that only accepts valid tokens. Furt
<dependency>
<groupId>com.sap.cloud.security</groupId>
<artifactId>java-security-test</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
<scope>test</scope>
</dependency>
```
Expand Down
2 changes: 1 addition & 1 deletion java-security-test/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<parent>
<groupId>com.sap.cloud.security.xsuaa</groupId>
<artifactId>parent</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
</parent>

<groupId>com.sap.cloud.security</groupId>
Expand Down
2 changes: 1 addition & 1 deletion java-security/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Since it requires the Tomcat 10 runtime, it needs to be deployed using the [SAP
<dependency>
<groupId>com.sap.cloud.security</groupId>
<artifactId>java-security</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
Expand Down
2 changes: 1 addition & 1 deletion java-security/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<parent>
<groupId>com.sap.cloud.security.xsuaa</groupId>
<artifactId>parent</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
</parent>

<groupId>com.sap.cloud.security</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ protected PublicKey getPublicKey(Token token, JwtSignatureAlgorithm algorithm) t
} else {
Map<String, String> cacheKeyParams = new HashMap<>(requestParams);

requestParams.put(HttpHeaders.X_CLIENT_CERT, cert.getPEM());
requestParams.put(HttpHeaders.X_CLIENT_CERT, cert.getLeafCertificateAsHeaderValue());
cacheKeyParams.put(X509Constants.FWD_CLIENT_CERT_SUB, cert.getSubjectDN());

cacheKey = new OAuth2TokenKeyServiceWithCache.CacheKey(keyParams.keyUri(), cacheKeyParams);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,22 @@ public String getPEM() {
return this.pem;
}

/**
* @return the leaf certificate as bare base64-encoded DER, safe to use as an HTTP header
* value (RFC 7230 forbids CR/LF in header values). If the PEM contains multiple
* concatenated certificates only the first is kept — consistent with
* {@link #getSubjectDN()} and {@link #getThumbprint()}, which also operate on the first
* (leaf) certificate.
*/
public String getLeafCertificateAsHeaderValue() {
int begin = pem.indexOf(X509Parser.BEGIN_CERTIFICATE);
if (begin < 0) {
return pem.replaceAll("\\s", "");
}
int bodyStart = begin + X509Parser.BEGIN_CERTIFICATE.length();
int end = pem.indexOf(X509Parser.END_CERTIFICATE, bodyStart);
int bodyEnd = end > bodyStart ? end : pem.length();
return pem.substring(bodyStart, bodyEnd).replaceAll("\\s", "");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,47 @@ public void validate_withEnabledProofTokenCheck_app2service() throws IOException
assertTrue(cut.validate(iasToken).isValid());
}

@Test
public void validate_withEnabledProofTokenCheck_multiLinePemCert_headerIsSanitized() throws IOException {
String base64 = IOUtils.resourceToString("/cf-forwarded-client-cert.txt", UTF_8);
String multiLinePem = "-----BEGIN CERTIFICATE-----\n"
+ chunk(base64, 64)
+ "\n-----END CERTIFICATE-----\n";
Certificate cert = X509Certificate.newCertificate(multiLinePem);
SecurityContext.setClientCertificate(cert);

OAuth2TokenKeyService tokenKeyMock = Mockito.mock(OAuth2TokenKeyService.class);
ParamsCapturesClientCert capture = new ParamsCapturesClientCert();
when(tokenKeyMock
.retrieveTokenKeys(any(), argThat(capture)))
.thenReturn(IOUtils.resourceToString("/iasJsonWebTokenKeys.json", UTF_8));

SapIdJwtSignatureValidator cut = new SapIdJwtSignatureValidator(
mockConfiguration,
OAuth2TokenKeyServiceWithCache.getInstance()
.withTokenKeyService(tokenKeyMock),
OidcConfigurationServiceWithCache.getInstance()
.withOidcConfigurationService(oidcConfigServiceMock));
cut.enableProofTokenValidationCheck();
assertTrue(cut.validate(iasToken).isValid());

assertThat(capture.captured).isNotNull();
assertThat(capture.captured).doesNotContain("-----BEGIN");
assertThat(capture.captured).doesNotContain("-----END");
assertThat(capture.captured).doesNotContain("\n");
assertThat(capture.captured).doesNotContain("\r");
assertThat(capture.captured).doesNotContain(" ");
}

private static String chunk(String s, int width) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i += width) {
if (i > 0) sb.append('\n');
sb.append(s, i, Math.min(i + width, s.length()));
}
return sb.toString();
}

@Test
public void validate_withEnabledProofTokenCheck_app2app() {
Token iasToken = new SapIdToken(
Expand Down Expand Up @@ -286,4 +327,18 @@ public boolean matches(Map<String, String> map) {
}
}

static class ParamsCapturesClientCert implements ArgumentMatcher<Map<String, String>> {
String captured;

@Override
public boolean matches(Map<String, String> map) {
String value = map.get(HttpHeaders.X_CLIENT_CERT);
if (value != null) {
this.captured = value;
return true;
}
return false;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,62 @@ void getPem() {
assertThat(cut.getPEM()).isEqualTo(x509_base64);
}

@Test
void getLeafCertificateAsHeaderValue_stripsDelimitersAndWhitespace_fromMultilinePEM() {
String pem = "-----BEGIN CERTIFICATE-----\n"
+ chunk(x509_base64, 64)
+ "\n-----END CERTIFICATE-----\n";
X509Certificate fromPem = X509Certificate.newCertificate(pem);

String headerValue = fromPem.getLeafCertificateAsHeaderValue();
assertThat(headerValue).isEqualTo(x509_base64);
assertThat(headerValue).doesNotContain("-----BEGIN");
assertThat(headerValue).doesNotContain("-----END");
assertThat(headerValue).doesNotContain("\n");
assertThat(headerValue).doesNotContain("\r");
assertThat(headerValue).doesNotContain(" ");
}

@Test
void getLeafCertificateAsHeaderValue_fromXfccUrlEncodedPEM() throws Exception {
String pem = "-----BEGIN CERTIFICATE-----\n"
+ chunk(x509_base64, 64)
+ "\n-----END CERTIFICATE-----\n";
String xfcc = "Hash=abc;Cert=\""
+ java.net.URLEncoder.encode(pem, java.nio.charset.StandardCharsets.UTF_8)
+ "\"";
X509Certificate fromXfcc = X509Certificate.newCertificate(xfcc);

assertThat(fromXfcc).isNotNull();
assertThat(fromXfcc.getLeafCertificateAsHeaderValue()).isEqualTo(x509_base64);
}

@Test
void getLeafCertificateAsHeaderValue_multiCertPEM_keepsOnlyFirstCert() {
String pem = "-----BEGIN CERTIFICATE-----\n"
+ chunk(x509_base64, 64)
+ "\n-----END CERTIFICATE-----\n"
+ "-----BEGIN CERTIFICATE-----\nMIIGARBITRARYSECONDCERTDATA==\n-----END CERTIFICATE-----\n";
X509Certificate cert = X509Certificate.newCertificate(pem);

assertThat(cert).isNotNull();
String headerValue = cert.getLeafCertificateAsHeaderValue();
assertThat(headerValue).isEqualTo(x509_base64);
assertThat(headerValue).doesNotContain("MIIGARBITRARYSECONDCERTDATA");
assertThat(headerValue).doesNotContain("-----BEGIN");
assertThat(headerValue).doesNotContain("-----END");
assertThat(headerValue).doesNotContain("\n");
}

private static String chunk(String s, int width) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i += width) {
if (i > 0) sb.append('\n');
sb.append(s, i, Math.min(i + width, s.length()));
}
return sb.toString();
}

@Test
void getSubjectDN() {
assertThat(cut.getSubjectDN()).isEqualTo(
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<groupId>com.sap.cloud.security.xsuaa</groupId>
<artifactId>parent</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
<packaging>pom</packaging>

<name>parent</name>
Expand Down
2 changes: 1 addition & 1 deletion spring-security-3-starter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<parent>
<groupId>com.sap.cloud.security.xsuaa</groupId>
<artifactId>parent</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
</parent>

<groupId>com.sap.cloud.security</groupId>
Expand Down
2 changes: 1 addition & 1 deletion spring-security-3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Use the Spring Boot 3.x starter instead:
<dependency>
<groupId>com.sap.cloud.security</groupId>
<artifactId>resourceserver-security-spring-boot-3-starter</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
</dependency>
```

Expand Down
4 changes: 2 additions & 2 deletions spring-security-3/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
<parent>
<groupId>com.sap.cloud.security.xsuaa</groupId>
<artifactId>parent</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
</parent>

<groupId>com.sap.cloud.security</groupId>
<artifactId>spring-security-3</artifactId>
<name>spring-security-3</name>
<packaging>jar</packaging>
<version>4.0.7</version>
<version>4.0.8</version>

<url>https://github.com/SAP/cloud-security-xsuaa-integration</url>
<description>Java Spring security library for Spring Boot 3</description>
Expand Down
2 changes: 1 addition & 1 deletion spring-security-starter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<parent>
<groupId>com.sap.cloud.security.xsuaa</groupId>
<artifactId>parent</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
</parent>

<groupId>com.sap.cloud.security</groupId>
Expand Down
2 changes: 1 addition & 1 deletion spring-security/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ These (spring) dependencies need to be provided:
<dependency>
<groupId>com.sap.cloud.security</groupId>
<artifactId>resourceserver-security-spring-boot-starter</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
</dependency>
```

Expand Down
4 changes: 2 additions & 2 deletions spring-security/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
<parent>
<groupId>com.sap.cloud.security.xsuaa</groupId>
<artifactId>parent</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
</parent>

<groupId>com.sap.cloud.security</groupId>
<artifactId>spring-security</artifactId>
<name>spring-security</name>
<packaging>jar</packaging>
<version>4.0.7</version>
<version>4.0.8</version>

<url>https://github.com/SAP/cloud-security-xsuaa-integration</url>
<description>Java Spring security library</description>
Expand Down
2 changes: 1 addition & 1 deletion token-client-spring-3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ This module is the Spring Boot 3.x compatible version of [token-client-spring](.
<dependency>
<groupId>com.sap.cloud.security.xsuaa</groupId>
<artifactId>token-client-spring-3</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
</dependency>
```

Expand Down
2 changes: 1 addition & 1 deletion token-client-spring-3/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<parent>
<groupId>com.sap.cloud.security.xsuaa</groupId>
<artifactId>parent</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
</parent>

<artifactId>token-client-spring-3</artifactId>
Expand Down
4 changes: 2 additions & 2 deletions token-client-spring/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Starting with version 4.0.0, Spring-specific implementations have been moved to
<dependency>
<groupId>com.sap.cloud.security.xsuaa</groupId>
<artifactId>token-client-spring</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
</dependency>
```

Expand Down Expand Up @@ -65,7 +65,7 @@ If you were using `XsuaaOAuth2TokenService`, `SpringOAuth2TokenKeyService`, or `
<dependency>
<groupId>com.sap.cloud.security.xsuaa</groupId>
<artifactId>token-client-spring</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
</dependency>
```

Expand Down
2 changes: 1 addition & 1 deletion token-client-spring/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<parent>
<groupId>com.sap.cloud.security.xsuaa</groupId>
<artifactId>parent</artifactId>
<version>4.0.7</version>
<version>4.0.8</version>
</parent>

<artifactId>token-client-spring</artifactId>
Expand Down
Loading