From b5798fcd32c465dc254699718f1098cb03eb7d5e Mon Sep 17 00:00:00 2001 From: Appu Goundan Date: Fri, 21 Oct 2022 16:05:54 -0400 Subject: [PATCH] Support multiple ctfe keys Signed-off-by: Appu Goundan --- .../main/java/dev/sigstore/KeylessSigner.java | 28 +++----- .../java/dev/sigstore/KeylessVerifier.java | 30 ++++----- .../dev/sigstore/VerificationMaterial.java | 66 +++++++++++++++++++ .../fulcio/client/FulcioVerifier.java | 37 +++++++---- .../main/resources/dev/sigstore/tuf/README.md | 4 +- .../dev/sigstore/tuf/staging/ctfe_2022.pub | 4 ++ .../dev/sigstore/tuf/staging/ctfe_2022_2.pub | 4 ++ .../fulcio/client/FulcioVerifierTest.java | 13 ++-- .../valid/{ctfe-ec.pub => ctfe.pub} | 0 9 files changed, 129 insertions(+), 57 deletions(-) create mode 100644 sigstore-java/src/main/java/dev/sigstore/VerificationMaterial.java create mode 100644 sigstore-java/src/main/resources/dev/sigstore/tuf/staging/ctfe_2022.pub create mode 100644 sigstore-java/src/main/resources/dev/sigstore/tuf/staging/ctfe_2022_2.pub rename sigstore-java/src/test/resources/dev/sigstore/samples/fulcio-response/valid/{ctfe-ec.pub => ctfe.pub} (100%) diff --git a/sigstore-java/src/main/java/dev/sigstore/KeylessSigner.java b/sigstore-java/src/main/java/dev/sigstore/KeylessSigner.java index 08fe1135..49e66184 100644 --- a/sigstore-java/src/main/java/dev/sigstore/KeylessSigner.java +++ b/sigstore-java/src/main/java/dev/sigstore/KeylessSigner.java @@ -17,7 +17,6 @@ import com.google.api.client.util.Preconditions; import com.google.common.hash.Hashing; -import com.google.common.io.Resources; import dev.sigstore.encryption.certificates.Certificates; import dev.sigstore.encryption.signers.Signer; import dev.sigstore.encryption.signers.Signers; @@ -113,17 +112,14 @@ public KeylessSigner build() { public Builder sigstorePublicDefaults() throws IOException, InvalidAlgorithmParameterException, CertificateException, InvalidKeySpecException, NoSuchAlgorithmException { - var fulcioCert = - Resources.toByteArray( - Resources.getResource("dev/sigstore/tuf/production/fulcio_v1.crt.pem")); - var ctfePublicKey = - Resources.toByteArray(Resources.getResource("dev/sigstore/tuf/production/ctfe.pub")); - var rekorPublicKey = - Resources.toByteArray(Resources.getResource("dev/sigstore/tuf/production/rekor.pub")); fulcioClient( FulcioClient.builder().build(), - FulcioVerifier.newFulcioVerifier(fulcioCert, ctfePublicKey)); - rekorClient(RekorClient.builder().build(), RekorVerifier.newRekorVerifier(rekorPublicKey)); + FulcioVerifier.newFulcioVerifier( + VerificationMaterial.Production.fulioCert(), + VerificationMaterial.Production.ctfePublicKeys())); + rekorClient( + RekorClient.builder().build(), + RekorVerifier.newRekorVerifier(VerificationMaterial.Production.rekorPublicKey())); oidcClient(WebOidcClient.builder().build()); signer(Signers.newEcdsaSigner()); return this; @@ -132,20 +128,16 @@ public Builder sigstorePublicDefaults() public Builder sigstoreStagingDefaults() throws IOException, InvalidAlgorithmParameterException, CertificateException, InvalidKeySpecException, NoSuchAlgorithmException { - var fulcioCert = - Resources.toByteArray(Resources.getResource("dev/sigstore/tuf/staging/fulcio.crt.pem")); - var ctfePublicKey = - Resources.toByteArray(Resources.getResource("dev/sigstore/tuf/staging/ctfe.pub")); - var rekorPublicKey = - Resources.toByteArray(Resources.getResource("dev/sigstore/tuf/staging/rekor.pub")); fulcioClient( FulcioClient.builder() .setServerUrl(URI.create(FulcioClient.STAGING_FULCIO_SERVER)) .build(), - FulcioVerifier.newFulcioVerifier(fulcioCert, ctfePublicKey)); + FulcioVerifier.newFulcioVerifier( + VerificationMaterial.Staging.fulioCert(), + VerificationMaterial.Staging.ctfePublicKeys())); rekorClient( RekorClient.builder().setServerUrl(URI.create(RekorClient.STAGING_REKOR_SERVER)).build(), - RekorVerifier.newRekorVerifier(rekorPublicKey)); + RekorVerifier.newRekorVerifier(VerificationMaterial.Staging.rekorPublicKey())); oidcClient(WebOidcClient.builder().setIssuer(WebOidcClient.STAGING_DEX_ISSUER).build()); signer(Signers.newEcdsaSigner()); return this; diff --git a/sigstore-java/src/main/java/dev/sigstore/KeylessVerifier.java b/sigstore-java/src/main/java/dev/sigstore/KeylessVerifier.java index d68e36fd..df8cd7dc 100644 --- a/sigstore-java/src/main/java/dev/sigstore/KeylessVerifier.java +++ b/sigstore-java/src/main/java/dev/sigstore/KeylessVerifier.java @@ -16,7 +16,6 @@ package dev.sigstore; import com.google.api.client.util.Preconditions; -import com.google.common.io.Resources; import dev.sigstore.encryption.certificates.Certificates; import dev.sigstore.encryption.signers.Verifiers; import dev.sigstore.fulcio.client.FulcioVerificationException; @@ -78,31 +77,26 @@ public KeylessVerifier build() { public Builder sigstorePublicDefaults() throws IOException, InvalidAlgorithmParameterException, CertificateException, InvalidKeySpecException, NoSuchAlgorithmException { - var fulcioCert = - Resources.toByteArray( - Resources.getResource("dev/sigstore/tuf/production/fulcio_v1.crt.pem")); - var ctfePublicKey = - Resources.toByteArray(Resources.getResource("dev/sigstore/tuf/production/ctfe.pub")); - var rekorPublicKey = - Resources.toByteArray(Resources.getResource("dev/sigstore/tuf/production/rekor.pub")); - fulcioVerifier(FulcioVerifier.newFulcioVerifier(fulcioCert, ctfePublicKey)); - rekorClient(RekorClient.builder().build(), RekorVerifier.newRekorVerifier(rekorPublicKey)); + fulcioVerifier( + FulcioVerifier.newFulcioVerifier( + VerificationMaterial.Production.fulioCert(), + VerificationMaterial.Production.ctfePublicKeys())); + rekorClient( + RekorClient.builder().build(), + RekorVerifier.newRekorVerifier(VerificationMaterial.Production.rekorPublicKey())); return this; } public Builder sigstoreStagingDefaults() throws IOException, InvalidAlgorithmParameterException, CertificateException, InvalidKeySpecException, NoSuchAlgorithmException { - var fulcioCert = - Resources.toByteArray(Resources.getResource("dev/sigstore/tuf/staging/fulcio.crt.pem")); - var ctfePublicKey = - Resources.toByteArray(Resources.getResource("dev/sigstore/tuf/staging/ctfe.pub")); - var rekorPublicKey = - Resources.toByteArray(Resources.getResource("dev/sigstore/tuf/staging/rekor.pub")); - fulcioVerifier(FulcioVerifier.newFulcioVerifier(fulcioCert, ctfePublicKey)); + fulcioVerifier( + FulcioVerifier.newFulcioVerifier( + VerificationMaterial.Staging.fulioCert(), + VerificationMaterial.Staging.ctfePublicKeys())); rekorClient( RekorClient.builder().setServerUrl(URI.create(RekorClient.STAGING_REKOR_SERVER)).build(), - RekorVerifier.newRekorVerifier(rekorPublicKey)); + RekorVerifier.newRekorVerifier(VerificationMaterial.Staging.rekorPublicKey())); return this; } } diff --git a/sigstore-java/src/main/java/dev/sigstore/VerificationMaterial.java b/sigstore-java/src/main/java/dev/sigstore/VerificationMaterial.java new file mode 100644 index 00000000..9d7b5f61 --- /dev/null +++ b/sigstore-java/src/main/java/dev/sigstore/VerificationMaterial.java @@ -0,0 +1,66 @@ +/* + * Copyright 2022 The Sigstore 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 dev.sigstore; + +import com.google.common.io.Resources; +import java.io.IOException; +import java.util.List; + +/** + * A temporary partial copy of the TUF repos that supplies necessary keys for prod and staging + * environments. This should be replaced with an actual TUF implementation + */ +public class VerificationMaterial { + + /** Verification material for *.sigstore.dev */ + public static class Production { + public static byte[] fulioCert() throws IOException { + return Resources.toByteArray( + Resources.getResource("dev/sigstore/tuf/production/fulcio_v1.crt.pem")); + } + + public static List ctfePublicKeys() throws IOException { + return List.of( + Resources.toByteArray(Resources.getResource("dev/sigstore/tuf/production/ctfe.pub"))); + } + + public static byte[] rekorPublicKey() throws IOException { + return Resources.toByteArray(Resources.getResource("dev/sigstore/tuf/production/rekor.pub")); + } + } + + /** Verification material for *.sigstage.dev */ + public static class Staging { + public static byte[] fulioCert() throws IOException { + return Resources.toByteArray( + Resources.getResource("dev/sigstore/tuf/staging/fulcio.crt.pem")); + } + + public static List ctfePublicKeys() throws IOException { + var ctfePublicKey = + Resources.toByteArray(Resources.getResource("dev/sigstore/tuf/staging/ctfe.pub")); + var ctfePublicKey2022 = + Resources.toByteArray(Resources.getResource("dev/sigstore/tuf/staging/ctfe_2022.pub")); + var ctfePublicKey2022_2 = + Resources.toByteArray(Resources.getResource("dev/sigstore/tuf/staging/ctfe_2022_2.pub")); + return List.of(ctfePublicKey, ctfePublicKey2022, ctfePublicKey2022_2); + } + + public static byte[] rekorPublicKey() throws IOException { + return Resources.toByteArray(Resources.getResource("dev/sigstore/tuf/staging/rekor.pub")); + } + } +} diff --git a/sigstore-java/src/main/java/dev/sigstore/fulcio/client/FulcioVerifier.java b/sigstore-java/src/main/java/dev/sigstore/fulcio/client/FulcioVerifier.java index 5f52a0ed..276381f9 100644 --- a/sigstore-java/src/main/java/dev/sigstore/fulcio/client/FulcioVerifier.java +++ b/sigstore-java/src/main/java/dev/sigstore/fulcio/client/FulcioVerifier.java @@ -24,9 +24,7 @@ import java.security.PublicKey; import java.security.cert.*; import java.security.spec.InvalidKeySpecException; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; +import java.util.*; import org.checkerframework.checker.nullness.qual.Nullable; /** Verifier for fulcio {@link dev.sigstore.fulcio.client.SigningCertificate}. */ @@ -38,15 +36,18 @@ public class FulcioVerifier { * Instantiate a new verifier. * * @param fulcioRoot fulcio's root certificate - * @param ctfePublicKey fulcio's certificate transparency log's public key + * @param ctfePublicKeys fulcio's certificate transparency log public keys (for all logs) */ - public static FulcioVerifier newFulcioVerifier(byte[] fulcioRoot, byte @Nullable [] ctfePublicKey) + public static FulcioVerifier newFulcioVerifier(byte[] fulcioRoot, List ctfePublicKeys) throws InvalidKeySpecException, NoSuchAlgorithmException, CertificateException, IOException, InvalidAlgorithmParameterException { - PublicKey ctfePublicKeyObj = null; - if (ctfePublicKey != null) { - ctfePublicKeyObj = Keys.parsePublicKey(ctfePublicKey); + List ctfePublicKeyObjs = null; + if (ctfePublicKeys != null && ctfePublicKeys.size() != 0) { + ctfePublicKeyObjs = new ArrayList<>(); + for (var pk : ctfePublicKeys) { + ctfePublicKeyObjs.add(Keys.parsePublicKey(pk)); + } } CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); @@ -59,15 +60,25 @@ public static FulcioVerifier newFulcioVerifier(byte[] fulcioRoot, byte @Nullable // encountered in verifyCertPath new PKIXParameters(Collections.singleton(fulcioRootTrustAnchor)); - return new FulcioVerifier(fulcioRootTrustAnchor, ctfePublicKeyObj); + return new FulcioVerifier(fulcioRootTrustAnchor, ctfePublicKeyObjs); } - private FulcioVerifier(TrustAnchor fulcioRoot, @Nullable PublicKey ctfePublicKey) { + private FulcioVerifier(TrustAnchor fulcioRoot, @Nullable List ctfePublicKeys) { this.fulcioRoot = fulcioRoot; - if (ctfePublicKey != null) { - var ctLogInfo = new CTLogInfo(ctfePublicKey, "fulcio ct log", "unused-url"); + if (ctfePublicKeys != null) { + var logInfos = new ArrayList(); + for (var pk : ctfePublicKeys) { + var ctLogInfo = new CTLogInfo(pk, "fulcio ct log", "unused-url"); + logInfos.add(ctLogInfo); + } this.ctVerifier = - new CTVerifier(logId -> Arrays.equals(logId, ctLogInfo.getID()) ? ctLogInfo : null); + new CTVerifier( + logId -> + logInfos + .stream() + .filter(ctLogInfo -> Arrays.equals(ctLogInfo.getID(), logId)) + .findFirst() + .orElse(null)); } else { ctVerifier = null; } diff --git a/sigstore-java/src/main/resources/dev/sigstore/tuf/README.md b/sigstore-java/src/main/resources/dev/sigstore/tuf/README.md index 1aed30b2..15b222f3 100644 --- a/sigstore-java/src/main/resources/dev/sigstore/tuf/README.md +++ b/sigstore-java/src/main/resources/dev/sigstore/tuf/README.md @@ -13,9 +13,9 @@ We keep copies of the remote tuf repositories locally in for interfacing with *.sigstage.dev For this client to function we need the following keys -1. CTFE public key (`ctfe.pub` \ +1. CTFE public keys (`ctfe.pub`, `ctfe_*.pub`) \ the public key for the certificate transparency log -2. Fulcio root cert (`fulcio_v1.crt.pem` or `fulcio.crt.pem`)\ +2. Fulcio root cert (`fulcio_v1.crt.pem` or `fulcio.crt.pem`) \ the root certificate for fulcio issued certificates 3. Rekor public key (`rekor.pub`) \ the public key for the rekor transparency log diff --git a/sigstore-java/src/main/resources/dev/sigstore/tuf/staging/ctfe_2022.pub b/sigstore-java/src/main/resources/dev/sigstore/tuf/staging/ctfe_2022.pub new file mode 100644 index 00000000..3023b861 --- /dev/null +++ b/sigstore-java/src/main/resources/dev/sigstore/tuf/staging/ctfe_2022.pub @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEh99xuRi6slBFd8VUJoK/rLigy4bY +eSYWO/fE6Br7r0D8NpMI94+A63LR/WvLxpUUGBpY8IJA3iU2telag5CRpA== +-----END PUBLIC KEY----- diff --git a/sigstore-java/src/main/resources/dev/sigstore/tuf/staging/ctfe_2022_2.pub b/sigstore-java/src/main/resources/dev/sigstore/tuf/staging/ctfe_2022_2.pub new file mode 100644 index 00000000..0f5eb863 --- /dev/null +++ b/sigstore-java/src/main/resources/dev/sigstore/tuf/staging/ctfe_2022_2.pub @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8gEDKNme8AnXuPBgHjrtXdS6miHq +c24CRblNEOFpiJRngeq8Ko73Y+K18yRYVf1DXD4AVLwvKyzdNdl5n0jUSQ== +-----END PUBLIC KEY----- diff --git a/sigstore-java/src/test/java/dev/sigstore/fulcio/client/FulcioVerifierTest.java b/sigstore-java/src/test/java/dev/sigstore/fulcio/client/FulcioVerifierTest.java index 0f5815ad..8e09906c 100644 --- a/sigstore-java/src/test/java/dev/sigstore/fulcio/client/FulcioVerifierTest.java +++ b/sigstore-java/src/test/java/dev/sigstore/fulcio/client/FulcioVerifierTest.java @@ -23,6 +23,7 @@ import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.spec.InvalidKeySpecException; +import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -51,7 +52,7 @@ public static void loadResources() throws IOException { Resources.getResource("dev/sigstore/samples/fulcio-response/valid/fulcio.crt.pem")); ctfePub = Resources.toByteArray( - Resources.getResource("dev/sigstore/samples/fulcio-response/valid/ctfe-ec.pub")); + Resources.getResource("dev/sigstore/samples/fulcio-response/valid/ctfe.pub")); badCtfePub = Resources.toByteArray(Resources.getResource("dev/sigstore/samples/keys/test-rsa.pub")); @@ -67,7 +68,7 @@ public void validSigningCertAndDetachedSct() NoSuchAlgorithmException, InvalidAlgorithmParameterException, FulcioVerificationException { var signingCertificate = SigningCertificate.newSigningCertificate(certs, sctBase64); - var fulcioVerifier = FulcioVerifier.newFulcioVerifier(fulcioRoot, ctfePub); + var fulcioVerifier = FulcioVerifier.newFulcioVerifier(fulcioRoot, List.of(ctfePub, badCtfePub)); fulcioVerifier.verifyCertChain(signingCertificate); fulcioVerifier.verifySct(signingCertificate); @@ -93,7 +94,7 @@ public void testVerifySct_noSct() throws SerializationException, CertificateException, IOException, InvalidAlgorithmParameterException, InvalidKeySpecException, NoSuchAlgorithmException { var signingCertificate = SigningCertificate.newSigningCertificate(certs, null); - var fulcioVerifier = FulcioVerifier.newFulcioVerifier(fulcioRoot, ctfePub); + var fulcioVerifier = FulcioVerifier.newFulcioVerifier(fulcioRoot, List.of(ctfePub, badCtfePub)); try { fulcioVerifier.verifySct(signingCertificate); @@ -110,7 +111,7 @@ public void validSigningCertAndEmbeddedSct() NoSuchAlgorithmException, InvalidAlgorithmParameterException, FulcioVerificationException { var signingCertificate = SigningCertificate.newSigningCertificate(certsWithEmbeddedSct, null); - var fulcioVerifier = FulcioVerifier.newFulcioVerifier(fulcioRoot, ctfePub); + var fulcioVerifier = FulcioVerifier.newFulcioVerifier(fulcioRoot, List.of(ctfePub, badCtfePub)); fulcioVerifier.verifyCertChain(signingCertificate); fulcioVerifier.verifySct(signingCertificate); @@ -122,7 +123,7 @@ public void invalidEmbeddedSct() InvalidAlgorithmParameterException, InvalidKeySpecException, NoSuchAlgorithmException, FulcioVerificationException { var signingCertificate = SigningCertificate.newSigningCertificate(certsWithEmbeddedSct, null); - var fulcioVerifier = FulcioVerifier.newFulcioVerifier(fulcioRoot, badCtfePub); + var fulcioVerifier = FulcioVerifier.newFulcioVerifier(fulcioRoot, List.of(badCtfePub)); var fve = Assertions.assertThrows( @@ -136,7 +137,7 @@ public void invalidDetachedSct() throws SerializationException, CertificateException, IOException, InvalidAlgorithmParameterException, InvalidKeySpecException, NoSuchAlgorithmException { var signingCertificate = SigningCertificate.newSigningCertificate(certs, sctBase64); - var fulcioVerifier = FulcioVerifier.newFulcioVerifier(fulcioRoot, badCtfePub); + var fulcioVerifier = FulcioVerifier.newFulcioVerifier(fulcioRoot, List.of(badCtfePub)); var fve = Assertions.assertThrows( diff --git a/sigstore-java/src/test/resources/dev/sigstore/samples/fulcio-response/valid/ctfe-ec.pub b/sigstore-java/src/test/resources/dev/sigstore/samples/fulcio-response/valid/ctfe.pub similarity index 100% rename from sigstore-java/src/test/resources/dev/sigstore/samples/fulcio-response/valid/ctfe-ec.pub rename to sigstore-java/src/test/resources/dev/sigstore/samples/fulcio-response/valid/ctfe.pub