Skip to content

fix(gdch): support EC private keys#1896

Open
diegomarquezp wants to merge 58 commits intomainfrom
b/488439640
Open

fix(gdch): support EC private keys#1896
diegomarquezp wants to merge 58 commits intomainfrom
b/488439640

Conversation

@diegomarquezp
Copy link
Contributor

@diegomarquezp diegomarquezp commented Mar 4, 2026

Context: b/488439640

Implementation originally proposed in b/431924643#comment9

The primary objective is to enable support for Elliptic Curve (EC) keys and non-URI audience formats, aligning the Java SDK with the behavior of the Python and Go implementations. Additionally, the GDCH key creation tool creates EC keys only, meaning the GDCH implementation was not following the convention.

Key Changes

  • Algorithm Support: Updated the credential signing process to use the ES256 (ECDSA) algorithm, which is the required standard for GDCH service accounts.
  • Audience Flexibility: Changed the apiAudience field from a URI to a String to accommodate "magic" non-URI strings (e.g., specific administrative audiences) required by certain GDCH services.
  • OAuth2Utils Refactoring: Enhanced privateKeyFromPkcs8 to accept an algorithm parameter, allowing the library to parse EC keys instead of defaulting exclusively to RSA.
  • Test fixes: Fixes in a couple of test files as per Sonarqube analysis.

Testing

  • Updated GdchCredentialsTest to include test cases for EC key parsing and token signing.

@product-auto-label product-auto-label bot added the size: l Pull request size is large. label Mar 4, 2026
@diegomarquezp diegomarquezp marked this pull request as ready for review March 9, 2026 21:36
@diegomarquezp diegomarquezp requested review from a team as code owners March 9, 2026 21:36
@diegomarquezp diegomarquezp requested a review from a team as a code owner March 10, 2026 21:22
@diegomarquezp diegomarquezp changed the title feat(gdch): support EC private keys fix(gdch): support EC private keys Mar 10, 2026
@product-auto-label product-auto-label bot added size: xl Pull request size is extra large. and removed size: l Pull request size is large. labels Mar 24, 2026
@product-auto-label product-auto-label bot added size: l Pull request size is large. and removed size: xl Pull request size is extra large. labels Mar 24, 2026
LoggerProvider.forClazz(GdchCredentials.class);
static final String SUPPORTED_FORMAT_VERSION = "1";
private static final String PARSE_ERROR_PREFIX = "Error parsing token refresh response. ";
@VisibleForTesting static final String SUPPORTED_FORMAT_VERSION = "1";
Copy link
Member

Choose a reason for hiding this comment

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

nit: Do you have any documentation regarding this format version? What is version 1 refrencing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's the format version of the JSON file. I believe this is to track changes such as a new entry in the json file.

I found go/gdch-python-auth-lib as a good source of truth.

Added a code comment to explain that.

@marcosgtz7
Copy link

marcosgtz7 commented Mar 24, 2026 via email

@marcosgtz7
Copy link

marcosgtz7 commented Mar 24, 2026 via email

@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud


@CanIgnoreReturnValue
public Builder setGdchAudience(URI apiAudience) {
public Builder setGdchAudience(String apiAudience) {
Copy link
Member

Choose a reason for hiding this comment

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

for this public method, add small javadocs and note that it cannot be null or empty.

private static final String SA_PRIVATE_KEY_PKCS8 =
ServiceAccountCredentialsTest.PRIVATE_KEY_PKCS8;
private static final String GDCH_SA_FORMAT_VERSION = GdchCredentials.SUPPORTED_FORMAT_VERSION;
private static final String GDCH_SA_FORMAT_VERSION =
Copy link
Member

Choose a reason for hiding this comment

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

can the tests just use GdchCredentials.SUPPORTED_JSON_FORMAT_VERSION constant directly?

Comment on lines +1094 to +1114
// SEQUENCE length doesn't match actual length
byte[] invalidDer = new byte[] {0x30, 0x05, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02};
GoogleAuthException e =
assertThrows(
GoogleAuthException.class, () -> GdchCredentials.transcodeDerToConcat(invalidDer, 64));
assertEquals("Invalid DER signature length.", e.getMessage());
}

@Test
void transcodeDerToConcat_invalidRInteger() {
// Missing INTEGER for R
byte[] invalidDer = new byte[] {0x30, 0x06, 0x03, 0x01, 0x01, 0x02, 0x01, 0x02};
GoogleAuthException e =
assertThrows(
GoogleAuthException.class, () -> GdchCredentials.transcodeDerToConcat(invalidDer, 64));
assertEquals("Expected INTEGER for R.", e.getMessage());
}

@Test
void transcodeDerToConcat_invalidSInteger() {
// Missing INTEGER for S
Copy link
Member

Choose a reason for hiding this comment

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

For these test cases, can the comment show where it's missing the INTEGER value?

// Missing INTEGER for R

Is it missing something from byte[] invalidDer = new byte[] {0x30, 0x06, 0x03, 0x01, 0x01, 0x02, 0x01, 0x02};?

private static final String SA_PRIVATE_KEY_PKCS8 =
ServiceAccountCredentialsTest.PRIVATE_KEY_PKCS8;
private static final String GDCH_SA_FORMAT_VERSION = GdchCredentials.SUPPORTED_FORMAT_VERSION;
private static final String GDCH_SA_FORMAT_VERSION =
Copy link
Member

Choose a reason for hiding this comment

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

same here, is it possible to just use GdchCredentials.SUPPORTED_JSON_FORMAT_VERSION directly in the test?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: l Pull request size is large.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants