Skip to content

Replace Spring Security Base64 with java.util equivalent#3857

Open
gdgenchev wants to merge 1 commit intocloudfoundry:developfrom
gdgenchev:migrate-base64-utf8
Open

Replace Spring Security Base64 with java.util equivalent#3857
gdgenchev wants to merge 1 commit intocloudfoundry:developfrom
gdgenchev:migrate-base64-utf8

Conversation

@gdgenchev
Copy link
Copy Markdown
Contributor

org.springframework.security.crypto.codec.Base64 was deprecated in Spring Security 6 and later removed in Spring Security 7. Replaced with java.util.Base64.

Spring Security 7 removed the Base64 class from the
spring-security-crypto.codec package. Replaced with java.util.Base64
in all files that used org.springframework.security.crypto.codec.Base64.

Related to Spring Boot 4 migration.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Replaces deprecated Spring Security org.springframework.security.crypto.codec.Base64 usage with java.util.Base64 to maintain compatibility with Spring Security 6+ and prepare for Spring Security 7.

Changes:

  • Swapped Spring Security Base64 imports for java.util.Base64 across several unit/integration tests.
  • Updated Basic Auth header construction to use Base64.getEncoder().encode(...).
  • Updated UserNotFoundEvent to use java.util.Base64 for hashing/encoding the username in audit events.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenMvcMockZonePathTests.java Replace Spring Base64 with JDK Base64 in multiple Basic Auth header constructions.
uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenMvcMockTests.java Same Base64 replacement for token endpoint tests.
uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/limited/LimitedModeTokenMockMvcTests.java Same Base64 replacement in limited-mode check_token test.
uaa/src/test/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpointDocs.java Same Base64 replacement in REST docs test for autologin.
uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/ScimGroupEndpointsIntegrationTests.java Same Base64 replacement in client-credentials token helper.
uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/NativeApplicationIntegrationTests.java Same Base64 replacement in Basic auth header for negative test.
uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/CheckTokenEndpointIntegrationTests.java Same Base64 replacement in forbidden check_token test.
server/src/test/java/org/cloudfoundry/identity/uaa/test/UaaTestAccounts.java Same Base64 replacement for shared test helper building Basic auth headers.
server/src/main/java/org/cloudfoundry/identity/uaa/authentication/event/UserNotFoundEvent.java Replace Spring Base64 with JDK Base64 in audit-event name hashing/encoding.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

ScimUser developer = setUpUser(jdbcScimUserProvisioning, jdbcScimGroupMembershipManager, jdbcScimGroupProvisioning, userId, userScopes, OriginKeys.LOGIN_SERVER, IdentityZoneHolder.get().getId());
String loginToken = testClient.getClientCredentialsOAuthAccessToken("login", "loginsecret", "");
String basicAuthForLoginClient = new String(Base64.encode("%s:%s".formatted("login", "loginsecret").getBytes()));
String basicAuthForLoginClient = new String(Base64.getEncoder().encode("%s:%s".formatted("login", "loginsecret").getBytes()));
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new String(Base64.getEncoder().encode(...)) relies on the platform default charset when converting the encoded bytes to a String. Use Base64.getEncoder().encodeToString(...) instead (and prefer getBytes(StandardCharsets.UTF_8) for the credential bytes) to make the Authorization header deterministic across platforms. This pattern appears multiple times in this file.

Suggested change
String basicAuthForLoginClient = new String(Base64.getEncoder().encode("%s:%s".formatted("login", "loginsecret").getBytes()));
String basicAuthForLoginClient = Base64.getEncoder().encodeToString("%s:%s".formatted("login", "loginsecret").getBytes(StandardCharsets.UTF_8));

Copilot uses AI. Check for mistakes.
Comment on lines 75 to +76
.header(AUTHORIZATION,
"Basic " + new String(Base64.encode((client.getClientId() + ":" + SECRET).getBytes())))
"Basic " + new String(Base64.getEncoder().encode((client.getClientId() + ":" + SECRET).getBytes())))
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Basic auth, avoid new String(Base64.getEncoder().encode(...)) because it depends on the default charset for the byte[]->String conversion. Prefer Base64.getEncoder().encodeToString((clientId + ":" + secret).getBytes(StandardCharsets.UTF_8)) (or US_ASCII for the encoded output) so the Authorization header is stable across environments.

Copilot uses AI. Check for mistakes.
String body = mockMvc.perform(
post("/autologin")
.header("Authorization", "Basic " + new String(Base64.encode("admin:adminsecret".getBytes())))
.header("Authorization", "Basic " + new String(Base64.getEncoder().encode("admin:adminsecret".getBytes())))
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Basic " + new String(Base64.getEncoder().encode(...)) is charset-dependent due to new String(byte[]). Prefer Base64.getEncoder().encodeToString("admin:adminsecret".getBytes(StandardCharsets.UTF_8)) (or specify US_ASCII for the encoded output) to avoid platform-specific behavior.

Copilot uses AI. Check for mistakes.
Comment on lines 565 to +566
headers.set("Authorization",
"Basic " + new String(Base64.encode("%s:%s".formatted(clientId, clientSecret).getBytes())));
"Basic " + new String(Base64.getEncoder().encode("%s:%s".formatted(clientId, clientSecret).getBytes())));
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Building the Basic auth header via new String(Base64.getEncoder().encode(...)) makes the result depend on the platform default charset. Use Base64.getEncoder().encodeToString(... ) and pass an explicit charset to getBytes(...) (e.g., StandardCharsets.UTF_8) so the header is deterministic.

Copilot uses AI. Check for mistakes.
formData.add("scope", "cloud_controller.read");
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Basic " + new String(Base64.encode("no-such-client:".getBytes(StandardCharsets.UTF_8))));
headers.set("Authorization", "Basic " + new String(Base64.getEncoder().encode("no-such-client:".getBytes(StandardCharsets.UTF_8))));
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though the input uses StandardCharsets.UTF_8, wrapping Base64.getEncoder().encode(...) in new String(...) still uses the platform default charset for decoding the Base64 bytes. Prefer Base64.getEncoder().encodeToString(...) to avoid charset-dependent behavior.

Suggested change
headers.set("Authorization", "Basic " + new String(Base64.getEncoder().encode("no-such-client:".getBytes(StandardCharsets.UTF_8))));
headers.set("Authorization", "Basic " + Base64.getEncoder().encodeToString("no-such-client:".getBytes(StandardCharsets.UTF_8)));

Copilot uses AI. Check for mistakes.
formData.add("token", "FOO");
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Basic " + new String(Base64.encode("cf:".getBytes(UTF_8))));
headers.set("Authorization", "Basic " + new String(Base64.getEncoder().encode("cf:".getBytes(UTF_8))));
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new String(Base64.getEncoder().encode(...)) uses the platform default charset to decode the encoded bytes, which is not guaranteed to be ASCII-compatible on all JVMs. Prefer Base64.getEncoder().encodeToString("cf:".getBytes(UTF_8)) (or specify US_ASCII for the output) for a deterministic Basic auth header.

Suggested change
headers.set("Authorization", "Basic " + new String(Base64.getEncoder().encode("cf:".getBytes(UTF_8))));
headers.set("Authorization", "Basic " + Base64.getEncoder().encodeToString("cf:".getBytes(UTF_8)));

Copilot uses AI. Check for mistakes.
String credentials =
"%s:%s".formatted(URLEncoder.encode(username, StandardCharsets.UTF_8), URLEncoder.encode(password, StandardCharsets.UTF_8));
return "Basic %s".formatted(new String(Base64.encode(credentials.getBytes())));
return "Basic %s".formatted(new String(Base64.getEncoder().encode(credentials.getBytes())));
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new String(Base64.getEncoder().encode(credentials.getBytes())) is charset-dependent twice: getBytes() uses the platform default charset, and new String(...) decodes Base64 bytes using the platform default charset. Prefer Base64.getEncoder().encodeToString(credentials.getBytes(StandardCharsets.UTF_8)) to make the Basic auth header deterministic across platforms.

Suggested change
return "Basic %s".formatted(new String(Base64.getEncoder().encode(credentials.getBytes())));
return "Basic %s".formatted(Base64.getEncoder().encodeToString(credentials.getBytes(StandardCharsets.UTF_8)));

Copilot uses AI. Check for mistakes.
// Store hash of name, to conceal accidental entry of sensitive info
// (e.g. password)
name = Utf8.decode(Base64.encode(MessageDigest.getInstance("SHA-1").digest(Utf8.encode(name))));
name = Utf8.decode(Base64.getEncoder().encode(MessageDigest.getInstance("SHA-1").digest(Utf8.encode(name))));
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this change already moves off Spring Security's Base64, consider also removing the remaining org.springframework.security.crypto.codec.Utf8 usage here: Base64.getEncoder().encodeToString(MessageDigest...digest(name.getBytes(StandardCharsets.UTF_8))) produces the same value and avoids depending on Spring's deprecated codec utilities (which may also be removed in Spring Security 7).

Copilot uses AI. Check for mistakes.
ScimUser developer = setUpUser(jdbcScimUserProvisioning, jdbcScimGroupMembershipManager, jdbcScimGroupProvisioning, userId, userScopes, OriginKeys.LOGIN_SERVER, IdentityZoneHolder.get().getId());
String loginToken = testClient.getClientCredentialsOAuthAccessToken("login", "loginsecret", "");
String basicAuthForLoginClient = new String(Base64.encode("%s:%s".formatted("login", "loginsecret").getBytes()));
String basicAuthForLoginClient = new String(Base64.getEncoder().encode("%s:%s".formatted("login", "loginsecret").getBytes()));
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new String(Base64.getEncoder().encode(...)) is charset-dependent and can produce an invalid Base64 string on non-ASCII default charsets. Prefer Base64.getEncoder().encodeToString(...) and specify the input bytes charset (e.g., getBytes(StandardCharsets.UTF_8)) for Basic auth credentials. This pattern appears multiple times in this file.

Suggested change
String basicAuthForLoginClient = new String(Base64.getEncoder().encode("%s:%s".formatted("login", "loginsecret").getBytes()));
String basicAuthForLoginClient = Base64.getEncoder().encodeToString("%s:%s".formatted("login", "loginsecret").getBytes(StandardCharsets.UTF_8));

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

2 participants