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 {

Reply via email to