Skip to content

Commit ecae9b7

Browse files
authored
security: Add updateIdentityCredentials methods to AdvancedTlsX509KeyManager. (grpc#11358)
Add new 'updateIdentityCredentials' methods with swapped (chain, key) signatures.
1 parent 21dec30 commit ecae9b7

4 files changed

Lines changed: 101 additions & 47 deletions

File tree

netty/src/test/java/io/grpc/netty/AdvancedTlsTest.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ public void basicMutualTlsTest() throws Exception {
147147
public void advancedTlsKeyManagerTrustManagerMutualTlsTest() throws Exception {
148148
// Create a server with the key manager and trust manager.
149149
AdvancedTlsX509KeyManager serverKeyManager = new AdvancedTlsX509KeyManager();
150-
serverKeyManager.updateIdentityCredentials(serverKey0, serverCert0);
150+
serverKeyManager.updateIdentityCredentials(serverCert0, serverKey0);
151151
AdvancedTlsX509TrustManager serverTrustManager = AdvancedTlsX509TrustManager.newBuilder()
152152
.setVerification(Verification.CERTIFICATE_ONLY_VERIFICATION)
153153
.build();
@@ -159,7 +159,7 @@ public void advancedTlsKeyManagerTrustManagerMutualTlsTest() throws Exception {
159159
new SimpleServiceImpl()).build().start();
160160
// Create a client with the key manager and trust manager.
161161
AdvancedTlsX509KeyManager clientKeyManager = new AdvancedTlsX509KeyManager();
162-
clientKeyManager.updateIdentityCredentials(clientKey0, clientCert0);
162+
clientKeyManager.updateIdentityCredentials(clientCert0, clientKey0);
163163
AdvancedTlsX509TrustManager clientTrustManager = AdvancedTlsX509TrustManager.newBuilder()
164164
.setVerification(Verification.CERTIFICATE_AND_HOST_NAME_VERIFICATION)
165165
.build();
@@ -182,7 +182,7 @@ public void advancedTlsKeyManagerTrustManagerMutualTlsTest() throws Exception {
182182
@Test
183183
public void trustManagerCustomVerifierMutualTlsTest() throws Exception {
184184
AdvancedTlsX509KeyManager serverKeyManager = new AdvancedTlsX509KeyManager();
185-
serverKeyManager.updateIdentityCredentials(serverKey0, serverCert0);
185+
serverKeyManager.updateIdentityCredentials(serverCert0, serverKey0);
186186
// Set server's custom verification based on the information of clientCert0.
187187
AdvancedTlsX509TrustManager serverTrustManager = AdvancedTlsX509TrustManager.newBuilder()
188188
.setVerification(Verification.CERTIFICATE_ONLY_VERIFICATION)
@@ -221,7 +221,7 @@ public void verifyPeerCertificate(X509Certificate[] peerCertChain, String authTy
221221
new SimpleServiceImpl()).build().start();
222222

223223
AdvancedTlsX509KeyManager clientKeyManager = new AdvancedTlsX509KeyManager();
224-
clientKeyManager.updateIdentityCredentials(clientKey0, clientCert0);
224+
clientKeyManager.updateIdentityCredentials(clientCert0, clientKey0);
225225
// Set client's custom verification based on the information of serverCert0.
226226
AdvancedTlsX509TrustManager clientTrustManager = AdvancedTlsX509TrustManager.newBuilder()
227227
.setVerification(Verification.CERTIFICATE_ONLY_VERIFICATION)
@@ -275,7 +275,7 @@ public void trustManagerInsecurelySkipAllTest() throws Exception {
275275
AdvancedTlsX509KeyManager serverKeyManager = new AdvancedTlsX509KeyManager();
276276
// Even if we provide bad credentials for the server, the test should still pass, because we
277277
// will configure the client to skip all checks later.
278-
serverKeyManager.updateIdentityCredentials(serverKeyBad, serverCertBad);
278+
serverKeyManager.updateIdentityCredentials(serverCertBad, serverKeyBad);
279279
AdvancedTlsX509TrustManager serverTrustManager = AdvancedTlsX509TrustManager.newBuilder()
280280
.setVerification(Verification.CERTIFICATE_ONLY_VERIFICATION)
281281
.setSslSocketAndEnginePeerVerifier(
@@ -297,7 +297,7 @@ public void verifyPeerCertificate(X509Certificate[] peerCertChain, String authTy
297297
new SimpleServiceImpl()).build().start();
298298

299299
AdvancedTlsX509KeyManager clientKeyManager = new AdvancedTlsX509KeyManager();
300-
clientKeyManager.updateIdentityCredentials(clientKey0, clientCert0);
300+
clientKeyManager.updateIdentityCredentials(clientCert0, clientKey0);
301301
// Set the client to skip all checks, including traditional certificate verification.
302302
// Note this is very dangerous in production environment - only do so if you are confident on
303303
// what you are doing!
@@ -334,8 +334,8 @@ public void verifyPeerCertificate(X509Certificate[] peerCertChain, String authTy
334334
public void onFileReloadingKeyManagerTrustManagerTest() throws Exception {
335335
// Create & start a server.
336336
AdvancedTlsX509KeyManager serverKeyManager = new AdvancedTlsX509KeyManager();
337-
Closeable serverKeyShutdown = serverKeyManager.updateIdentityCredentialsFromFile(serverKey0File,
338-
serverCert0File, 100, TimeUnit.MILLISECONDS, executor);
337+
Closeable serverKeyShutdown = serverKeyManager.updateIdentityCredentials(serverCert0File,
338+
serverKey0File, 100, TimeUnit.MILLISECONDS, executor);
339339
AdvancedTlsX509TrustManager serverTrustManager = AdvancedTlsX509TrustManager.newBuilder()
340340
.setVerification(Verification.CERTIFICATE_ONLY_VERIFICATION)
341341
.build();
@@ -348,8 +348,8 @@ public void onFileReloadingKeyManagerTrustManagerTest() throws Exception {
348348
new SimpleServiceImpl()).build().start();
349349
// Create a client to connect.
350350
AdvancedTlsX509KeyManager clientKeyManager = new AdvancedTlsX509KeyManager();
351-
Closeable clientKeyShutdown = clientKeyManager.updateIdentityCredentialsFromFile(clientKey0File,
352-
clientCert0File,100, TimeUnit.MILLISECONDS, executor);
351+
Closeable clientKeyShutdown = clientKeyManager.updateIdentityCredentials(clientCert0File,
352+
clientKey0File,100, TimeUnit.MILLISECONDS, executor);
353353
AdvancedTlsX509TrustManager clientTrustManager = AdvancedTlsX509TrustManager.newBuilder()
354354
.setVerification(Verification.CERTIFICATE_AND_HOST_NAME_VERIFICATION)
355355
.build();
@@ -381,7 +381,7 @@ public void onFileReloadingKeyManagerTrustManagerTest() throws Exception {
381381
public void onFileLoadingKeyManagerTrustManagerTest() throws Exception {
382382
// Create & start a server.
383383
AdvancedTlsX509KeyManager serverKeyManager = new AdvancedTlsX509KeyManager();
384-
serverKeyManager.updateIdentityCredentialsFromFile(serverKey0File, serverCert0File);
384+
serverKeyManager.updateIdentityCredentials(serverCert0File, serverKey0File);
385385
AdvancedTlsX509TrustManager serverTrustManager = AdvancedTlsX509TrustManager.newBuilder()
386386
.setVerification(Verification.CERTIFICATE_ONLY_VERIFICATION)
387387
.build();
@@ -393,7 +393,7 @@ public void onFileLoadingKeyManagerTrustManagerTest() throws Exception {
393393
new SimpleServiceImpl()).build().start();
394394
// Create a client to connect.
395395
AdvancedTlsX509KeyManager clientKeyManager = new AdvancedTlsX509KeyManager();
396-
clientKeyManager.updateIdentityCredentialsFromFile(clientKey0File, clientCert0File);
396+
clientKeyManager.updateIdentityCredentials(clientCert0File, clientKey0File);
397397
AdvancedTlsX509TrustManager clientTrustManager = AdvancedTlsX509TrustManager.newBuilder()
398398
.setVerification(Verification.CERTIFICATE_AND_HOST_NAME_VERIFICATION)
399399
.build();
@@ -420,8 +420,8 @@ public void onFileReloadingKeyManagerBadInitialContentTest() {
420420
AdvancedTlsX509KeyManager keyManager = new AdvancedTlsX509KeyManager();
421421
// We swap the order of key and certificates to intentionally create an exception.
422422
assertThrows(GeneralSecurityException.class,
423-
() -> keyManager.updateIdentityCredentialsFromFile(serverCert0File,
424-
serverKey0File, 100, TimeUnit.MILLISECONDS, executor));
423+
() -> keyManager.updateIdentityCredentials(serverKey0File, serverCert0File,
424+
100, TimeUnit.MILLISECONDS, executor));
425425
}
426426

427427
@Test

util/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ java_library(
1313
"//api",
1414
"//core:internal",
1515
artifact("com.google.code.findbugs:jsr305"),
16+
artifact("com.google.errorprone:error_prone_annotations"),
1617
artifact("com.google.guava:guava"),
1718
artifact("com.google.j2objc:j2objc-annotations"),
1819
artifact("org.codehaus.mojo:animal-sniffer-annotations"),

util/src/main/java/io/grpc/util/AdvancedTlsX509KeyManager.java

Lines changed: 74 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
import static com.google.common.base.Preconditions.checkNotNull;
2020

21-
import io.grpc.ExperimentalApi;
21+
import com.google.errorprone.annotations.InlineMe;
2222
import java.io.File;
2323
import java.io.FileInputStream;
2424
import java.io.IOException;
@@ -40,7 +40,6 @@
4040
* AdvancedTlsX509KeyManager is an {@code X509ExtendedKeyManager} that allows users to configure
4141
* advanced TLS features, such as private key and certificate chain reloading.
4242
*/
43-
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/8024")
4443
public final class AdvancedTlsX509KeyManager extends X509ExtendedKeyManager {
4544
private static final Logger log = Logger.getLogger(AdvancedTlsX509KeyManager.class.getName());
4645
// Minimum allowed period for refreshing files with credential information.
@@ -100,31 +99,44 @@ public String chooseEngineServerAlias(String keyType, Principal[] issuers,
10099
*
101100
* @param key the private key that is going to be used
102101
* @param certs the certificate chain that is going to be used
102+
* @deprecated Use {@link #updateIdentityCredentials(X509Certificate[], PrivateKey)}
103103
*/
104+
@Deprecated
105+
@InlineMe(replacement = "this.updateIdentityCredentials(certs, key)")
104106
public void updateIdentityCredentials(PrivateKey key, X509Certificate[] certs) {
107+
updateIdentityCredentials(certs, key);
108+
}
109+
110+
/**
111+
* Updates the current cached private key and cert chains.
112+
*
113+
* @param certs the certificate chain that is going to be used
114+
* @param key the private key that is going to be used
115+
*/
116+
public void updateIdentityCredentials(X509Certificate[] certs, PrivateKey key) {
105117
this.keyInfo = new KeyInfo(checkNotNull(key, "key"), checkNotNull(certs, "certs"));
106118
}
107119

108120
/**
109-
* Schedules a {@code ScheduledExecutorService} to read private key and certificate chains from
121+
* Schedules a {@code ScheduledExecutorService} to read certificate chains and private key from
110122
* the local file paths periodically, and update the cached identity credentials if they are both
111123
* updated. You must close the returned Closeable before calling this method again or other update
112124
* methods ({@link AdvancedTlsX509KeyManager#updateIdentityCredentials}, {@link
113-
* AdvancedTlsX509KeyManager#updateIdentityCredentialsFromFile(File, File)}).
125+
* AdvancedTlsX509KeyManager#updateIdentityCredentials(File, File)}).
114126
* Before scheduling the task, the method synchronously executes {@code readAndUpdate} once. The
115127
* minimum refresh period of 1 minute is enforced.
116128
*
117-
* @param keyFile the file on disk holding the private key
118129
* @param certFile the file on disk holding the certificate chain
130+
* @param keyFile the file on disk holding the private key
119131
* @param period the period between successive read-and-update executions
120132
* @param unit the time unit of the initialDelay and period parameters
121-
* @param executor the execute service we use to read and update the credentials
133+
* @param executor the executor service we use to read and update the credentials
122134
* @return an object that caller should close when the file refreshes are not needed
123135
*/
124-
public Closeable updateIdentityCredentialsFromFile(File keyFile, File certFile,
136+
public Closeable updateIdentityCredentials(File certFile, File keyFile,
125137
long period, TimeUnit unit, ScheduledExecutorService executor) throws IOException,
126138
GeneralSecurityException {
127-
UpdateResult newResult = readAndUpdate(keyFile, certFile, 0, 0);
139+
UpdateResult newResult = readAndUpdate(certFile, keyFile, 0, 0);
128140
if (!newResult.success) {
129141
throw new GeneralSecurityException(
130142
"Files were unmodified before their initial update. Probably a bug.");
@@ -138,25 +150,66 @@ public Closeable updateIdentityCredentialsFromFile(File keyFile, File certFile,
138150
}
139151
final ScheduledFuture<?> future =
140152
checkNotNull(executor, "executor").scheduleWithFixedDelay(
141-
new LoadFilePathExecution(keyFile, certFile), period, period, unit);
153+
new LoadFilePathExecution(certFile, keyFile), period, period, unit);
142154
return () -> future.cancel(false);
143155
}
144156

145157
/**
146-
* Updates the private key and certificate chains from the local file paths.
158+
* Updates certificate chains and the private key from the local file paths.
147159
*
148-
* @param keyFile the file on disk holding the private key
149160
* @param certFile the file on disk holding the certificate chain
161+
* @param keyFile the file on disk holding the private key
150162
*/
151-
public void updateIdentityCredentialsFromFile(File keyFile, File certFile) throws IOException,
163+
public void updateIdentityCredentials(File certFile, File keyFile) throws IOException,
152164
GeneralSecurityException {
153-
UpdateResult newResult = readAndUpdate(keyFile, certFile, 0, 0);
165+
UpdateResult newResult = readAndUpdate(certFile, keyFile, 0, 0);
154166
if (!newResult.success) {
155167
throw new GeneralSecurityException(
156168
"Files were unmodified before their initial update. Probably a bug.");
157169
}
158170
}
159171

172+
/**
173+
* Updates the private key and certificate chains from the local file paths.
174+
*
175+
* @param keyFile the file on disk holding the private key
176+
* @param certFile the file on disk holding the certificate chain
177+
* @deprecated Use {@link #updateIdentityCredentials(File, File)} instead.
178+
*/
179+
@Deprecated
180+
@InlineMe(replacement = "this.updateIdentityCredentials(certFile, keyFile)")
181+
public void updateIdentityCredentialsFromFile(File keyFile, File certFile) throws IOException,
182+
GeneralSecurityException {
183+
updateIdentityCredentials(certFile, keyFile);
184+
}
185+
186+
/**
187+
* Schedules a {@code ScheduledExecutorService} to read private key and certificate chains from
188+
* the local file paths periodically, and update the cached identity credentials if they are both
189+
* updated. You must close the returned Closeable before calling this method again or other update
190+
* methods ({@link AdvancedTlsX509KeyManager#updateIdentityCredentials}, {@link
191+
* AdvancedTlsX509KeyManager#updateIdentityCredentials(File, File)}).
192+
* Before scheduling the task, the method synchronously executes {@code readAndUpdate} once. The
193+
* minimum refresh period of 1 minute is enforced.
194+
*
195+
* @param keyFile the file on disk holding the private key
196+
* @param certFile the file on disk holding the certificate chain
197+
* @param period the period between successive read-and-update executions
198+
* @param unit the time unit of the initialDelay and period parameters
199+
* @param executor the executor service we use to read and update the credentials
200+
* @return an object that caller should close when the file refreshes are not needed
201+
* @deprecated Use {@link
202+
* #updateIdentityCredentials(File, File, long, TimeUnit, ScheduledExecutorService)} instead.
203+
*/
204+
@Deprecated
205+
@InlineMe(replacement =
206+
"this.updateIdentityCredentials(certFile, keyFile, period, unit, executor)")
207+
public Closeable updateIdentityCredentialsFromFile(File keyFile, File certFile,
208+
long period, TimeUnit unit, ScheduledExecutorService executor) throws IOException,
209+
GeneralSecurityException {
210+
return updateIdentityCredentials(certFile, keyFile, period, unit, executor);
211+
}
212+
160213
private static class KeyInfo {
161214
// The private key and the cert chain we will use to send to peers to prove our identity.
162215
final PrivateKey key;
@@ -174,26 +227,26 @@ private class LoadFilePathExecution implements Runnable {
174227
long currentKeyTime;
175228
long currentCertTime;
176229

177-
public LoadFilePathExecution(File keyFile, File certFile) {
178-
this.keyFile = keyFile;
230+
public LoadFilePathExecution(File certFile, File keyFile) {
179231
this.certFile = certFile;
232+
this.keyFile = keyFile;
180233
this.currentKeyTime = 0;
181234
this.currentCertTime = 0;
182235
}
183236

184237
@Override
185238
public void run() {
186239
try {
187-
UpdateResult newResult = readAndUpdate(this.keyFile, this.certFile, this.currentKeyTime,
240+
UpdateResult newResult = readAndUpdate(this.certFile, this.keyFile, this.currentKeyTime,
188241
this.currentCertTime);
189242
if (newResult.success) {
190243
this.currentKeyTime = newResult.keyTime;
191244
this.currentCertTime = newResult.certTime;
192245
}
193246
} catch (IOException | GeneralSecurityException e) {
194247
log.log(Level.SEVERE, String.format("Failed refreshing private key and certificate"
195-
+ " chain from files. Using previous ones (keyFile lastModified = %s, certFile "
196-
+ "lastModified = %s)", keyFile.lastModified(), certFile.lastModified()), e);
248+
+ " chain from files. Using previous ones (certFile lastModified = %s, keyFile "
249+
+ "lastModified = %s)", certFile.lastModified(), keyFile.lastModified()), e);
197250
}
198251
}
199252
}
@@ -214,13 +267,13 @@ public UpdateResult(boolean success, long keyTime, long certTime) {
214267
* Reads the private key and certificates specified in the path locations. Updates {@code key} and
215268
* {@code cert} if both of their modified time changed since last read.
216269
*
217-
* @param keyFile the file on disk holding the private key
218270
* @param certFile the file on disk holding the certificate chain
271+
* @param keyFile the file on disk holding the private key
219272
* @param oldKeyTime the time when the private key file is modified during last execution
220273
* @param oldCertTime the time when the certificate chain file is modified during last execution
221274
* @return the result of this update execution
222275
*/
223-
private UpdateResult readAndUpdate(File keyFile, File certFile, long oldKeyTime, long oldCertTime)
276+
private UpdateResult readAndUpdate(File certFile, File keyFile, long oldKeyTime, long oldCertTime)
224277
throws IOException, GeneralSecurityException {
225278
long newKeyTime = checkNotNull(keyFile, "keyFile").lastModified();
226279
long newCertTime = checkNotNull(certFile, "certFile").lastModified();
@@ -232,7 +285,7 @@ private UpdateResult readAndUpdate(File keyFile, File certFile, long oldKeyTime,
232285
FileInputStream certInputStream = new FileInputStream(certFile);
233286
try {
234287
X509Certificate[] certs = CertificateUtils.getX509Certificates(certInputStream);
235-
updateIdentityCredentials(key, certs);
288+
updateIdentityCredentials(certs, key);
236289
return new UpdateResult(true, newKeyTime, newCertTime);
237290
} finally {
238291
certInputStream.close();

0 commit comments

Comments
 (0)