• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grpc / grpc-java / #19157

08 Apr 2024 06:30PM CUT coverage: 88.312%. Remained the same
#19157

push

github

web-flow
buildscripts: Migrate PSM Interop to Artifact Registry (#11079) (#11092)

From Container Registry (gcr.io) to Artifact Registry (pkg.dev).

30337 of 34352 relevant lines covered (88.31%)

0.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.37
/../util/src/main/java/io/grpc/util/AdvancedTlsX509KeyManager.java
1
/*
2
 * Copyright 2021 The gRPC Authors
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package io.grpc.util;
18

19
import static com.google.common.base.Preconditions.checkNotNull;
20

21
import io.grpc.ExperimentalApi;
22
import java.io.File;
23
import java.io.FileInputStream;
24
import java.io.IOException;
25
import java.net.Socket;
26
import java.security.GeneralSecurityException;
27
import java.security.Principal;
28
import java.security.PrivateKey;
29
import java.security.cert.CertificateException;
30
import java.security.cert.X509Certificate;
31
import java.util.Arrays;
32
import java.util.concurrent.ScheduledExecutorService;
33
import java.util.concurrent.ScheduledFuture;
34
import java.util.concurrent.TimeUnit;
35
import java.util.logging.Level;
36
import java.util.logging.Logger;
37
import javax.net.ssl.SSLEngine;
38
import javax.net.ssl.X509ExtendedKeyManager;
39

40
/**
41
 * AdvancedTlsX509KeyManager is an {@code X509ExtendedKeyManager} that allows users to configure
42
 * advanced TLS features, such as private key and certificate chain reloading, etc.
43
 */
44
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/8024")
45
public final class AdvancedTlsX509KeyManager extends X509ExtendedKeyManager {
46
  private static final Logger log = Logger.getLogger(AdvancedTlsX509KeyManager.class.getName());
1✔
47

48
  // The credential information sent to peers to prove our identity.
49
  private volatile KeyInfo keyInfo;
50

51
  /**
52
   * Constructs an AdvancedTlsX509KeyManager.
53
   */
54
  public AdvancedTlsX509KeyManager() throws CertificateException { }
1✔
55

56
  @Override
57
  public PrivateKey getPrivateKey(String alias) {
58
    if (alias.equals("default")) {
1✔
59
      return this.keyInfo.key;
1✔
60
    }
61
    return null;
×
62
  }
63

64
  @Override
65
  public X509Certificate[] getCertificateChain(String alias) {
66
    if (alias.equals("default")) {
1✔
67
      return Arrays.copyOf(this.keyInfo.certs, this.keyInfo.certs.length);
1✔
68
    }
69
    return null;
×
70
  }
71

72
  @Override
73
  public String[] getClientAliases(String keyType, Principal[] issuers) {
74
    return new String[] {"default"};
1✔
75
  }
76

77
  @Override
78
  public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
79
    return "default";
1✔
80
  }
81

82
  @Override
83
  public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) {
84
    return "default";
1✔
85
  }
86

87
  @Override
88
  public String[] getServerAliases(String keyType, Principal[] issuers) {
89
    return new String[] {"default"};
1✔
90
  }
91

92
  @Override
93
  public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
94
    return "default";
1✔
95
  }
96

97
  @Override
98
  public String chooseEngineServerAlias(String keyType, Principal[] issuers,
99
      SSLEngine engine) {
100
    return "default";
1✔
101
  }
102

103
  /**
104
   * Updates the current cached private key and cert chains.
105
   *
106
   * @param key  the private key that is going to be used
107
   * @param certs  the certificate chain that is going to be used
108
   */
109
  public void updateIdentityCredentials(PrivateKey key, X509Certificate[] certs) {
110
    // TODO(ZhenLian): explore possibilities to do a crypto check here.
111
    this.keyInfo = new KeyInfo(checkNotNull(key, "key"), checkNotNull(certs, "certs"));
1✔
112
  }
1✔
113

114
  /**
115
   * Schedules a {@code ScheduledExecutorService} to read private key and certificate chains from
116
   * the local file paths periodically, and update the cached identity credentials if they are both
117
   * updated.
118
   *
119
   * @param keyFile  the file on disk holding the private key
120
   * @param certFile  the file on disk holding the certificate chain
121
   * @param period the period between successive read-and-update executions
122
   * @param unit the time unit of the initialDelay and period parameters
123
   * @param executor the execute service we use to read and update the credentials
124
   * @return an object that caller should close when the file refreshes are not needed
125
   */
126
  public Closeable updateIdentityCredentialsFromFile(File keyFile, File certFile,
127
      long period, TimeUnit unit, ScheduledExecutorService executor) throws IOException,
128
      GeneralSecurityException {
129
    UpdateResult newResult = readAndUpdate(keyFile, certFile, 0, 0);
1✔
130
    if (!newResult.success) {
1✔
131
      throw new GeneralSecurityException(
×
132
          "Files were unmodified before their initial update. Probably a bug.");
133
    }
134
    final ScheduledFuture<?> future =
1✔
135
        executor.scheduleWithFixedDelay(
1✔
136
            new LoadFilePathExecution(keyFile, certFile), period, period, unit);
137
    return new Closeable() {
1✔
138
      @Override public void close() {
139
        future.cancel(false);
1✔
140
      }
1✔
141
    };
142
  }
143

144
  /**
145
   * Updates the private key and certificate chains from the local file paths.
146
   *
147
   * @param keyFile  the file on disk holding the private key
148
   * @param certFile  the file on disk holding the certificate chain
149
   */
150
  public void updateIdentityCredentialsFromFile(File keyFile, File certFile) throws IOException,
151
      GeneralSecurityException {
152
    UpdateResult newResult = readAndUpdate(keyFile, certFile, 0, 0);
1✔
153
    if (!newResult.success) {
1✔
154
      throw new GeneralSecurityException(
×
155
          "Files were unmodified before their initial update. Probably a bug.");
156
    }
157
  }
1✔
158

159
  private static class KeyInfo {
160
    // The private key and the cert chain we will use to send to peers to prove our identity.
161
    final PrivateKey key;
162
    final X509Certificate[] certs;
163

164
    public KeyInfo(PrivateKey key, X509Certificate[] certs) {
1✔
165
      this.key = key;
1✔
166
      this.certs = certs;
1✔
167
    }
1✔
168
  }
169

170
  private class LoadFilePathExecution implements Runnable {
171
    File keyFile;
172
    File certFile;
173
    long currentKeyTime;
174
    long currentCertTime;
175

176
    public LoadFilePathExecution(File keyFile, File certFile) {
1✔
177
      this.keyFile = keyFile;
1✔
178
      this.certFile = certFile;
1✔
179
      this.currentKeyTime = 0;
1✔
180
      this.currentCertTime = 0;
1✔
181
    }
1✔
182

183
    @Override
184
    public void run() {
185
      try {
186
        UpdateResult newResult = readAndUpdate(this.keyFile, this.certFile, this.currentKeyTime,
×
187
            this.currentCertTime);
188
        if (newResult.success) {
×
189
          this.currentKeyTime = newResult.keyTime;
×
190
          this.currentCertTime = newResult.certTime;
×
191
        }
192
      } catch (IOException | GeneralSecurityException e) {
×
193
        log.log(Level.SEVERE, "Failed refreshing private key and certificate chain from files. "
×
194
            + "Using previous ones", e);
195
      }
×
196
    }
×
197
  }
198

199
  private static class UpdateResult {
200
    boolean success;
201
    long keyTime;
202
    long certTime;
203

204
    public UpdateResult(boolean success, long keyTime, long certTime) {
1✔
205
      this.success = success;
1✔
206
      this.keyTime = keyTime;
1✔
207
      this.certTime = certTime;
1✔
208
    }
1✔
209
  }
210

211
  /**
212
   * Reads the private key and certificates specified in the path locations. Updates {@code key} and
213
   * {@code cert} if both of their modified time changed since last read.
214
   *
215
   * @param keyFile  the file on disk holding the private key
216
   * @param certFile  the file on disk holding the certificate chain
217
   * @param oldKeyTime the time when the private key file is modified during last execution
218
   * @param oldCertTime the time when the certificate chain file is modified during last execution
219
   * @return the result of this update execution
220
   */
221
  private UpdateResult readAndUpdate(File keyFile, File certFile, long oldKeyTime, long oldCertTime)
222
      throws IOException, GeneralSecurityException {
223
    long newKeyTime = keyFile.lastModified();
1✔
224
    long newCertTime = certFile.lastModified();
1✔
225
    // We only update when both the key and the certs are updated.
226
    if (newKeyTime != oldKeyTime && newCertTime != oldCertTime) {
1✔
227
      FileInputStream keyInputStream = new FileInputStream(keyFile);
1✔
228
      try {
229
        PrivateKey key = CertificateUtils.getPrivateKey(keyInputStream);
1✔
230
        FileInputStream certInputStream = new FileInputStream(certFile);
1✔
231
        try {
232
          X509Certificate[] certs = CertificateUtils.getX509Certificates(certInputStream);
1✔
233
          updateIdentityCredentials(key, certs);
1✔
234
          return new UpdateResult(true, newKeyTime, newCertTime);
1✔
235
        } finally {
236
          certInputStream.close();
1✔
237
        }
238
      } finally {
239
        keyInputStream.close();
1✔
240
      }
241
    }
242
    return new UpdateResult(false, oldKeyTime, oldCertTime);
×
243
  }
244

245
  /**
246
   * Mainly used to avoid throwing IO Exceptions in java.io.Closeable.
247
   */
248
  public interface Closeable extends java.io.Closeable {
249
    @Override
250
    void close();
251
  }
252
}
253

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc