Skip to content

Commit 8dbb3dc

Browse files
committed
Put RekorClient and FulcioClient behind interfaces
This lets them be a little more extensible if the underlying communication mechanism needs to change (grpc, http) Signed-off-by: Appu Goundan <[email protected]>
1 parent 8fdbdac commit 8dbb3dc

File tree

8 files changed

+343
-262
lines changed

8 files changed

+343
-262
lines changed

sigstore-java/src/main/java/dev/sigstore/KeylessSigner.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import dev.sigstore.encryption.signers.Signers;
3232
import dev.sigstore.fulcio.client.CertificateRequest;
3333
import dev.sigstore.fulcio.client.FulcioClient;
34+
import dev.sigstore.fulcio.client.FulcioClientGrpc;
3435
import dev.sigstore.fulcio.client.FulcioVerificationException;
3536
import dev.sigstore.fulcio.client.FulcioVerifier;
3637
import dev.sigstore.fulcio.client.UnsupportedAlgorithmException;
@@ -39,6 +40,7 @@
3940
import dev.sigstore.oidc.client.OidcToken;
4041
import dev.sigstore.rekor.client.HashedRekordRequest;
4142
import dev.sigstore.rekor.client.RekorClient;
43+
import dev.sigstore.rekor.client.RekorClientHttp;
4244
import dev.sigstore.rekor.client.RekorParseException;
4345
import dev.sigstore.rekor.client.RekorResponse;
4446
import dev.sigstore.rekor.client.RekorVerificationException;
@@ -216,9 +218,9 @@ public KeylessSigner build()
216218
Preconditions.checkNotNull(oidcIdentities);
217219
Preconditions.checkNotNull(signer);
218220
Preconditions.checkNotNull(minSigningCertificateLifetime);
219-
var fulcioClient = FulcioClient.builder().setUri(fulcioUri).build();
221+
var fulcioClient = FulcioClientGrpc.builder().setUri(fulcioUri).build();
220222
var fulcioVerifier = FulcioVerifier.newFulcioVerifier(trustedRoot);
221-
var rekorClient = RekorClient.builder().setUri(rekorUri).build();
223+
var rekorClient = RekorClientHttp.builder().setUri(rekorUri).build();
222224
var rekorVerifier = RekorVerifier.newRekorVerifier(trustedRoot);
223225
return new KeylessSigner(
224226
fulcioClient,

sigstore-java/src/main/java/dev/sigstore/fulcio/client/FulcioClient.java

Lines changed: 6 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -15,135 +15,15 @@
1515
*/
1616
package dev.sigstore.fulcio.client;
1717

18-
import static dev.sigstore.fulcio.v2.SigningCertificate.CertificateCase.SIGNED_CERTIFICATE_DETACHED_SCT;
19-
20-
import com.google.common.annotations.VisibleForTesting;
21-
import com.google.protobuf.ByteString;
22-
import dev.sigstore.fulcio.v2.CAGrpc;
23-
import dev.sigstore.fulcio.v2.CertificateChain;
24-
import dev.sigstore.fulcio.v2.CreateSigningCertificateRequest;
25-
import dev.sigstore.fulcio.v2.Credentials;
26-
import dev.sigstore.fulcio.v2.PublicKey;
27-
import dev.sigstore.fulcio.v2.PublicKeyRequest;
28-
import dev.sigstore.http.GrpcChannels;
29-
import dev.sigstore.http.HttpParams;
30-
import dev.sigstore.http.ImmutableHttpParams;
31-
import java.io.ByteArrayInputStream;
3218
import java.net.URI;
3319
import java.security.cert.CertPath;
3420
import java.security.cert.CertificateException;
35-
import java.security.cert.CertificateFactory;
36-
import java.security.cert.CertificateParsingException;
37-
import java.security.cert.X509Certificate;
38-
import java.util.ArrayList;
39-
import java.util.Base64;
40-
import java.util.concurrent.TimeUnit;
41-
42-
/** A client to communicate with a fulcio service instance over gRPC. */
43-
public class FulcioClient {
44-
45-
public static final URI PUBLIC_GOOD_URI = URI.create("https://fulcio.sigstore.dev");
46-
public static final URI STAGING_URI = URI.create("https://fulcio.sigstage.dev");
47-
48-
private final HttpParams httpParams;
49-
private final URI uri;
50-
51-
public static Builder builder() {
52-
return new Builder();
53-
}
54-
55-
private FulcioClient(HttpParams httpParams, URI uri) {
56-
this.uri = uri;
57-
this.httpParams = httpParams;
58-
}
59-
60-
public static class Builder {
61-
private URI uri = PUBLIC_GOOD_URI;
62-
private HttpParams httpParams = ImmutableHttpParams.builder().build();
63-
64-
private Builder() {}
65-
66-
/** Configure the http properties, see {@link HttpParams}. */
67-
public Builder setHttpParams(HttpParams httpParams) {
68-
this.httpParams = httpParams;
69-
return this;
70-
}
71-
72-
/** Base url of the remote fulcio instance. */
73-
public Builder setUri(URI uri) {
74-
this.uri = uri;
75-
return this;
76-
}
77-
78-
public FulcioClient build() {
79-
return new FulcioClient(httpParams, uri);
80-
}
81-
}
82-
83-
/**
84-
* Request a signing certificate from fulcio.
85-
*
86-
* @param request certificate request parameters
87-
* @return a {@link CertPath} from fulcio
88-
*/
89-
public CertPath signingCertificate(CertificateRequest request)
90-
throws InterruptedException, CertificateException {
91-
// TODO: 1. If we want to reduce the cost of creating channels/connections, we could try
92-
// to make a new connection once per batch of fulcio requests, but we're not really
93-
// at that point yet.
94-
// TODO: 2. getUri().getAuthority() is potentially prone to error if we don't get a good URI
95-
var channel = GrpcChannels.newManagedChannel(uri.getAuthority(), httpParams);
96-
97-
try {
98-
var client = CAGrpc.newBlockingStub(channel);
99-
var credentials = Credentials.newBuilder().setOidcIdentityToken(request.getIdToken()).build();
100-
101-
String pemEncodedPublicKey =
102-
"-----BEGIN PUBLIC KEY-----\n"
103-
+ Base64.getEncoder().encodeToString(request.getPublicKey().getEncoded())
104-
+ "\n-----END PUBLIC KEY-----";
105-
var publicKeyRequest =
106-
PublicKeyRequest.newBuilder()
107-
.setPublicKey(
108-
PublicKey.newBuilder()
109-
.setAlgorithm(request.getPublicKeyAlgorithm())
110-
.setContent(pemEncodedPublicKey)
111-
.build())
112-
.setProofOfPossession(ByteString.copyFrom(request.getProofOfPossession()))
113-
.build();
114-
var req =
115-
CreateSigningCertificateRequest.newBuilder()
116-
.setCredentials(credentials)
117-
.setPublicKeyRequest(publicKeyRequest)
118-
.build();
119-
120-
var certs =
121-
client
122-
.withDeadlineAfter(httpParams.getTimeout(), TimeUnit.SECONDS)
123-
.createSigningCertificate(req);
12421

125-
if (certs.getCertificateCase() == SIGNED_CERTIFICATE_DETACHED_SCT) {
126-
throw new CertificateException("Detached SCTs are not supported");
127-
}
128-
return decodeCerts(certs.getSignedCertificateEmbeddedSct().getChain());
129-
} finally {
130-
channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
131-
}
132-
}
22+
/** A client to communicate with a fulcio service instance. */
23+
public interface FulcioClient {
24+
URI PUBLIC_GOOD_URI = URI.create("https://fulcio.sigstore.dev");
25+
URI STAGING_URI = URI.create("https://fulcio.sigstage.dev");
13326

134-
@VisibleForTesting
135-
CertPath decodeCerts(CertificateChain certChain) throws CertificateException {
136-
var certificateFactory = CertificateFactory.getInstance("X.509");
137-
var certs = new ArrayList<X509Certificate>();
138-
if (certChain.getCertificatesCount() == 0) {
139-
throw new CertificateParsingException(
140-
"no valid PEM certificates were found in response from Fulcio");
141-
}
142-
for (var cert : certChain.getCertificatesList().asByteStringList()) {
143-
certs.add(
144-
(X509Certificate)
145-
certificateFactory.generateCertificate(new ByteArrayInputStream(cert.toByteArray())));
146-
}
147-
return certificateFactory.generateCertPath(certs);
148-
}
27+
CertPath signingCertificate(CertificateRequest request)
28+
throws InterruptedException, CertificateException;
14929
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
* Copyright 2022 The Sigstore 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+
package dev.sigstore.fulcio.client;
17+
18+
import static dev.sigstore.fulcio.v2.SigningCertificate.CertificateCase.SIGNED_CERTIFICATE_DETACHED_SCT;
19+
20+
import com.google.common.annotations.VisibleForTesting;
21+
import com.google.protobuf.ByteString;
22+
import dev.sigstore.fulcio.v2.CAGrpc;
23+
import dev.sigstore.fulcio.v2.CertificateChain;
24+
import dev.sigstore.fulcio.v2.CreateSigningCertificateRequest;
25+
import dev.sigstore.fulcio.v2.Credentials;
26+
import dev.sigstore.fulcio.v2.PublicKey;
27+
import dev.sigstore.fulcio.v2.PublicKeyRequest;
28+
import dev.sigstore.http.GrpcChannels;
29+
import dev.sigstore.http.HttpParams;
30+
import dev.sigstore.http.ImmutableHttpParams;
31+
import java.io.ByteArrayInputStream;
32+
import java.net.URI;
33+
import java.security.cert.CertPath;
34+
import java.security.cert.CertificateException;
35+
import java.security.cert.CertificateFactory;
36+
import java.security.cert.CertificateParsingException;
37+
import java.security.cert.X509Certificate;
38+
import java.util.ArrayList;
39+
import java.util.Base64;
40+
import java.util.concurrent.TimeUnit;
41+
42+
/** A client to communicate with a fulcio service instance over gRPC. */
43+
public class FulcioClientGrpc implements FulcioClient {
44+
45+
private final HttpParams httpParams;
46+
private final URI uri;
47+
48+
public static Builder builder() {
49+
return new Builder();
50+
}
51+
52+
private FulcioClientGrpc(HttpParams httpParams, URI uri) {
53+
this.uri = uri;
54+
this.httpParams = httpParams;
55+
}
56+
57+
public static class Builder {
58+
private URI uri = FulcioClient.PUBLIC_GOOD_URI;
59+
private HttpParams httpParams = ImmutableHttpParams.builder().build();
60+
61+
private Builder() {}
62+
63+
/** Configure the http properties, see {@link HttpParams}. */
64+
public Builder setHttpParams(HttpParams httpParams) {
65+
this.httpParams = httpParams;
66+
return this;
67+
}
68+
69+
/** Base url of the remote fulcio instance. */
70+
public Builder setUri(URI uri) {
71+
this.uri = uri;
72+
return this;
73+
}
74+
75+
public FulcioClientGrpc build() {
76+
return new FulcioClientGrpc(httpParams, uri);
77+
}
78+
}
79+
80+
/**
81+
* Request a signing certificate from fulcio.
82+
*
83+
* @param request certificate request parameters
84+
* @return a {@link CertPath} from fulcio
85+
*/
86+
@Override
87+
public CertPath signingCertificate(CertificateRequest request)
88+
throws InterruptedException, CertificateException {
89+
// TODO: 1. If we want to reduce the cost of creating channels/connections, we could try
90+
// to make a new connection once per batch of fulcio requests, but we're not really
91+
// at that point yet.
92+
// TODO: 2. getUri().getAuthority() is potentially prone to error if we don't get a good URI
93+
var channel = GrpcChannels.newManagedChannel(uri.getAuthority(), httpParams);
94+
95+
try {
96+
var client = CAGrpc.newBlockingStub(channel);
97+
var credentials = Credentials.newBuilder().setOidcIdentityToken(request.getIdToken()).build();
98+
99+
String pemEncodedPublicKey =
100+
"-----BEGIN PUBLIC KEY-----\n"
101+
+ Base64.getEncoder().encodeToString(request.getPublicKey().getEncoded())
102+
+ "\n-----END PUBLIC KEY-----";
103+
var publicKeyRequest =
104+
PublicKeyRequest.newBuilder()
105+
.setPublicKey(
106+
PublicKey.newBuilder()
107+
.setAlgorithm(request.getPublicKeyAlgorithm())
108+
.setContent(pemEncodedPublicKey)
109+
.build())
110+
.setProofOfPossession(ByteString.copyFrom(request.getProofOfPossession()))
111+
.build();
112+
var req =
113+
CreateSigningCertificateRequest.newBuilder()
114+
.setCredentials(credentials)
115+
.setPublicKeyRequest(publicKeyRequest)
116+
.build();
117+
118+
var certs =
119+
client
120+
.withDeadlineAfter(httpParams.getTimeout(), TimeUnit.SECONDS)
121+
.createSigningCertificate(req);
122+
123+
if (certs.getCertificateCase() == SIGNED_CERTIFICATE_DETACHED_SCT) {
124+
throw new CertificateException("Detached SCTs are not supported");
125+
}
126+
return decodeCerts(certs.getSignedCertificateEmbeddedSct().getChain());
127+
} finally {
128+
channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
129+
}
130+
}
131+
132+
@VisibleForTesting
133+
CertPath decodeCerts(CertificateChain certChain) throws CertificateException {
134+
var certificateFactory = CertificateFactory.getInstance("X.509");
135+
var certs = new ArrayList<X509Certificate>();
136+
if (certChain.getCertificatesCount() == 0) {
137+
throw new CertificateParsingException(
138+
"no valid PEM certificates were found in response from Fulcio");
139+
}
140+
for (var cert : certChain.getCertificatesList().asByteStringList()) {
141+
certs.add(
142+
(X509Certificate)
143+
certificateFactory.generateCertificate(new ByteArrayInputStream(cert.toByteArray())));
144+
}
145+
return certificateFactory.generateCertPath(certs);
146+
}
147+
}

0 commit comments

Comments
 (0)