This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 8e479d7a2823 Encode HTTP Basic credentials as UTF-8, not in the
platform default charset (#29694)
8e479d7a2823 is described below
commit 8e479d7a2823024c46c237ee368ac1d7c03bbbcd
Author: dhairya05 <[email protected]>
AuthorDate: Thu Nov 12 19:23:03 2026 +0530
Encode HTTP Basic credentials as UTF-8, not in the platform default charset
(#29694)
* Encode HTTP Basic credentials as UTF-8, not in the platform default
charset
Five places build an Authorization/Proxy-Authorization header by
Base64-encoding "user:password" through String.getBytes() with no argument,
which uses the JVM's default charset:
camel-as2 AS2HeaderUtils.addAuthorizationHeader
camel-keycloak KeycloakProducer (token revocation)
camel-keycloak KeycloakTokenIntrospector
camel-smpp SmppConnectionFactory.connectProxy
camel-wordpress WordpressBasicAuthentication
RFC 7617 says the credentials are encoded with UTF-8 unless the server asks
for another charset, and that is what Keycloak, WordPress and ordinary HTTP
proxies expect. The default charset is only UTF-8 when nothing overrides it:
-Dfile.encoding, file.encoding=COMPAT, or a JVM before 18 following the
platform locale, all give something else. A user name or password holding a
non-ASCII character then produces a different Authorization header on
different machines, and the server rejects it with 401 even though the
credentials are right. Since the bytes are Base64-encoded, nothing in the
error points at the encoding.
All five now pass StandardCharsets.UTF_8 explicitly. Credentials made only
of
ASCII characters encode to exactly the same bytes as before.
camel-smpp's connectProxy also wrote the CONNECT request line, the proxy
headers and the line separators, and read the proxy's response, in the
default charset; those are converted too, so the whole exchange is built
one way.
* camel-as2: assert the Basic header against a fixed value
Recomputing the expectation the way the code does makes the assertion
vacuous
on a JVM whose default charset is already UTF-8, which is every JVM Camel CI
runs. The expected header is now written out, and the ISO-8859-1 encoding of
the same credentials is asserted to differ, so the test says what has to go
on
the wire rather than agreeing with whatever the code did.
---
.../component/as2/api/util/AS2HeaderUtils.java | 4 +++-
.../component/as2/api/util/AS2HeaderUtilsTest.java | 23 ++++++++++++++++++++++
.../camel/component/keycloak/KeycloakProducer.java | 3 ++-
.../security/KeycloakTokenIntrospector.java | 3 ++-
.../component/smpp/SmppConnectionFactory.java | 17 ++++++++--------
.../api/auth/WordpressBasicAuthentication.java | 7 +++++--
6 files changed, 44 insertions(+), 13 deletions(-)
diff --git
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/AS2HeaderUtils.java
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/AS2HeaderUtils.java
index d65009ccded2..31678a99e029 100644
---
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/AS2HeaderUtils.java
+++
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/AS2HeaderUtils.java
@@ -16,6 +16,7 @@
*/
package org.apache.camel.component.as2.api.util;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
@@ -167,7 +168,8 @@ public final class AS2HeaderUtils {
public static void addAuthorizationHeader(HttpMessage message, String
userName, String password, String accessToken) {
if (userName != null && password != null) {
message.addHeader(AS2Header.AUTHORIZATION,
- ("Basic " + Base64.getEncoder().encodeToString((userName +
":" + password).getBytes())));
+ ("Basic " + Base64.getEncoder()
+ .encodeToString((userName + ":" +
password).getBytes(StandardCharsets.UTF_8))));
} else if (accessToken != null) {
message.addHeader(AS2Header.AUTHORIZATION, "Bearer " +
accessToken);
}
diff --git
a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/util/AS2HeaderUtilsTest.java
b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/util/AS2HeaderUtilsTest.java
index aa26051a0699..507c02214d13 100644
---
a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/util/AS2HeaderUtilsTest.java
+++
b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/util/AS2HeaderUtilsTest.java
@@ -16,13 +16,20 @@
*/
package org.apache.camel.component.as2.api.util;
+import java.util.Base64;
+
+import org.apache.camel.component.as2.api.AS2Header;
import org.apache.camel.component.as2.api.util.AS2HeaderUtils.Parameter;
+import org.apache.hc.core5.http.HttpMessage;
+import org.apache.hc.core5.http.message.BasicHttpRequest;
import org.apache.hc.core5.http.message.ParserCursor;
import org.apache.hc.core5.util.CharArrayBuffer;
import org.junit.jupiter.api.Test;
+import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
public class AS2HeaderUtilsTest {
@@ -55,4 +62,20 @@ public class AS2HeaderUtilsTest {
assertArrayEquals(SIGNED_RECEIPT_MICALG_VALUES, parameter.getValues(),
"Unexpected value for parameter values");
}
+ @Test
+ public void addAuthorizationHeaderEncodesCredentialsAsUtf8() {
+ // a password outside US-ASCII: "pa(diaeresis)ssword"
+ final String password = "p\u00e4ssword";
+ final HttpMessage message = new BasicHttpRequest("POST", "/");
+
+ AS2HeaderUtils.addAuthorizationHeader(message, "user", password, null);
+
+ // the expected value is written out rather than recomputed the way
the code does it, so the assertion says
+ // what has to go on the wire whatever the JVM's default charset is:
RFC 7617 asks for UTF-8
+ assertEquals("Basic dXNlcjpww6Rzc3dvcmQ=",
message.getFirstHeader(AS2Header.AUTHORIZATION).getValue());
+
+ // what the default charset would have produced instead, had it not
been UTF-8
+ assertNotEquals("Basic " + Base64.getEncoder().encodeToString(("user:"
+ password).getBytes(ISO_8859_1)),
+ message.getFirstHeader(AS2Header.AUTHORIZATION).getValue());
+ }
}
diff --git
a/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakProducer.java
b/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakProducer.java
index a54cb4e17b97..b9c41747fbc7 100644
---
a/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakProducer.java
+++
b/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakProducer.java
@@ -18,6 +18,7 @@ package org.apache.camel.component.keycloak;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
@@ -1414,7 +1415,7 @@ public class KeycloakProducer extends DefaultProducer {
// Add Basic authentication header
String auth = clientId + ":" + clientSecret;
- String encodedAuth =
Base64.getEncoder().encodeToString(auth.getBytes());
+ String encodedAuth =
Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
request.setHeader("Authorization", "Basic " + encodedAuth);
request.setHeader("Content-Type",
"application/x-www-form-urlencoded");
diff --git
a/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/security/KeycloakTokenIntrospector.java
b/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/security/KeycloakTokenIntrospector.java
index f95617613d8d..46a1900dd15e 100644
---
a/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/security/KeycloakTokenIntrospector.java
+++
b/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/security/KeycloakTokenIntrospector.java
@@ -17,6 +17,7 @@
package org.apache.camel.component.keycloak.security;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.Map;
@@ -142,7 +143,7 @@ public class KeycloakTokenIntrospector {
// Add Basic authentication header
String auth = clientId + ":" + clientSecret;
- String encodedAuth =
Base64.getEncoder().encodeToString(auth.getBytes());
+ String encodedAuth =
Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
request.setHeader("Authorization", "Basic " + encodedAuth);
request.setHeader("Content-Type", "application/x-www-form-urlencoded");
diff --git
a/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppConnectionFactory.java
b/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppConnectionFactory.java
index 0bbbbbb7e796..9b7db82dccae 100644
---
a/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppConnectionFactory.java
+++
b/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppConnectionFactory.java
@@ -53,6 +53,7 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
+import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Map;
@@ -171,33 +172,33 @@ public final class SmppConnectionFactory implements
ConnectionFactory {
InputStream in = socket.getInputStream();
String connectString = "CONNECT " + host + ":" + port + "
HTTP/1.0\r\n";
- out.write(connectString.getBytes());
+ out.write(connectString.getBytes(StandardCharsets.UTF_8));
String username = config.getHttpProxyUsername();
String password = config.getHttpProxyPassword();
if (username != null && password != null) {
String usernamePassword = username + ":" + password;
- byte[] code =
Base64.getEncoder().encode(usernamePassword.getBytes());
- out.write("Proxy-Authorization: Basic ".getBytes());
+ byte[] code =
Base64.getEncoder().encode(usernamePassword.getBytes(StandardCharsets.UTF_8));
+ out.write("Proxy-Authorization: Basic
".getBytes(StandardCharsets.UTF_8));
out.write(code);
- out.write("\r\n".getBytes());
+ out.write("\r\n".getBytes(StandardCharsets.UTF_8));
}
Map<String, String> proxyHeaders = config.getProxyHeaders();
if (proxyHeaders != null) {
for (Map.Entry<String, String> entry :
proxyHeaders.entrySet()) {
- out.write((entry.getKey() + ": " +
entry.getValue()).getBytes());
- out.write("\r\n".getBytes());
+ out.write((entry.getKey() + ": " +
entry.getValue()).getBytes(StandardCharsets.UTF_8));
+ out.write("\r\n".getBytes(StandardCharsets.UTF_8));
}
}
- out.write("\r\n".getBytes());
+ out.write("\r\n".getBytes(StandardCharsets.UTF_8));
out.flush();
int ch = 0;
- BufferedReader reader = IOHelper.buffered(new
InputStreamReader(in));
+ BufferedReader reader = IOHelper.buffered(new
InputStreamReader(in, StandardCharsets.UTF_8));
String response = reader.readLine();
if (response == null) {
throw new RuntimeCamelException("Empty response to CONNECT
request to host " + host + ":" + port);
diff --git
a/components/camel-wordpress/src/main/java/org/apache/camel/component/wordpress/api/auth/WordpressBasicAuthentication.java
b/components/camel-wordpress/src/main/java/org/apache/camel/component/wordpress/api/auth/WordpressBasicAuthentication.java
index 80ee47917832..275d5da52d5b 100644
---
a/components/camel-wordpress/src/main/java/org/apache/camel/component/wordpress/api/auth/WordpressBasicAuthentication.java
+++
b/components/camel-wordpress/src/main/java/org/apache/camel/component/wordpress/api/auth/WordpressBasicAuthentication.java
@@ -16,6 +16,8 @@
*/
package org.apache.camel.component.wordpress.api.auth;
+import java.nio.charset.StandardCharsets;
+
import org.apache.cxf.common.util.Base64Utility;
import org.apache.cxf.jaxrs.client.ClientConfiguration;
import org.apache.cxf.jaxrs.client.WebClient;
@@ -51,8 +53,9 @@ public class WordpressBasicAuthentication extends
BaseWordpressAuthentication {
@Override
public void configureAuthentication(Object api) {
if (isCredentialsSet()) {
- final String authorizationHeader = String.format("Basic %s",
- Base64Utility.encode(String.format("%s:%s", this.username,
this.password).getBytes()));
+ final String credentials = String.format("%s:%s", this.username,
this.password);
+ final String authorizationHeader
+ = String.format("Basic %s",
Base64Utility.encode(credentials.getBytes(StandardCharsets.UTF_8)));
LOGGER.info("Credentials set for user {}", username);
WebClient.client(api).header("Authorization", authorizationHeader);
} else {