Respect certificate validity period (#391)

Client SSL handler already performs the necessary validation. Only tests are
added.

Server SSL handler does not currently check for the validity period of
the client certificate as the insecure trust manager is used. This PR
added the check but does not actually terminate the connection yet. It
will log the expired certificates so that we can contact the registrars
to update them.

Once we are certain that all certificates are updated, we can turn off
dryrun mode.

We should also consider checking if the certificate has too long a
validity period as it defeats the purpose of using regularly updated
certificates to deprecate insecure cipher suites.
This commit is contained in:
Lai Jiang 2019-11-27 16:08:38 -05:00 committed by GitHub
parent 9bb6b598a3
commit 1c1ccee75e
17 changed files with 473 additions and 35 deletions

View file

@ -24,6 +24,7 @@ dependencies {
compile deps['io.netty:netty-handler'] compile deps['io.netty:netty-handler']
compile deps['io.netty:netty-transport'] compile deps['io.netty:netty-transport']
compile deps['javax.inject:javax.inject'] compile deps['javax.inject:javax.inject']
compile project(':util')
runtime deps['com.google.flogger:flogger-system-backend'] runtime deps['com.google.flogger:flogger-system-backend']
runtime deps['io.netty:netty-tcnative-boringssl-static'] runtime deps['io.netty:netty-tcnative-boringssl-static']

View file

@ -1,13 +1,30 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.fasterxml.jackson.core:jackson-core:2.9.9
com.google.api-client:google-api-client:1.29.2
com.google.appengine:appengine-api-1.0-sdk:1.9.48
com.google.appengine:appengine-testing:1.9.58
com.google.auth:google-auth-library-credentials:0.16.1
com.google.auth:google-auth-library-oauth2-http:0.16.1
com.google.auto.value:auto-value-annotations:1.6.3
com.google.auto.value:auto-value:1.6.3
com.google.code.findbugs:jsr305:3.0.2 com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger:2.21
com.google.errorprone:error_prone_annotations:2.3.2 com.google.errorprone:error_prone_annotations:2.3.2
com.google.flogger:flogger:0.1 com.google.flogger:flogger:0.1
com.google.guava:failureaccess:1.0.1 com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client:1.30.1
com.google.j2objc:j2objc-annotations:1.3 com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.29.2
com.google.re2j:re2j:1.1
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
commons-logging:commons-logging:1.2
io.grpc:grpc-context:1.19.0
io.netty:netty-buffer:4.1.31.Final io.netty:netty-buffer:4.1.31.Final
io.netty:netty-codec-http:4.1.31.Final io.netty:netty-codec-http:4.1.31.Final
io.netty:netty-codec:4.1.31.Final io.netty:netty-codec:4.1.31.Final
@ -15,6 +32,15 @@ io.netty:netty-common:4.1.31.Final
io.netty:netty-handler:4.1.31.Final io.netty:netty-handler:4.1.31.Final
io.netty:netty-resolver:4.1.31.Final io.netty:netty-resolver:4.1.31.Final
io.netty:netty-transport:4.1.31.Final io.netty:netty-transport:4.1.31.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-contrib-http-util:0.21.0
javax.activation:activation:1.1
javax.inject:javax.inject:1 javax.inject:javax.inject:1
javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.checkerframework:checker-qual:2.8.1 org.checkerframework:checker-qual:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18 org.codehaus.mojo:animal-sniffer-annotations:1.18
org.yaml:snakeyaml:1.17

View file

@ -1,13 +1,30 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.fasterxml.jackson.core:jackson-core:2.9.9
com.google.api-client:google-api-client:1.29.2
com.google.appengine:appengine-api-1.0-sdk:1.9.48
com.google.appengine:appengine-testing:1.9.58
com.google.auth:google-auth-library-credentials:0.16.1
com.google.auth:google-auth-library-oauth2-http:0.16.1
com.google.auto.value:auto-value-annotations:1.6.3
com.google.auto.value:auto-value:1.6.3
com.google.code.findbugs:jsr305:3.0.2 com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger:2.21
com.google.errorprone:error_prone_annotations:2.3.2 com.google.errorprone:error_prone_annotations:2.3.2
com.google.flogger:flogger:0.1 com.google.flogger:flogger:0.1
com.google.guava:failureaccess:1.0.1 com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client:1.30.1
com.google.j2objc:j2objc-annotations:1.3 com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.29.2
com.google.re2j:re2j:1.1
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
commons-logging:commons-logging:1.2
io.grpc:grpc-context:1.19.0
io.netty:netty-buffer:4.1.31.Final io.netty:netty-buffer:4.1.31.Final
io.netty:netty-codec-http:4.1.31.Final io.netty:netty-codec-http:4.1.31.Final
io.netty:netty-codec:4.1.31.Final io.netty:netty-codec:4.1.31.Final
@ -15,6 +32,15 @@ io.netty:netty-common:4.1.31.Final
io.netty:netty-handler:4.1.31.Final io.netty:netty-handler:4.1.31.Final
io.netty:netty-resolver:4.1.31.Final io.netty:netty-resolver:4.1.31.Final
io.netty:netty-transport:4.1.31.Final io.netty:netty-transport:4.1.31.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-contrib-http-util:0.21.0
javax.activation:activation:1.1
javax.inject:javax.inject:1 javax.inject:javax.inject:1
javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.checkerframework:checker-qual:2.8.1 org.checkerframework:checker-qual:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18 org.codehaus.mojo:animal-sniffer-annotations:1.18
org.yaml:snakeyaml:1.17

View file

@ -1,14 +1,31 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.fasterxml.jackson.core:jackson-core:2.9.9
com.google.api-client:google-api-client:1.29.2
com.google.appengine:appengine-api-1.0-sdk:1.9.48
com.google.appengine:appengine-testing:1.9.58
com.google.auth:google-auth-library-credentials:0.16.1
com.google.auth:google-auth-library-oauth2-http:0.16.1
com.google.auto.value:auto-value-annotations:1.6.3
com.google.auto.value:auto-value:1.6.3
com.google.code.findbugs:jsr305:3.0.2 com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger:2.21
com.google.errorprone:error_prone_annotations:2.3.2 com.google.errorprone:error_prone_annotations:2.3.2
com.google.flogger:flogger-system-backend:0.1 com.google.flogger:flogger-system-backend:0.1
com.google.flogger:flogger:0.1 com.google.flogger:flogger:0.1
com.google.guava:failureaccess:1.0.1 com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client:1.30.1
com.google.j2objc:j2objc-annotations:1.3 com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.29.2
com.google.re2j:re2j:1.1
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
commons-logging:commons-logging:1.2
io.grpc:grpc-context:1.19.0
io.netty:netty-buffer:4.1.31.Final io.netty:netty-buffer:4.1.31.Final
io.netty:netty-codec-http:4.1.31.Final io.netty:netty-codec-http:4.1.31.Final
io.netty:netty-codec:4.1.31.Final io.netty:netty-codec:4.1.31.Final
@ -17,6 +34,15 @@ io.netty:netty-handler:4.1.31.Final
io.netty:netty-resolver:4.1.31.Final io.netty:netty-resolver:4.1.31.Final
io.netty:netty-tcnative-boringssl-static:2.0.22.Final io.netty:netty-tcnative-boringssl-static:2.0.22.Final
io.netty:netty-transport:4.1.31.Final io.netty:netty-transport:4.1.31.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-contrib-http-util:0.21.0
javax.activation:activation:1.1
javax.inject:javax.inject:1 javax.inject:javax.inject:1
javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.checkerframework:checker-qual:2.8.1 org.checkerframework:checker-qual:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18 org.codehaus.mojo:animal-sniffer-annotations:1.18
org.yaml:snakeyaml:1.17

View file

@ -1,14 +1,31 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.fasterxml.jackson.core:jackson-core:2.9.9
com.google.api-client:google-api-client:1.29.2
com.google.appengine:appengine-api-1.0-sdk:1.9.48
com.google.appengine:appengine-testing:1.9.58
com.google.auth:google-auth-library-credentials:0.16.1
com.google.auth:google-auth-library-oauth2-http:0.16.1
com.google.auto.value:auto-value-annotations:1.6.3
com.google.auto.value:auto-value:1.6.3
com.google.code.findbugs:jsr305:3.0.2 com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger:2.21
com.google.errorprone:error_prone_annotations:2.3.2 com.google.errorprone:error_prone_annotations:2.3.2
com.google.flogger:flogger-system-backend:0.1 com.google.flogger:flogger-system-backend:0.1
com.google.flogger:flogger:0.1 com.google.flogger:flogger:0.1
com.google.guava:failureaccess:1.0.1 com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client:1.30.1
com.google.j2objc:j2objc-annotations:1.3 com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.29.2
com.google.re2j:re2j:1.1
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
commons-logging:commons-logging:1.2
io.grpc:grpc-context:1.19.0
io.netty:netty-buffer:4.1.31.Final io.netty:netty-buffer:4.1.31.Final
io.netty:netty-codec-http:4.1.31.Final io.netty:netty-codec-http:4.1.31.Final
io.netty:netty-codec:4.1.31.Final io.netty:netty-codec:4.1.31.Final
@ -17,6 +34,15 @@ io.netty:netty-handler:4.1.31.Final
io.netty:netty-resolver:4.1.31.Final io.netty:netty-resolver:4.1.31.Final
io.netty:netty-tcnative-boringssl-static:2.0.22.Final io.netty:netty-tcnative-boringssl-static:2.0.22.Final
io.netty:netty-transport:4.1.31.Final io.netty:netty-transport:4.1.31.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-contrib-http-util:0.21.0
javax.activation:activation:1.1
javax.inject:javax.inject:1 javax.inject:javax.inject:1
javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.checkerframework:checker-qual:2.8.1 org.checkerframework:checker-qual:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18 org.codehaus.mojo:animal-sniffer-annotations:1.18
org.yaml:snakeyaml:1.17

View file

@ -1,14 +1,31 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.fasterxml.jackson.core:jackson-core:2.9.9
com.google.api-client:google-api-client:1.29.2
com.google.appengine:appengine-api-1.0-sdk:1.9.48
com.google.appengine:appengine-testing:1.9.58
com.google.auth:google-auth-library-credentials:0.16.1
com.google.auth:google-auth-library-oauth2-http:0.16.1
com.google.auto.value:auto-value-annotations:1.6.3
com.google.auto.value:auto-value:1.6.3
com.google.code.findbugs:jsr305:3.0.2 com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger:2.21
com.google.errorprone:error_prone_annotations:2.3.2 com.google.errorprone:error_prone_annotations:2.3.2
com.google.flogger:flogger-system-backend:0.1 com.google.flogger:flogger-system-backend:0.1
com.google.flogger:flogger:0.1 com.google.flogger:flogger:0.1
com.google.guava:failureaccess:1.0.1 com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client:1.30.1
com.google.j2objc:j2objc-annotations:1.3 com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.29.2
com.google.re2j:re2j:1.1
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
commons-logging:commons-logging:1.2
io.grpc:grpc-context:1.19.0
io.netty:netty-buffer:4.1.31.Final io.netty:netty-buffer:4.1.31.Final
io.netty:netty-codec-http:4.1.31.Final io.netty:netty-codec-http:4.1.31.Final
io.netty:netty-codec:4.1.31.Final io.netty:netty-codec:4.1.31.Final
@ -17,6 +34,15 @@ io.netty:netty-handler:4.1.31.Final
io.netty:netty-resolver:4.1.31.Final io.netty:netty-resolver:4.1.31.Final
io.netty:netty-tcnative-boringssl-static:2.0.22.Final io.netty:netty-tcnative-boringssl-static:2.0.22.Final
io.netty:netty-transport:4.1.31.Final io.netty:netty-transport:4.1.31.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-contrib-http-util:0.21.0
javax.activation:activation:1.1
javax.inject:javax.inject:1 javax.inject:javax.inject:1
javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.checkerframework:checker-qual:2.8.1 org.checkerframework:checker-qual:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18 org.codehaus.mojo:animal-sniffer-annotations:1.18
org.yaml:snakeyaml:1.17

View file

@ -1,16 +1,32 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.fasterxml.jackson.core:jackson-core:2.9.9
com.google.api-client:google-api-client:1.29.2
com.google.appengine:appengine-api-1.0-sdk:1.9.48
com.google.appengine:appengine-testing:1.9.58
com.google.auth:google-auth-library-credentials:0.16.1
com.google.auth:google-auth-library-oauth2-http:0.16.1
com.google.auto.value:auto-value-annotations:1.6.3 com.google.auto.value:auto-value-annotations:1.6.3
com.google.auto.value:auto-value:1.6.3
com.google.code.findbugs:jsr305:3.0.2 com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger:2.21
com.google.errorprone:error_prone_annotations:2.3.2 com.google.errorprone:error_prone_annotations:2.3.2
com.google.flogger:flogger:0.1 com.google.flogger:flogger:0.1
com.google.guava:failureaccess:1.0.1 com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client:1.30.1
com.google.j2objc:j2objc-annotations:1.3 com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.29.2
com.google.re2j:re2j:1.1
com.google.truth:truth:1.0 com.google.truth:truth:1.0
com.googlecode.java-diff-utils:diffutils:1.3.0 com.googlecode.java-diff-utils:diffutils:1.3.0
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
commons-logging:commons-logging:1.2
io.grpc:grpc-context:1.19.0
io.netty:netty-buffer:4.1.31.Final io.netty:netty-buffer:4.1.31.Final
io.netty:netty-codec-http:4.1.31.Final io.netty:netty-codec-http:4.1.31.Final
io.netty:netty-codec:4.1.31.Final io.netty:netty-codec:4.1.31.Final
@ -18,11 +34,20 @@ io.netty:netty-common:4.1.31.Final
io.netty:netty-handler:4.1.31.Final io.netty:netty-handler:4.1.31.Final
io.netty:netty-resolver:4.1.31.Final io.netty:netty-resolver:4.1.31.Final
io.netty:netty-transport:4.1.31.Final io.netty:netty-transport:4.1.31.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-contrib-http-util:0.21.0
javax.activation:activation:1.1
javax.inject:javax.inject:1 javax.inject:javax.inject:1
javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
junit:junit:4.12 junit:junit:4.12
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.bouncycastle:bcpkix-jdk15on:1.61 org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61 org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5 org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.8.1 org.checkerframework:checker-qual:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18 org.codehaus.mojo:animal-sniffer-annotations:1.18
org.hamcrest:hamcrest-core:1.3 org.hamcrest:hamcrest-core:1.3
org.yaml:snakeyaml:1.17

View file

@ -1,16 +1,32 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.fasterxml.jackson.core:jackson-core:2.9.9
com.google.api-client:google-api-client:1.29.2
com.google.appengine:appengine-api-1.0-sdk:1.9.48
com.google.appengine:appengine-testing:1.9.58
com.google.auth:google-auth-library-credentials:0.16.1
com.google.auth:google-auth-library-oauth2-http:0.16.1
com.google.auto.value:auto-value-annotations:1.6.3 com.google.auto.value:auto-value-annotations:1.6.3
com.google.auto.value:auto-value:1.6.3
com.google.code.findbugs:jsr305:3.0.2 com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger:2.21
com.google.errorprone:error_prone_annotations:2.3.2 com.google.errorprone:error_prone_annotations:2.3.2
com.google.flogger:flogger:0.1 com.google.flogger:flogger:0.1
com.google.guava:failureaccess:1.0.1 com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client:1.30.1
com.google.j2objc:j2objc-annotations:1.3 com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.29.2
com.google.re2j:re2j:1.1
com.google.truth:truth:1.0 com.google.truth:truth:1.0
com.googlecode.java-diff-utils:diffutils:1.3.0 com.googlecode.java-diff-utils:diffutils:1.3.0
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
commons-logging:commons-logging:1.2
io.grpc:grpc-context:1.19.0
io.netty:netty-buffer:4.1.31.Final io.netty:netty-buffer:4.1.31.Final
io.netty:netty-codec-http:4.1.31.Final io.netty:netty-codec-http:4.1.31.Final
io.netty:netty-codec:4.1.31.Final io.netty:netty-codec:4.1.31.Final
@ -18,11 +34,20 @@ io.netty:netty-common:4.1.31.Final
io.netty:netty-handler:4.1.31.Final io.netty:netty-handler:4.1.31.Final
io.netty:netty-resolver:4.1.31.Final io.netty:netty-resolver:4.1.31.Final
io.netty:netty-transport:4.1.31.Final io.netty:netty-transport:4.1.31.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-contrib-http-util:0.21.0
javax.activation:activation:1.1
javax.inject:javax.inject:1 javax.inject:javax.inject:1
javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
junit:junit:4.12 junit:junit:4.12
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.bouncycastle:bcpkix-jdk15on:1.61 org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61 org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5 org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.8.1 org.checkerframework:checker-qual:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18 org.codehaus.mojo:animal-sniffer-annotations:1.18
org.hamcrest:hamcrest-core:1.3 org.hamcrest:hamcrest-core:1.3
org.yaml:snakeyaml:1.17

View file

@ -1,17 +1,33 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.fasterxml.jackson.core:jackson-core:2.9.9
com.google.api-client:google-api-client:1.29.2
com.google.appengine:appengine-api-1.0-sdk:1.9.48
com.google.appengine:appengine-testing:1.9.58
com.google.auth:google-auth-library-credentials:0.16.1
com.google.auth:google-auth-library-oauth2-http:0.16.1
com.google.auto.value:auto-value-annotations:1.6.3 com.google.auto.value:auto-value-annotations:1.6.3
com.google.auto.value:auto-value:1.6.3
com.google.code.findbugs:jsr305:3.0.2 com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger:2.21
com.google.errorprone:error_prone_annotations:2.3.2 com.google.errorprone:error_prone_annotations:2.3.2
com.google.flogger:flogger-system-backend:0.1 com.google.flogger:flogger-system-backend:0.1
com.google.flogger:flogger:0.1 com.google.flogger:flogger:0.1
com.google.guava:failureaccess:1.0.1 com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client:1.30.1
com.google.j2objc:j2objc-annotations:1.3 com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.29.2
com.google.re2j:re2j:1.1
com.google.truth:truth:1.0 com.google.truth:truth:1.0
com.googlecode.java-diff-utils:diffutils:1.3.0 com.googlecode.java-diff-utils:diffutils:1.3.0
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
commons-logging:commons-logging:1.2
io.grpc:grpc-context:1.19.0
io.netty:netty-buffer:4.1.31.Final io.netty:netty-buffer:4.1.31.Final
io.netty:netty-codec-http:4.1.31.Final io.netty:netty-codec-http:4.1.31.Final
io.netty:netty-codec:4.1.31.Final io.netty:netty-codec:4.1.31.Final
@ -20,11 +36,20 @@ io.netty:netty-handler:4.1.31.Final
io.netty:netty-resolver:4.1.31.Final io.netty:netty-resolver:4.1.31.Final
io.netty:netty-tcnative-boringssl-static:2.0.22.Final io.netty:netty-tcnative-boringssl-static:2.0.22.Final
io.netty:netty-transport:4.1.31.Final io.netty:netty-transport:4.1.31.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-contrib-http-util:0.21.0
javax.activation:activation:1.1
javax.inject:javax.inject:1 javax.inject:javax.inject:1
javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
junit:junit:4.12 junit:junit:4.12
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.bouncycastle:bcpkix-jdk15on:1.61 org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61 org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5 org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.8.1 org.checkerframework:checker-qual:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18 org.codehaus.mojo:animal-sniffer-annotations:1.18
org.hamcrest:hamcrest-core:1.3 org.hamcrest:hamcrest-core:1.3
org.yaml:snakeyaml:1.17

View file

@ -1,17 +1,33 @@
# This is a Gradle generated file for dependency locking. # This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised. # Manual edits can break the build and are not advised.
# This file is expected to be part of source control. # This file is expected to be part of source control.
com.fasterxml.jackson.core:jackson-core:2.9.9
com.google.api-client:google-api-client:1.29.2
com.google.appengine:appengine-api-1.0-sdk:1.9.48
com.google.appengine:appengine-testing:1.9.58
com.google.auth:google-auth-library-credentials:0.16.1
com.google.auth:google-auth-library-oauth2-http:0.16.1
com.google.auto.value:auto-value-annotations:1.6.3 com.google.auto.value:auto-value-annotations:1.6.3
com.google.auto.value:auto-value:1.6.3
com.google.code.findbugs:jsr305:3.0.2 com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger:2.21
com.google.errorprone:error_prone_annotations:2.3.2 com.google.errorprone:error_prone_annotations:2.3.2
com.google.flogger:flogger-system-backend:0.1 com.google.flogger:flogger-system-backend:0.1
com.google.flogger:flogger:0.1 com.google.flogger:flogger:0.1
com.google.guava:failureaccess:1.0.1 com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client:1.30.1
com.google.j2objc:j2objc-annotations:1.3 com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.29.2
com.google.re2j:re2j:1.1
com.google.truth:truth:1.0 com.google.truth:truth:1.0
com.googlecode.java-diff-utils:diffutils:1.3.0 com.googlecode.java-diff-utils:diffutils:1.3.0
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
commons-logging:commons-logging:1.2
io.grpc:grpc-context:1.19.0
io.netty:netty-buffer:4.1.31.Final io.netty:netty-buffer:4.1.31.Final
io.netty:netty-codec-http:4.1.31.Final io.netty:netty-codec-http:4.1.31.Final
io.netty:netty-codec:4.1.31.Final io.netty:netty-codec:4.1.31.Final
@ -20,11 +36,20 @@ io.netty:netty-handler:4.1.31.Final
io.netty:netty-resolver:4.1.31.Final io.netty:netty-resolver:4.1.31.Final
io.netty:netty-tcnative-boringssl-static:2.0.22.Final io.netty:netty-tcnative-boringssl-static:2.0.22.Final
io.netty:netty-transport:4.1.31.Final io.netty:netty-transport:4.1.31.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-contrib-http-util:0.21.0
javax.activation:activation:1.1
javax.inject:javax.inject:1 javax.inject:javax.inject:1
javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
junit:junit:4.12 junit:junit:4.12
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.bouncycastle:bcpkix-jdk15on:1.61 org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61 org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5 org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.8.1 org.checkerframework:checker-qual:2.8.1
org.codehaus.mojo:animal-sniffer-annotations:1.18 org.codehaus.mojo:animal-sniffer-annotations:1.18
org.hamcrest:hamcrest-core:1.3 org.hamcrest:hamcrest-core:1.3
org.yaml:snakeyaml:1.17

View file

@ -14,9 +14,13 @@
package google.registry.networking.handler; package google.registry.networking.handler;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.util.X509Utils.getCertificateHash;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.embedded.EmbeddedChannel;
@ -29,6 +33,8 @@ import io.netty.util.AttributeKey;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.Promise;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -58,6 +64,8 @@ public class SslServerInitializer<C extends Channel> extends ChannelInitializer<
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final boolean requireClientCert; private final boolean requireClientCert;
// TODO(jianglai): Always validate client certs (if required).
private final boolean validateClientCert;
private final SslProvider sslProvider; private final SslProvider sslProvider;
// We use suppliers for the key/cert pair because they are fetched and cached from GCS, and can // We use suppliers for the key/cert pair because they are fetched and cached from GCS, and can
// change when the artifacts on GCS changes. // change when the artifacts on GCS changes.
@ -66,11 +74,16 @@ public class SslServerInitializer<C extends Channel> extends ChannelInitializer<
public SslServerInitializer( public SslServerInitializer(
boolean requireClientCert, boolean requireClientCert,
boolean validateClientCert,
SslProvider sslProvider, SslProvider sslProvider,
Supplier<PrivateKey> privateKeySupplier, Supplier<PrivateKey> privateKeySupplier,
Supplier<ImmutableList<X509Certificate>> certificatesSupplier) { Supplier<ImmutableList<X509Certificate>> certificatesSupplier) {
logger.atInfo().log("Server SSL Provider: %s", sslProvider); logger.atInfo().log("Server SSL Provider: %s", sslProvider);
checkArgument(
requireClientCert || !validateClientCert,
"Cannot validate client certificate if client certificate is not required.");
this.requireClientCert = requireClientCert; this.requireClientCert = requireClientCert;
this.validateClientCert = validateClientCert;
this.sslProvider = sslProvider; this.sslProvider = sslProvider;
this.privateKeySupplier = privateKeySupplier; this.privateKeySupplier = privateKeySupplier;
this.certificatesSupplier = certificatesSupplier; this.certificatesSupplier = certificatesSupplier;
@ -95,10 +108,23 @@ public class SslServerInitializer<C extends Channel> extends ChannelInitializer<
.addListener( .addListener(
future -> { future -> {
if (future.isSuccess()) { if (future.isSuccess()) {
Promise<X509Certificate> unusedPromise = X509Certificate clientCertificate =
clientCertificatePromise.setSuccess( (X509Certificate)
(X509Certificate) sslHandler.engine().getSession().getPeerCertificates()[0];
sslHandler.engine().getSession().getPeerCertificates()[0]); try {
clientCertificate.checkValidity();
Promise<X509Certificate> unusedPromise =
clientCertificatePromise.setSuccess(clientCertificate);
} catch (CertificateNotYetValidException | CertificateExpiredException e) {
logger.atWarning().withCause(e).log(
"Client certificate is not valid.\nHash: %s",
getCertificateHash(clientCertificate));
if (validateClientCert) {
Promise<X509Certificate> unusedPromise =
clientCertificatePromise.setFailure(e);
ChannelFuture unusedFuture2 = channel.close();
}
}
} else { } else {
Promise<X509Certificate> unusedPromise = Promise<X509Certificate> unusedPromise =
clientCertificatePromise.setFailure(future.cause()); clientCertificatePromise.setFailure(future.cause());

View file

@ -61,7 +61,9 @@ public final class NettyRule extends ExternalResource {
// Handler attached to client's channel to record the response received. // Handler attached to client's channel to record the response received.
private DumpHandler dumpHandler; private DumpHandler dumpHandler;
private Channel channel; private Channel clientChannel;
private Channel serverChannel;
public EventLoopGroup getEventLoopGroup() { public EventLoopGroup getEventLoopGroup() {
return eventLoopGroup; return eventLoopGroup;
@ -79,6 +81,7 @@ public final class NettyRule extends ExternalResource {
ch.pipeline().addLast(handlers); ch.pipeline().addLast(handlers);
// Add the "echoHandler" last to log the incoming message and send it back // Add the "echoHandler" last to log the incoming message and send it back
ch.pipeline().addLast(echoHandler); ch.pipeline().addLast(echoHandler);
serverChannel = ch;
} }
}; };
ServerBootstrap sb = ServerBootstrap sb =
@ -109,11 +112,11 @@ public final class NettyRule extends ExternalResource {
.group(eventLoopGroup) .group(eventLoopGroup)
.channel(LocalChannel.class) .channel(LocalChannel.class)
.handler(clientInitializer); .handler(clientInitializer);
channel = b.connect(localAddress).syncUninterruptibly().channel(); clientChannel = b.connect(localAddress).syncUninterruptibly().channel();
} }
void checkReady() { void checkReady() {
checkState(channel != null, "Must call setUpClient to finish NettyRule setup"); checkState(clientChannel != null, "Must call setUpClient to finish NettyRule setup");
} }
/** /**
@ -125,16 +128,21 @@ public final class NettyRule extends ExternalResource {
*/ */
void assertThatMessagesWork() throws Exception { void assertThatMessagesWork() throws Exception {
checkReady(); checkReady();
assertThat(channel.isActive()).isTrue(); assertThat(clientChannel.isActive()).isTrue();
writeToChannelAndFlush(channel, "Hello, world!"); writeToChannelAndFlush(clientChannel, "Hello, world!");
assertThat(echoHandler.getRequestFuture().get()).isEqualTo("Hello, world!"); assertThat(echoHandler.getRequestFuture().get()).isEqualTo("Hello, world!");
assertThat(dumpHandler.getResponseFuture().get()).isEqualTo("Hello, world!"); assertThat(dumpHandler.getResponseFuture().get()).isEqualTo("Hello, world!");
} }
Channel getChannel() { Channel getClientChannel() {
checkReady(); checkReady();
return channel; return clientChannel;
}
Channel getServerChannel() {
checkReady();
return serverChannel;
} }
ThrowableSubject assertThatServerRootCause() { ThrowableSubject assertThatServerRootCause() {

View file

@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.networking.handler.SslInitializerTestUtils.getKeyPair; import static google.registry.networking.handler.SslInitializerTestUtils.getKeyPair;
import static google.registry.networking.handler.SslInitializerTestUtils.setUpSslChannel; import static google.registry.networking.handler.SslInitializerTestUtils.setUpSslChannel;
import static google.registry.networking.handler.SslInitializerTestUtils.signKeyPair; import static google.registry.networking.handler.SslInitializerTestUtils.signKeyPair;
import static google.registry.networking.handler.SslInitializerTestUtils.verifySslExcpetion;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import io.netty.channel.Channel; import io.netty.channel.Channel;
@ -39,7 +40,12 @@ import java.security.KeyPair;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.cert.CertPathBuilderException; import java.security.cert.CertPathBuilderException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.function.Function; import java.util.function.Function;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSession;
@ -159,7 +165,7 @@ public class SslClientInitializerTest {
// exceptions. // exceptions.
nettyRule.assertThatClientRootCause().isInstanceOf(CertPathBuilderException.class); nettyRule.assertThatClientRootCause().isInstanceOf(CertPathBuilderException.class);
nettyRule.assertThatServerRootCause().isInstanceOf(SSLException.class); nettyRule.assertThatServerRootCause().isInstanceOf(SSLException.class);
assertThat(nettyRule.getChannel().isActive()).isFalse(); assertThat(nettyRule.getClientChannel().isActive()).isFalse();
} }
@Test @Test
@ -184,13 +190,81 @@ public class SslClientInitializerTest {
sslProvider, hostProvider, portProvider, ImmutableList.of(ssc.cert()), null, null); sslProvider, hostProvider, portProvider, ImmutableList.of(ssc.cert()), null, null);
nettyRule.setUpClient(localAddress, sslClientInitializer); nettyRule.setUpClient(localAddress, sslClientInitializer);
setUpSslChannel(nettyRule.getChannel(), cert); setUpSslChannel(nettyRule.getClientChannel(), cert);
nettyRule.assertThatMessagesWork(); nettyRule.assertThatMessagesWork();
// Verify that the SNI extension is sent during handshake. // Verify that the SNI extension is sent during handshake.
assertThat(sniHostReceived).isEqualTo(SSL_HOST); assertThat(sniHostReceived).isEqualTo(SSL_HOST);
} }
@Test
public void testFailure_customTrustManager_serverCertExpired() throws Exception {
LocalAddress localAddress =
new LocalAddress("CUSTOM_TRUST_MANAGER_SERVE_CERT_EXPIRED_" + sslProvider);
// Generate a new key pair.
KeyPair keyPair = getKeyPair();
// Generate a self signed certificate, and use it to sign the key pair.
SelfSignedCertificate ssc = new SelfSignedCertificate();
X509Certificate cert =
signKeyPair(
ssc,
keyPair,
SSL_HOST,
Date.from(Instant.now().minus(Duration.ofDays(2))),
Date.from(Instant.now().minus(Duration.ofDays(1))));
// Set up the server to use the signed cert and private key to perform handshake;
PrivateKey privateKey = keyPair.getPrivate();
nettyRule.setUpServer(localAddress, getServerHandler(false, privateKey, cert));
// Set up the client to trust the self signed cert used to sign the cert that server provides.
SslClientInitializer<LocalChannel> sslClientInitializer =
new SslClientInitializer<>(
sslProvider, hostProvider, portProvider, ImmutableList.of(ssc.cert()), null, null);
nettyRule.setUpClient(localAddress, sslClientInitializer);
verifySslExcpetion(
nettyRule.getClientChannel(),
channel -> channel.pipeline().get(SslHandler.class).handshakeFuture().get(),
CertificateExpiredException.class);
}
@Test
public void testFailure_customTrustManager_serverCertNotYetValid() throws Exception {
LocalAddress localAddress =
new LocalAddress("CUSTOM_TRUST_MANAGER_SERVE_CERT_NOT_YET_VALID_" + sslProvider);
// Generate a new key pair.
KeyPair keyPair = getKeyPair();
// Generate a self signed certificate, and use it to sign the key pair.
SelfSignedCertificate ssc = new SelfSignedCertificate();
X509Certificate cert =
signKeyPair(
ssc,
keyPair,
SSL_HOST,
Date.from(Instant.now().plus(Duration.ofDays(1))),
Date.from(Instant.now().plus(Duration.ofDays(2))));
// Set up the server to use the signed cert and private key to perform handshake;
PrivateKey privateKey = keyPair.getPrivate();
nettyRule.setUpServer(localAddress, getServerHandler(false, privateKey, cert));
// Set up the client to trust the self signed cert used to sign the cert that server provides.
SslClientInitializer<LocalChannel> sslClientInitializer =
new SslClientInitializer<>(
sslProvider, hostProvider, portProvider, ImmutableList.of(ssc.cert()), null, null);
nettyRule.setUpClient(localAddress, sslClientInitializer);
verifySslExcpetion(
nettyRule.getClientChannel(),
channel -> channel.pipeline().get(SslHandler.class).handshakeFuture().get(),
CertificateNotYetValidException.class);
}
@Test @Test
public void testSuccess_customTrustManager_acceptSelfSignedCert_clientCertRequired() public void testSuccess_customTrustManager_acceptSelfSignedCert_clientCertRequired()
throws Exception { throws Exception {
@ -215,7 +289,7 @@ public class SslClientInitializerTest {
() -> ImmutableList.of(clientSsc.cert())); () -> ImmutableList.of(clientSsc.cert()));
nettyRule.setUpClient(localAddress, sslClientInitializer); nettyRule.setUpClient(localAddress, sslClientInitializer);
SSLSession sslSession = setUpSslChannel(nettyRule.getChannel(), serverSsc.cert()); SSLSession sslSession = setUpSslChannel(nettyRule.getClientChannel(), serverSsc.cert());
nettyRule.assertThatMessagesWork(); nettyRule.assertThatMessagesWork();
// Verify that the SNI extension is sent during handshake. // Verify that the SNI extension is sent during handshake.
@ -255,6 +329,6 @@ public class SslClientInitializerTest {
nettyRule.assertThatClientRootCause().isInstanceOf(CertificateException.class); nettyRule.assertThatClientRootCause().isInstanceOf(CertificateException.class);
nettyRule.assertThatClientRootCause().hasMessageThat().contains(SSL_HOST); nettyRule.assertThatClientRootCause().hasMessageThat().contains(SSL_HOST);
nettyRule.assertThatServerRootCause().isInstanceOf(SSLException.class); nettyRule.assertThatServerRootCause().isInstanceOf(SSLException.class);
assertThat(nettyRule.getChannel().isActive()).isFalse(); assertThat(nettyRule.getClientChannel().isActive()).isFalse();
} }
} }

View file

@ -15,8 +15,11 @@
package google.registry.networking.handler; package google.registry.networking.handler;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.JUnitBackports.assertThrows;
import com.google.common.base.Throwables;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.handler.ssl.SslHandler; import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.handler.ssl.util.SelfSignedCertificate;
import java.math.BigInteger; import java.math.BigInteger;
@ -28,6 +31,7 @@ import java.security.cert.X509Certificate;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.Date; import java.util.Date;
import java.util.concurrent.ExecutionException;
import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSession;
import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
@ -61,17 +65,17 @@ public final class SslInitializerTestUtils {
} }
/** /**
* Signs the given key pair with the given self signed certificate. * Signs the given key pair with the given self signed certificate to generate a certificate with
* the given validity range.
* *
* @return signed public key (of the key pair) certificate * @return signed public key (of the key pair) certificate
*/ */
public static X509Certificate signKeyPair( public static X509Certificate signKeyPair(
SelfSignedCertificate ssc, KeyPair keyPair, String hostname) throws Exception { SelfSignedCertificate ssc, KeyPair keyPair, String hostname, Date from, Date to)
throws Exception {
X500Name subjectDnName = new X500Name("CN=" + hostname); X500Name subjectDnName = new X500Name("CN=" + hostname);
BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis()); BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis());
X500Name issuerDnName = new X500Name(ssc.cert().getIssuerDN().getName()); X500Name issuerDnName = new X500Name(ssc.cert().getIssuerDN().getName());
Date from = Date.from(Instant.now().minus(Duration.ofDays(1)));
Date to = Date.from(Instant.now().plus(Duration.ofDays(1)));
SubjectPublicKeyInfo subPubKeyInfo = SubjectPublicKeyInfo subPubKeyInfo =
SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()); SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());
AlgorithmIdentifier sigAlgId = AlgorithmIdentifier sigAlgId =
@ -89,6 +93,22 @@ public final class SslInitializerTestUtils {
return new JcaX509CertificateConverter().setProvider("BC").getCertificate(certificateHolder); return new JcaX509CertificateConverter().setProvider("BC").getCertificate(certificateHolder);
} }
/**
* Signs the given key pair with the given self signed certificate to generate a certificate that
* is valid from yesterday to tomorrow.
*
* @return signed public key (of the key pair) certificate
*/
public static X509Certificate signKeyPair(
SelfSignedCertificate ssc, KeyPair keyPair, String hostname) throws Exception {
return signKeyPair(
ssc,
keyPair,
hostname,
Date.from(Instant.now().minus(Duration.ofDays(1))),
Date.from(Instant.now().plus(Duration.ofDays(1))));
}
/** /**
* Verifies tha the SSL channel is established as expected, and also sends a message to the server * Verifies tha the SSL channel is established as expected, and also sends a message to the server
* and verifies if it is echoed back correctly. * and verifies if it is echoed back correctly.
@ -110,4 +130,25 @@ public final class SslInitializerTestUtils {
// Returns the SSL session for further assertion. // Returns the SSL session for further assertion.
return sslHandler.engine().getSession(); return sslHandler.engine().getSession();
} }
/** Verifies tha the SSL channel cannot be established due to a given exception. */
static void verifySslExcpetion(
Channel channel, CheckedConsumer<Channel> operation, Class<? extends Exception> cause)
throws Exception {
// Extract SSL exception from the handshake future.
Exception exception = assertThrows(ExecutionException.class, () -> operation.accept(channel));
// Verify that the exception is caused by the expected cause.
assertThat(Throwables.getRootCause(exception)).isInstanceOf(cause);
// Ensure that the channel is closed. If this step times out, the channel is not properly
// closed.
ChannelFuture unusedFuture = channel.closeFuture().syncUninterruptibly();
}
/** A consumer that can throw checked exceptions. */
@FunctionalInterface
interface CheckedConsumer<T> {
void accept(T t) throws Exception;
}
} }

View file

@ -18,6 +18,8 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.networking.handler.SslInitializerTestUtils.getKeyPair; import static google.registry.networking.handler.SslInitializerTestUtils.getKeyPair;
import static google.registry.networking.handler.SslInitializerTestUtils.setUpSslChannel; import static google.registry.networking.handler.SslInitializerTestUtils.setUpSslChannel;
import static google.registry.networking.handler.SslInitializerTestUtils.signKeyPair; import static google.registry.networking.handler.SslInitializerTestUtils.signKeyPair;
import static google.registry.networking.handler.SslInitializerTestUtils.verifySslExcpetion;
import static google.registry.networking.handler.SslServerInitializer.CLIENT_CERTIFICATE_PROMISE_KEY;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@ -35,7 +37,12 @@ import io.netty.handler.ssl.util.SelfSignedCertificate;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLHandshakeException;
@ -82,18 +89,18 @@ public class SslServerInitializerTest {
} }
private ChannelHandler getServerHandler( private ChannelHandler getServerHandler(
boolean requireClientCert, PrivateKey privateKey, X509Certificate... certificates) { boolean requireClientCert,
boolean validateClientCert,
PrivateKey privateKey,
X509Certificate... certificates) {
return new SslServerInitializer<LocalChannel>( return new SslServerInitializer<LocalChannel>(
requireClientCert, requireClientCert,
validateClientCert,
sslProvider, sslProvider,
Suppliers.ofInstance(privateKey), Suppliers.ofInstance(privateKey),
Suppliers.ofInstance(ImmutableList.copyOf(certificates))); Suppliers.ofInstance(ImmutableList.copyOf(certificates)));
} }
private ChannelHandler getServerHandler(PrivateKey privateKey, X509Certificate... certificates) {
return getServerHandler(true, privateKey, certificates);
}
private ChannelHandler getClientHandler( private ChannelHandler getClientHandler(
X509Certificate trustedCertificate, PrivateKey privateKey, X509Certificate certificate) { X509Certificate trustedCertificate, PrivateKey privateKey, X509Certificate certificate) {
return new ChannelInitializer<LocalChannel>() { return new ChannelInitializer<LocalChannel>() {
@ -124,6 +131,7 @@ public class SslServerInitializerTest {
SslServerInitializer<EmbeddedChannel> sslServerInitializer = SslServerInitializer<EmbeddedChannel> sslServerInitializer =
new SslServerInitializer<>( new SslServerInitializer<>(
true, true,
false,
sslProvider, sslProvider,
Suppliers.ofInstance(ssc.key()), Suppliers.ofInstance(ssc.key()),
Suppliers.ofInstance(ImmutableList.of(ssc.cert()))); Suppliers.ofInstance(ImmutableList.of(ssc.cert())));
@ -142,12 +150,13 @@ public class SslServerInitializerTest {
SelfSignedCertificate serverSsc = new SelfSignedCertificate(SSL_HOST); SelfSignedCertificate serverSsc = new SelfSignedCertificate(SSL_HOST);
LocalAddress localAddress = new LocalAddress("TRUST_ANY_CLIENT_CERT_" + sslProvider); LocalAddress localAddress = new LocalAddress("TRUST_ANY_CLIENT_CERT_" + sslProvider);
nettyRule.setUpServer(localAddress, getServerHandler(serverSsc.key(), serverSsc.cert())); nettyRule.setUpServer(
localAddress, getServerHandler(true, false, serverSsc.key(), serverSsc.cert()));
SelfSignedCertificate clientSsc = new SelfSignedCertificate(); SelfSignedCertificate clientSsc = new SelfSignedCertificate();
nettyRule.setUpClient( nettyRule.setUpClient(
localAddress, getClientHandler(serverSsc.cert(), clientSsc.key(), clientSsc.cert())); localAddress, getClientHandler(serverSsc.cert(), clientSsc.key(), clientSsc.cert()));
SSLSession sslSession = setUpSslChannel(nettyRule.getChannel(), serverSsc.cert()); SSLSession sslSession = setUpSslChannel(nettyRule.getClientChannel(), serverSsc.cert());
nettyRule.assertThatMessagesWork(); nettyRule.assertThatMessagesWork();
// Verify that the SSL session gets the client cert. Note that this SslSession is for the client // Verify that the SSL session gets the client cert. Note that this SslSession is for the client
@ -157,15 +166,58 @@ public class SslServerInitializerTest {
assertThat(sslSession.getPeerCertificates()).asList().containsExactly(serverSsc.cert()); assertThat(sslSession.getPeerCertificates()).asList().containsExactly(serverSsc.cert());
} }
@Test
public void testFailure_clientCertExpired() throws Exception {
SelfSignedCertificate serverSsc = new SelfSignedCertificate(SSL_HOST);
LocalAddress localAddress = new LocalAddress("CLIENT_CERT_EXPIRED_" + sslProvider);
nettyRule.setUpServer(
localAddress, getServerHandler(true, true, serverSsc.key(), serverSsc.cert()));
SelfSignedCertificate clientSsc =
new SelfSignedCertificate(
"CLIENT",
Date.from(Instant.now().minus(Duration.ofDays(2))),
Date.from(Instant.now().minus(Duration.ofDays(1))));
nettyRule.setUpClient(
localAddress, getClientHandler(serverSsc.cert(), clientSsc.key(), clientSsc.cert()));
verifySslExcpetion(
nettyRule.getServerChannel(),
channel -> channel.attr(CLIENT_CERTIFICATE_PROMISE_KEY).get().get(),
CertificateExpiredException.class);
}
@Test
public void testFailure_clientCertNotYetValid() throws Exception {
SelfSignedCertificate serverSsc = new SelfSignedCertificate(SSL_HOST);
LocalAddress localAddress = new LocalAddress("CLIENT_CERT_EXPIRED_" + sslProvider);
nettyRule.setUpServer(
localAddress, getServerHandler(true, true, serverSsc.key(), serverSsc.cert()));
SelfSignedCertificate clientSsc =
new SelfSignedCertificate(
"CLIENT",
Date.from(Instant.now().plus(Duration.ofDays(1))),
Date.from(Instant.now().plus(Duration.ofDays(2))));
nettyRule.setUpClient(
localAddress, getClientHandler(serverSsc.cert(), clientSsc.key(), clientSsc.cert()));
verifySslExcpetion(
nettyRule.getServerChannel(),
channel -> channel.attr(CLIENT_CERTIFICATE_PROMISE_KEY).get().get(),
CertificateNotYetValidException.class);
}
@Test @Test
public void testSuccess_doesNotRequireClientCert() throws Exception { public void testSuccess_doesNotRequireClientCert() throws Exception {
SelfSignedCertificate serverSsc = new SelfSignedCertificate(SSL_HOST); SelfSignedCertificate serverSsc = new SelfSignedCertificate(SSL_HOST);
LocalAddress localAddress = new LocalAddress("DOES_NOT_REQUIRE_CLIENT_CERT_" + sslProvider); LocalAddress localAddress = new LocalAddress("DOES_NOT_REQUIRE_CLIENT_CERT_" + sslProvider);
nettyRule.setUpServer(localAddress, getServerHandler(false, serverSsc.key(), serverSsc.cert())); nettyRule.setUpServer(
localAddress, getServerHandler(false, false, serverSsc.key(), serverSsc.cert()));
nettyRule.setUpClient(localAddress, getClientHandler(serverSsc.cert(), null, null)); nettyRule.setUpClient(localAddress, getClientHandler(serverSsc.cert(), null, null));
SSLSession sslSession = setUpSslChannel(nettyRule.getChannel(), serverSsc.cert()); SSLSession sslSession = setUpSslChannel(nettyRule.getClientChannel(), serverSsc.cert());
nettyRule.assertThatMessagesWork(); nettyRule.assertThatMessagesWork();
// Verify that the SSL session does not contain any client cert. Note that this SslSession is // Verify that the SSL session does not contain any client cert. Note that this SslSession is
@ -186,6 +238,8 @@ public class SslServerInitializerTest {
nettyRule.setUpServer( nettyRule.setUpServer(
localAddress, localAddress,
getServerHandler( getServerHandler(
true,
false,
keyPair.getPrivate(), keyPair.getPrivate(),
// Serving both the server cert, and the CA cert // Serving both the server cert, and the CA cert
serverCert, serverCert,
@ -197,7 +251,7 @@ public class SslServerInitializerTest {
// Client trusts the CA cert // Client trusts the CA cert
caSsc.cert(), clientSsc.key(), clientSsc.cert())); caSsc.cert(), clientSsc.key(), clientSsc.cert()));
SSLSession sslSession = setUpSslChannel(nettyRule.getChannel(), serverCert, caSsc.cert()); SSLSession sslSession = setUpSslChannel(nettyRule.getClientChannel(), serverCert, caSsc.cert());
nettyRule.assertThatMessagesWork(); nettyRule.assertThatMessagesWork();
assertThat(sslSession.getLocalCertificates()).asList().containsExactly(clientSsc.cert()); assertThat(sslSession.getLocalCertificates()).asList().containsExactly(clientSsc.cert());
@ -212,7 +266,8 @@ public class SslServerInitializerTest {
SelfSignedCertificate serverSsc = new SelfSignedCertificate(SSL_HOST); SelfSignedCertificate serverSsc = new SelfSignedCertificate(SSL_HOST);
LocalAddress localAddress = new LocalAddress("REQUIRE_CLIENT_CERT_" + sslProvider); LocalAddress localAddress = new LocalAddress("REQUIRE_CLIENT_CERT_" + sslProvider);
nettyRule.setUpServer(localAddress, getServerHandler(serverSsc.key(), serverSsc.cert())); nettyRule.setUpServer(
localAddress, getServerHandler(true, false, serverSsc.key(), serverSsc.cert()));
nettyRule.setUpClient( nettyRule.setUpClient(
localAddress, localAddress,
getClientHandler( getClientHandler(
@ -225,7 +280,7 @@ public class SslServerInitializerTest {
// should throw exceptions. // should throw exceptions.
nettyRule.assertThatServerRootCause().isInstanceOf(SSLHandshakeException.class); nettyRule.assertThatServerRootCause().isInstanceOf(SSLHandshakeException.class);
nettyRule.assertThatClientRootCause().isInstanceOf(SSLException.class); nettyRule.assertThatClientRootCause().isInstanceOf(SSLException.class);
assertThat(nettyRule.getChannel().isActive()).isFalse(); assertThat(nettyRule.getClientChannel().isActive()).isFalse();
} }
@Test @Test
@ -233,7 +288,8 @@ public class SslServerInitializerTest {
SelfSignedCertificate serverSsc = new SelfSignedCertificate("wrong.com"); SelfSignedCertificate serverSsc = new SelfSignedCertificate("wrong.com");
LocalAddress localAddress = new LocalAddress("WRONG_HOSTNAME_" + sslProvider); LocalAddress localAddress = new LocalAddress("WRONG_HOSTNAME_" + sslProvider);
nettyRule.setUpServer(localAddress, getServerHandler(serverSsc.key(), serverSsc.cert())); nettyRule.setUpServer(
localAddress, getServerHandler(true, false, serverSsc.key(), serverSsc.cert()));
SelfSignedCertificate clientSsc = new SelfSignedCertificate(); SelfSignedCertificate clientSsc = new SelfSignedCertificate();
nettyRule.setUpClient( nettyRule.setUpClient(
localAddress, getClientHandler(serverSsc.cert(), clientSsc.key(), clientSsc.cert())); localAddress, getClientHandler(serverSsc.cert(), clientSsc.key(), clientSsc.cert()));
@ -243,6 +299,6 @@ public class SslServerInitializerTest {
nettyRule.assertThatClientRootCause().isInstanceOf(CertificateException.class); nettyRule.assertThatClientRootCause().isInstanceOf(CertificateException.class);
nettyRule.assertThatClientRootCause().hasMessageThat().contains(SSL_HOST); nettyRule.assertThatClientRootCause().hasMessageThat().contains(SSL_HOST);
nettyRule.assertThatServerRootCause().isInstanceOf(SSLException.class); nettyRule.assertThatServerRootCause().isInstanceOf(SSLException.class);
assertThat(nettyRule.getChannel().isActive()).isFalse(); assertThat(nettyRule.getClientChannel().isActive()).isFalse();
} }
} }

View file

@ -162,7 +162,8 @@ public final class EppProtocolModule {
SslProvider sslProvider, SslProvider sslProvider,
Supplier<PrivateKey> privateKeySupplier, Supplier<PrivateKey> privateKeySupplier,
Supplier<ImmutableList<X509Certificate>> certificatesSupplier) { Supplier<ImmutableList<X509Certificate>> certificatesSupplier) {
return new SslServerInitializer<>(true, sslProvider, privateKeySupplier, certificatesSupplier); return new SslServerInitializer<>(
true, false, sslProvider, privateKeySupplier, certificatesSupplier);
} }
@Provides @Provides

View file

@ -134,6 +134,7 @@ public final class WebWhoisProtocolsModule {
SslProvider sslProvider, SslProvider sslProvider,
Supplier<PrivateKey> privateKeySupplier, Supplier<PrivateKey> privateKeySupplier,
Supplier<ImmutableList<X509Certificate>> certificatesSupplier) { Supplier<ImmutableList<X509Certificate>> certificatesSupplier) {
return new SslServerInitializer<>(false, sslProvider, privateKeySupplier, certificatesSupplier); return new SslServerInitializer<>(
false, false, sslProvider, privateKeySupplier, certificatesSupplier);
} }
} }