Skip to content

Commit

Permalink
Merge branch 'grpc:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
temawi committed Aug 19, 2021
2 parents bbb088f + e32e177 commit a795d3c
Show file tree
Hide file tree
Showing 11 changed files with 1,114 additions and 56 deletions.
36 changes: 18 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ For a guided tour, take a look at the [quick start
guide](https://grpc.io/docs/languages/java/quickstart) or the more explanatory [gRPC
basics](https://grpc.io/docs/languages/java/basics).

The [examples](https://github.com/grpc/grpc-java/tree/v1.39.0/examples) and the
[Android example](https://github.com/grpc/grpc-java/tree/v1.39.0/examples/android)
The [examples](https://github.com/grpc/grpc-java/tree/v1.40.0/examples) and the
[Android example](https://github.com/grpc/grpc-java/tree/v1.40.0/examples/android)
are standalone projects that showcase the usage of gRPC.

Download
Expand All @@ -43,17 +43,17 @@ Download [the JARs][]. Or for Maven with non-Android, add to your `pom.xml`:
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.39.0</version>
<version>1.40.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.39.0</version>
<version>1.40.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.39.0</version>
<version>1.40.0</version>
</dependency>
<dependency> <!-- necessary for Java 9+ -->
<groupId>org.apache.tomcat</groupId>
Expand All @@ -65,23 +65,23 @@ Download [the JARs][]. Or for Maven with non-Android, add to your `pom.xml`:

Or for Gradle with non-Android, add to your dependencies:
```gradle
implementation 'io.grpc:grpc-netty-shaded:1.39.0'
implementation 'io.grpc:grpc-protobuf:1.39.0'
implementation 'io.grpc:grpc-stub:1.39.0'
implementation 'io.grpc:grpc-netty-shaded:1.40.0'
implementation 'io.grpc:grpc-protobuf:1.40.0'
implementation 'io.grpc:grpc-stub:1.40.0'
compileOnly 'org.apache.tomcat:annotations-api:6.0.53' // necessary for Java 9+
```

For Android client, use `grpc-okhttp` instead of `grpc-netty-shaded` and
`grpc-protobuf-lite` instead of `grpc-protobuf`:
```gradle
implementation 'io.grpc:grpc-okhttp:1.39.0'
implementation 'io.grpc:grpc-protobuf-lite:1.39.0'
implementation 'io.grpc:grpc-stub:1.39.0'
implementation 'io.grpc:grpc-okhttp:1.40.0'
implementation 'io.grpc:grpc-protobuf-lite:1.40.0'
implementation 'io.grpc:grpc-stub:1.40.0'
compileOnly 'org.apache.tomcat:annotations-api:6.0.53' // necessary for Java 9+
```

[the JARs]:
https://search.maven.org/search?q=g:io.grpc%20AND%20v:1.39.0
https://search.maven.org/search?q=g:io.grpc%20AND%20v:1.40.0

Development snapshots are available in [Sonatypes's snapshot
repository](https://oss.sonatype.org/content/repositories/snapshots/).
Expand Down Expand Up @@ -111,9 +111,9 @@ For protobuf-based codegen integrated with the Maven build system, you can use
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.17.2:exe:${os.detected.classifier}</protocArtifact>
<protocArtifact>com.google.protobuf:protoc:3.17.3:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.39.0:exe:${os.detected.classifier}</pluginArtifact>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.40.0:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
Expand All @@ -139,11 +139,11 @@ plugins {
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.17.2"
artifact = "com.google.protobuf:protoc:3.17.3"
}
plugins {
grpc {
artifact = 'io.grpc:protoc-gen-grpc-java:1.39.0'
artifact = 'io.grpc:protoc-gen-grpc-java:1.40.0'
}
}
generateProtoTasks {
Expand Down Expand Up @@ -172,11 +172,11 @@ plugins {
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.17.2"
artifact = "com.google.protobuf:protoc:3.17.3"
}
plugins {
grpc {
artifact = 'io.grpc:protoc-gen-grpc-java:1.39.0'
artifact = 'io.grpc:protoc-gen-grpc-java:1.40.0'
}
}
generateProtoTasks {
Expand Down
5 changes: 3 additions & 2 deletions buildscripts/kokoro/android.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ export OS_NAME=$(uname)
cat <<EOF >> gradle.properties
# defaults to -Xmx512m -XX:MaxMetaspaceSize=256m
# https://docs.gradle.org/current/userguide/build_environment.html#sec:configuring_jvm_memory
# Increased due to java.lang.OutOfMemoryError: Metaspace failures
org.gradle.jvmargs=-Xmx512m -XX:MaxMetaspaceSize=512m
# Increased due to java.lang.OutOfMemoryError: Metaspace failures, "JVM heap
# space is exhausted", and to increase build speed
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
EOF

echo y | ${ANDROID_HOME}/tools/bin/sdkmanager "build-tools;28.0.3"
Expand Down
1 change: 1 addition & 0 deletions core/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ java_library(
"@com_google_code_findbugs_jsr305//jar",
"@com_google_guava_guava//jar",
"@com_google_j2objc_j2objc_annotations//jar",
"@org_codehaus_mojo_animal_sniffer_annotations//jar",
],
)

Expand Down
234 changes: 234 additions & 0 deletions core/src/main/java/io/grpc/util/AdvancedTlsX509KeyManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
/*
* Copyright 2021 The gRPC Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.grpc.util;

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

import io.grpc.ExperimentalApi;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.Socket;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509ExtendedKeyManager;

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

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

/**
* Constructs an AdvancedTlsX509KeyManager.
*/
public AdvancedTlsX509KeyManager() throws CertificateException { }

@Override
public PrivateKey getPrivateKey(String alias) {
if (alias.equals("default")) {
return this.keyInfo.key;
}
return null;
}

@Override
public X509Certificate[] getCertificateChain(String alias) {
if (alias.equals("default")) {
return Arrays.copyOf(this.keyInfo.certs, this.keyInfo.certs.length);
}
return null;
}

@Override
public String[] getClientAliases(String keyType, Principal[] issuers) {
return new String[] {"default"};
}

@Override
public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
return "default";
}

@Override
public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) {
return "default";
}

@Override
public String[] getServerAliases(String keyType, Principal[] issuers) {
return new String[] {"default"};
}

@Override
public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
return "default";
}

@Override
public String chooseEngineServerAlias(String keyType, Principal[] issuers,
SSLEngine engine) {
return "default";
}

/**
* Updates the current cached private key and cert chains.
*
* @param key the private key that is going to be used
* @param certs the certificate chain that is going to be used
*/
public void updateIdentityCredentials(PrivateKey key, X509Certificate[] certs)
throws CertificateException {
// TODO(ZhenLian): explore possibilities to do a crypto check here.
this.keyInfo = new KeyInfo(checkNotNull(key, "key"), checkNotNull(certs, "certs"));
}

/**
* Schedules a {@code ScheduledExecutorService} to read private key and certificate chains from
* the local file paths periodically, and update the cached identity credentials if they are both
* updated.
*
* @param keyFile the file on disk holding the private key
* @param certFile the file on disk holding the certificate chain
* @param period the period between successive read-and-update executions
* @param unit the time unit of the initialDelay and period parameters
* @param executor the execute service we use to read and update the credentials
* @return an object that caller should close when the file refreshes are not needed
*/
public Closeable updateIdentityCredentialsFromFile(File keyFile, File certFile,
long period, TimeUnit unit, ScheduledExecutorService executor) {
final ScheduledFuture<?> future =
executor.scheduleWithFixedDelay(
new LoadFilePathExecution(keyFile, certFile), 0, period, unit);
return new Closeable() {
@Override public void close() {
future.cancel(false);
}
};
}

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

public KeyInfo(PrivateKey key, X509Certificate[] certs) {
this.key = key;
this.certs = certs;
}
}

private class LoadFilePathExecution implements Runnable {
File keyFile;
File certFile;
long currentKeyTime;
long currentCertTime;

public LoadFilePathExecution(File keyFile, File certFile) {
this.keyFile = keyFile;
this.certFile = certFile;
this.currentKeyTime = 0;
this.currentCertTime = 0;
}

@Override
public void run() {
try {
UpdateResult newResult = readAndUpdate(this.keyFile, this.certFile, this.currentKeyTime,
this.currentCertTime);
if (newResult.success) {
this.currentKeyTime = newResult.keyTime;
this.currentCertTime = newResult.certTime;
}
} catch (CertificateException | IOException | NoSuchAlgorithmException
| InvalidKeySpecException e) {
log.log(Level.SEVERE, "Failed refreshing private key and certificate chain from files. "
+ "Using previous ones", e);
}
}
}

private static class UpdateResult {
boolean success;
long keyTime;
long certTime;

public UpdateResult(boolean success, long keyTime, long certTime) {
this.success = success;
this.keyTime = keyTime;
this.certTime = certTime;
}
}

/**
* Reads the private key and certificates specified in the path locations. Updates {@code key} and
* {@code cert} if both of their modified time changed since last read.
*
* @param keyFile the file on disk holding the private key
* @param certFile the file on disk holding the certificate chain
* @param oldKeyTime the time when the private key file is modified during last execution
* @param oldCertTime the time when the certificate chain file is modified during last execution
* @return the result of this update execution
*/
private UpdateResult readAndUpdate(File keyFile, File certFile, long oldKeyTime, long oldCertTime)
throws IOException, CertificateException, NoSuchAlgorithmException, InvalidKeySpecException {
long newKeyTime = keyFile.lastModified();
long newCertTime = certFile.lastModified();
// We only update when both the key and the certs are updated.
if (newKeyTime != oldKeyTime && newCertTime != oldCertTime) {
FileInputStream keyInputStream = new FileInputStream(keyFile);
try {
PrivateKey key = CertificateUtils.getPrivateKey(keyInputStream);
FileInputStream certInputStream = new FileInputStream(certFile);
try {
X509Certificate[] certs = CertificateUtils.getX509Certificates(certInputStream);
updateIdentityCredentials(key, certs);
return new UpdateResult(true, newKeyTime, newCertTime);
} finally {
certInputStream.close();
}
} finally {
keyInputStream.close();
}
}
return new UpdateResult(false, oldKeyTime, oldCertTime);
}

/**
* Mainly used to avoid throwing IO Exceptions in java.io.Closeable.
*/
public interface Closeable extends java.io.Closeable {
@Override public void close();
}
}

0 comments on commit a795d3c

Please sign in to comment.