Skip to content

Commit c9a67cb

Browse files
committed
chore: Enhance Javadoc, comments, and test robustness for certificate source.
1 parent 1559054 commit c9a67cb

File tree

6 files changed

+69
-16
lines changed

6 files changed

+69
-16
lines changed

oauth2_http/java/com/google/auth/mtls/MtlsHttpTransportFactory.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
* An HttpTransportFactory that creates {@link NetHttpTransport} instances configured for mTLS
4242
* (mutual TLS) using a specific {@link KeyStore} containing the client's certificate and private
4343
* key.
44+
*
45+
* <p><b>Warning:</b> This class is considered internal and is not intended for direct use by
46+
* library consumers. Its API and behavior may change without notice.
4447
*/
4548
public class MtlsHttpTransportFactory implements HttpTransportFactory {
4649
private final KeyStore mtlsKeyStore;

oauth2_http/java/com/google/auth/mtls/X509Provider.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,14 @@ public KeyStore getKeyStore() throws IOException {
122122
if (loadedConfig == null) {
123123
loadedConfig = getWorkloadCertificateConfiguration();
124124
}
125-
WorkloadCertificateConfiguration workloadCertConfig = loadedConfig;
126125

127126
InputStream certStream = null;
128127
InputStream privateKeyStream = null;
129128
SequenceInputStream certAndPrivateKeyStream = null;
130129
try {
131130
// Read the certificate and private key file paths into separate streams.
132-
File certFile = new File(workloadCertConfig.getCertPath());
133-
File privateKeyFile = new File(workloadCertConfig.getPrivateKeyPath());
131+
File certFile = new File(loadedConfig.getCertPath());
132+
File privateKeyFile = new File(loadedConfig.getPrivateKeyPath());
134133
certStream = createInputStream(certFile);
135134
privateKeyStream = createInputStream(privateKeyFile);
136135

oauth2_http/java/com/google/auth/oauth2/CertificateIdentityPoolSubjectTokenSupplier.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ private static X509Certificate loadLeafCertificate(String path)
7979
@VisibleForTesting
8080
static X509Certificate parseCertificate(byte[] certData) throws CertificateException {
8181
if (certData == null || certData.length == 0) {
82-
throw new IllegalArgumentException("Invalid certificate data: empty or null input");
82+
throw new IllegalArgumentException(
83+
"Invalid certificate data: Certificate file is empty or null.");
8384
}
8485

8586
try {
@@ -98,6 +99,17 @@ private static String encodeCert(X509Certificate certificate)
9899
return Base64.getEncoder().encodeToString(certificate.getEncoded());
99100
}
100101

102+
/**
103+
* Retrieves the X509 subject token. This method loads the leaf certificate specified by the
104+
* {@code credentialSource.credentialLocation}. The subject token is constructed as a JSON array
105+
* containing the base64-encoded (DER format) leaf certificate. This JSON array serves as the
106+
* subject token for mTLS authentication.
107+
*
108+
* @param context The external account supplier context. This parameter is currently not used in
109+
* this implementation.
110+
* @return The JSON string representation of the base64-encoded leaf certificate in a JSON array.
111+
* @throws IOException If an I/O error occurs while reading the certificate file.
112+
*/
101113
@Override
102114
public String getSubjectToken(ExternalAccountSupplierContext context) throws IOException {
103115
try {

oauth2_http/javatests/com/google/auth/oauth2/CertificateIdentityPoolSubjectTokenSupplierTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ public void parseCertificate_emptyData_throwsIllegalArgumentException() {
108108
assertThrows(
109109
IllegalArgumentException.class,
110110
() -> CertificateIdentityPoolSubjectTokenSupplier.parseCertificate(new byte[0]));
111-
assertEquals("Invalid certificate data: empty or null input", exception.getMessage());
111+
assertEquals(
112+
"Invalid certificate data: Certificate file is empty or null.", exception.getMessage());
112113
}
113114

114115
@Test
@@ -117,7 +118,8 @@ public void parseCertificate_nullData_throwsIllegalArgumentException() {
117118
assertThrows(
118119
IllegalArgumentException.class,
119120
() -> CertificateIdentityPoolSubjectTokenSupplier.parseCertificate(null));
120-
assertEquals("Invalid certificate data: empty or null input", exception.getMessage());
121+
assertEquals(
122+
"Invalid certificate data: Certificate file is empty or null.", exception.getMessage());
121123
}
122124

123125
@Test

oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,7 +1112,8 @@ public void build_withDefaultCertificate_throwsOnTransportInitFailure() {
11121112
@Test
11131113
public void build_withCustomProvider_throwsOnGetKeyStore()
11141114
throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {
1115-
// Setup a custom provider configured to throw during getKeyStore().
1115+
// Simulate a scenario where the X509Provider fails to load the KeyStore, typically due to an
1116+
// IOException when reading the certificate or private key files.
11161117
KeyStore keyStore = KeyStore.getInstance("JKS");
11171118
keyStore.load(null, null);
11181119
TestX509Provider x509Provider = new TestX509Provider(keyStore, "/path/to/certificate.json");
@@ -1121,19 +1122,28 @@ public void build_withCustomProvider_throwsOnGetKeyStore()
11211122
Map<String, Object> certificateMap = new HashMap<>();
11221123
certificateMap.put("certificate_config_location", "/path/to/certificate.json");
11231124

1124-
// Expect RuntimeException from the custom provider during build.
1125+
// Expect RuntimeException because the constructor wraps the IOException.
11251126
RuntimeException exception =
11261127
assertThrows(
11271128
RuntimeException.class,
11281129
() -> createCredentialsWithCertificate(x509Provider, certificateMap));
11291130

1130-
assertEquals("Exception on get keystore", exception.getMessage());
1131+
// Verify the cause is the expected IOException from the mock.
1132+
assertNotNull(exception.getCause());
1133+
assertTrue(exception.getCause() instanceof IOException);
1134+
assertEquals("Simulated IOException on get keystore", exception.getCause().getMessage());
1135+
1136+
// Verify the wrapper exception message
1137+
assertEquals(
1138+
"Failed to initialize IdentityPoolCredentials from certificate source due to an I/O error.",
1139+
exception.getMessage());
11311140
}
11321141

11331142
@Test
11341143
public void build_withCustomProvider_throwsOnGetCertificatePath()
11351144
throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {
1136-
// Setup a custom provider configured to throw during getCertificatePath().
1145+
// Simulate a scenario where the X509Provider cannot access or read the certificate
1146+
// configuration file needed to determine the certificate path, resulting in an IOException.
11371147
KeyStore keyStore = KeyStore.getInstance("JKS");
11381148
keyStore.load(null, null);
11391149
TestX509Provider x509Provider = new TestX509Provider(keyStore, "/path/to/certificate.json");
@@ -1142,13 +1152,21 @@ public void build_withCustomProvider_throwsOnGetCertificatePath()
11421152
Map<String, Object> certificateMap = new HashMap<>();
11431153
certificateMap.put("certificate_config_location", "/path/to/certificate.json");
11441154

1145-
// Expect RuntimeException from the custom provider during build.
1155+
// Expect RuntimeException because the constructor wraps the IOException.
11461156
RuntimeException exception =
11471157
assertThrows(
11481158
RuntimeException.class,
11491159
() -> createCredentialsWithCertificate(x509Provider, certificateMap));
11501160

1151-
assertEquals("Exception on get certificate path", exception.getMessage());
1161+
// Verify the cause is the expected IOException from the mock.
1162+
assertNotNull(exception.getCause());
1163+
assertTrue(exception.getCause() instanceof IOException);
1164+
assertEquals("Simulated IOException on certificate path", exception.getCause().getMessage());
1165+
1166+
// Verify the wrapper exception message
1167+
assertEquals(
1168+
"Failed to initialize IdentityPoolCredentials from certificate source due to an I/O error.",
1169+
exception.getMessage());
11521170
}
11531171

11541172
private void createCredentialsWithCertificate(
@@ -1261,17 +1279,17 @@ private static class TestX509Provider extends X509Provider {
12611279
}
12621280

12631281
@Override
1264-
public KeyStore getKeyStore() {
1282+
public KeyStore getKeyStore() throws IOException {
12651283
if (shouldThrowOnGetKeyStore) {
1266-
throw new RuntimeException("Exception on get keystore");
1284+
throw new IOException("Simulated IOException on get keystore");
12671285
}
12681286
return keyStore;
12691287
}
12701288

12711289
@Override
1272-
public String getCertificatePath() {
1290+
public String getCertificatePath() throws IOException {
12731291
if (shouldThrowOnGetCertificatePath) {
1274-
throw new RuntimeException("Exception on get certificate path");
1292+
throw new IOException("Simulated IOException on certificate path");
12751293
}
12761294
return certificatePath;
12771295
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDIzCCAgugAwIBAgIJAMfISuBQ5m+5MA0GCSqGSIb3DQEBBQUAMBUxEzARBgNV
3+
BAMTCnVuaXQtdGVzdHMwHhcNMTExMjA2MTYyNjAyWhcNMjExMjAzMTYyNjAyWjAV
4+
MRMwEQYDVQQDEwp1bml0LXRlc3RzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
5+
CgKCAQEA4ej0p7bQ7L/r4rVGUz9RN4VQWoej1Bg1mYWIDYslvKrk1gpj7wZgkdmM
6+
7oVK2OfgrSj/FCTkInKPqaCR0gD7K80q+mLBrN3PUkDrJQZpvRZIff3/xmVU1Wer
7+
uQLFJjnFb2dqu0s/FY/2kWiJtBCakXvXEOb7zfbINuayL+MSsCGSdVYsSliS5qQp
8+
gyDap+8b5fpXZVJkq92hrcNtbkg7hCYUJczt8n9hcCTJCfUpApvaFQ18pe+zpyl4
9+
+WzkP66I28hniMQyUlA1hBiskT7qiouq0m8IOodhv2fagSZKjOTTU2xkSBc//fy3
10+
ZpsL7WqgsZS7Q+0VRK8gKfqkxg5OYQIDAQABo3YwdDAdBgNVHQ4EFgQU2RQ8yO+O
11+
gN8oVW2SW7RLrfYd9jEwRQYDVR0jBD4wPIAU2RQ8yO+OgN8oVW2SW7RLrfYd9jGh
12+
GaQXMBUxEzARBgNVBAMTCnVuaXQtdGVzdHOCCQDHyErgUOZvuTAMBgNVHRMEBTAD
13+
AQH/MA0GCSqGSIb3DQEBBQUAA4IBAQBRv+M/6+FiVu7KXNjFI5pSN17OcW5QUtPr
14+
odJMlWrJBtynn/TA1oJlYu3yV5clc/71Vr/AxuX5xGP+IXL32YDF9lTUJXG/uUGk
15+
+JETpKmQviPbRsvzYhz4pf6ZIOZMc3/GIcNq92ECbseGO+yAgyWUVKMmZM0HqXC9
16+
ovNslqe0M8C1sLm1zAR5z/h/litE7/8O2ietija3Q/qtl2TOXJdCA6sgjJX2WUql
17+
ybrC55ct18NKf3qhpcEkGQvFU40rVYApJpi98DiZPYFdx1oBDp/f4uZ3ojpxRVFT
18+
cDwcJLfNRCPUhormsY7fDS9xSyThiHsW9mjJYdcaKQkwYZ0F11yB
19+
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)