Skip to content

Commit 2adbbf8

Browse files
authored
Merge pull request #1197 from sigstore/more_algorithm_registry
Make algorithm registry more central to system
2 parents 527d50a + 8223cd8 commit 2adbbf8

6 files changed

Lines changed: 224 additions & 29 deletions

File tree

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

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,55 @@
1515
*/
1616
package dev.sigstore;
1717

18-
import static com.google.common.hash.Hashing.*;
18+
import static com.google.common.hash.Hashing.sha256;
1919

2020
import com.google.common.hash.HashFunction;
21+
import java.security.PublicKey;
22+
import java.security.interfaces.ECPublicKey;
23+
import java.security.interfaces.RSAPublicKey;
2124

2225
public class AlgorithmRegistry {
26+
27+
/**
28+
* Determine the signing algorithm based on the public key.
29+
*
30+
* @param publicKey the public key
31+
* @return the signing algorithm
32+
* @throws UnsupportedAlgorithmException if the key algorithm or curve is not supported
33+
* @throws IllegalStateException if the public key cannot be converted into a known type
34+
*/
35+
public static SigningAlgorithm getSigningAlgorithm(PublicKey publicKey)
36+
throws UnsupportedAlgorithmException {
37+
String algorithm = publicKey.getAlgorithm();
38+
if ("RSA".equals(algorithm)) {
39+
if (publicKey instanceof RSAPublicKey) {
40+
var rsaKey = (RSAPublicKey) publicKey;
41+
int bitLength = rsaKey.getModulus().bitLength();
42+
if (bitLength == 2048) {
43+
return SigningAlgorithm.PKIX_RSA_PKCS1V15_2048_SHA256;
44+
} else if (bitLength == 3072) {
45+
return SigningAlgorithm.PKIX_RSA_PKCS1V15_3072_SHA256;
46+
} else if (bitLength == 4096) {
47+
return SigningAlgorithm.PKIX_RSA_PKCS1V15_4096_SHA256;
48+
}
49+
throw new UnsupportedAlgorithmException("Unsupported RSA bit length: " + bitLength);
50+
}
51+
throw new IllegalStateException("RSA key must be an instance of RSAPublicKey");
52+
}
53+
if ("EC".equals(algorithm) || "ECDSA".equals(algorithm)) {
54+
if (publicKey instanceof ECPublicKey) {
55+
var ecKey = (ECPublicKey) publicKey;
56+
int fieldSize = ecKey.getParams().getCurve().getField().getFieldSize();
57+
if (fieldSize == 256) {
58+
return SigningAlgorithm.PKIX_ECDSA_P256_SHA_256;
59+
}
60+
throw new UnsupportedAlgorithmException("Unsupported EC field size: " + fieldSize);
61+
}
62+
throw new IllegalStateException("EC/ECDSA key must be an instance of ECPublicKey");
63+
}
64+
throw new UnsupportedAlgorithmException("Unsupported key algorithm: " + algorithm);
65+
}
66+
2367
public enum SigningAlgorithm {
2468
PKIX_RSA_PKCS1V15_2048_SHA256(HashAlgorithm.SHA2_256),
2569
PKIX_RSA_PKCS1V15_3072_SHA256(HashAlgorithm.SHA2_256),
@@ -36,7 +80,7 @@ public enum SigningAlgorithm {
3680
this.hashAlgorithm = hashAlgorithm;
3781
}
3882

39-
public HashAlgorithm getHashing() {
83+
public HashAlgorithm getHashAlgorithm() {
4084
return hashAlgorithm;
4185
}
4286
}

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import dev.sigstore.fulcio.client.FulcioClientGrpc;
3939
import dev.sigstore.fulcio.client.FulcioVerificationException;
4040
import dev.sigstore.fulcio.client.FulcioVerifier;
41-
import dev.sigstore.fulcio.client.UnsupportedAlgorithmException;
4241
import dev.sigstore.json.JsonParseException;
4342
import dev.sigstore.oidc.client.OidcClients;
4443
import dev.sigstore.oidc.client.OidcException;
@@ -337,7 +336,7 @@ public KeylessSigner build()
337336
oidcClients = OidcClients.from(oidcService.get());
338337
}
339338

340-
if (!signingAlgorithm.getHashing().equals(AlgorithmRegistry.HashAlgorithm.SHA2_256)) {
339+
if (!signingAlgorithm.getHashAlgorithm().equals(AlgorithmRegistry.HashAlgorithm.SHA2_256)) {
341340
throw new SigstoreConfigurationException("Signing algorithm must use sha256");
342341
}
343342

@@ -402,7 +401,7 @@ public List<Bundle> sign(List<byte[]> artifactDigests) throws KeylessSignerExcep
402401
}
403402

404403
for (var digest : artifactDigests) {
405-
if (signingAlgorithm.getHashing().getLength() != digest.length) {
404+
if (signingAlgorithm.getHashAlgorithm().getLength() != digest.length) {
406405
throw new KeylessSignerException(
407406
"Invalid digest length: "
408407
+ digest.length
@@ -457,7 +456,8 @@ public List<Bundle> sign(List<byte[]> artifactDigests) throws KeylessSignerExcep
457456
ImmutableBundle.builder()
458457
.certPath(signingCert)
459458
.messageSignature(
460-
MessageSignature.of(signingAlgorithm.getHashing(), artifactDigest, signature));
459+
MessageSignature.of(
460+
signingAlgorithm.getHashAlgorithm(), artifactDigest, signature));
461461

462462
if (rekorV2Client != null) { // Using Rekor v2 and a TSA
463463
Preconditions.checkNotNull(
@@ -656,7 +656,9 @@ public Map<Path, Bundle> signFiles(List<Path> artifacts) throws KeylessSignerExc
656656
var artifactByteSource = com.google.common.io.Files.asByteSource(artifact.toFile());
657657
try {
658658
digests.add(
659-
artifactByteSource.hash(signingAlgorithm.getHashing().getHashFunction()).asBytes());
659+
artifactByteSource
660+
.hash(signingAlgorithm.getHashAlgorithm().getHashFunction())
661+
.asBytes());
660662
} catch (IOException ex) {
661663
throw new KeylessSignerException("Failed to hash artifact " + artifact);
662664
}

sigstore-java/src/main/java/dev/sigstore/fulcio/client/UnsupportedAlgorithmException.java renamed to sigstore-java/src/main/java/dev/sigstore/UnsupportedAlgorithmException.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,14 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package dev.sigstore.fulcio.client;
17-
18-
import java.util.Arrays;
19-
import java.util.Set;
16+
package dev.sigstore;
2017

2118
public class UnsupportedAlgorithmException extends Exception {
22-
public UnsupportedAlgorithmException(Set<String> allowedAlgorithms, String algorithm) {
23-
super(
24-
algorithm
25-
+ " is not from supported list of "
26-
+ Arrays.toString(allowedAlgorithms.toArray()));
19+
public UnsupportedAlgorithmException(String message) {
20+
super(message);
21+
}
22+
23+
public UnsupportedAlgorithmException(String message, Throwable cause) {
24+
super(message, cause);
2725
}
2826
}

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

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

18-
import com.google.common.collect.ImmutableMap;
18+
import dev.sigstore.AlgorithmRegistry;
19+
import dev.sigstore.UnsupportedAlgorithmException;
1920
import dev.sigstore.fulcio.v2.PublicKeyAlgorithm;
21+
import dev.sigstore.proto.ProtoMutators;
2022
import java.security.PublicKey;
21-
import java.util.Map;
2223
import org.immutables.value.Value;
2324

2425
@Value.Immutable
2526
public interface CertificateRequest {
26-
Map<String, PublicKeyAlgorithm> SUPPORTED_ALGORITHMS =
27-
ImmutableMap.of("EC", PublicKeyAlgorithm.ECDSA, "RSA", PublicKeyAlgorithm.RSA_PSS);
28-
2927
PublicKey getPublicKey();
3028

3129
PublicKeyAlgorithm getPublicKeyAlgorithm();
@@ -37,23 +35,19 @@ public interface CertificateRequest {
3735
/**
3836
* Create a certificate request
3937
*
40-
* @param publicKey An ECDSA public key
38+
* @param publicKey A public key to verify {@code proofOfPossession}
4139
* @param idToken An oidc token obtained from an oauth provider
4240
* @param proofOfPossession The subject or email address from {@code idToken}, signed by the
4341
* private key counterpart of {@code publicKey} in asn1 notation
44-
* @throws UnsupportedAlgorithmException if key type is not in {@link
45-
* CertificateRequest#SUPPORTED_ALGORITHMS}
42+
* @throws UnsupportedAlgorithmException if key type is not in {@link AlgorithmRegistry}
4643
*/
4744
static CertificateRequest newCertificateRequest(
4845
PublicKey publicKey, String idToken, byte[] proofOfPossession)
4946
throws UnsupportedAlgorithmException {
50-
if (!SUPPORTED_ALGORITHMS.containsKey(publicKey.getAlgorithm())) {
51-
throw new UnsupportedAlgorithmException(
52-
SUPPORTED_ALGORITHMS.keySet(), publicKey.getAlgorithm());
53-
}
47+
var signingAlgorithm = AlgorithmRegistry.getSigningAlgorithm(publicKey);
5448
return ImmutableCertificateRequest.builder()
5549
.publicKey(publicKey)
56-
.publicKeyAlgorithm(SUPPORTED_ALGORITHMS.get(publicKey.getAlgorithm()))
50+
.publicKeyAlgorithm(ProtoMutators.toPublicKeyAlgorithm(signingAlgorithm))
5751
.idToken(idToken)
5852
.proofOfPossession(proofOfPossession)
5953
.build();

sigstore-java/src/main/java/dev/sigstore/proto/ProtoMutators.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.google.protobuf.Timestamp;
2222
import dev.sigstore.AlgorithmRegistry;
2323
import dev.sigstore.encryption.certificates.Certificates;
24+
import dev.sigstore.fulcio.v2.PublicKeyAlgorithm;
2425
import dev.sigstore.proto.common.v1.HashAlgorithm;
2526
import dev.sigstore.proto.common.v1.PublicKeyDetails;
2627
import dev.sigstore.proto.common.v1.X509Certificate;
@@ -86,6 +87,19 @@ public static PublicKeyDetails toPublicKeyDetails(AlgorithmRegistry.SigningAlgor
8687
throw new IllegalStateException("Unknown algorithm: " + algorithm);
8788
}
8889

90+
public static PublicKeyAlgorithm toPublicKeyAlgorithm(
91+
AlgorithmRegistry.SigningAlgorithm algorithm) {
92+
switch (algorithm) {
93+
case PKIX_RSA_PKCS1V15_2048_SHA256:
94+
case PKIX_RSA_PKCS1V15_3072_SHA256:
95+
case PKIX_RSA_PKCS1V15_4096_SHA256:
96+
return PublicKeyAlgorithm.RSA_PSS;
97+
case PKIX_ECDSA_P256_SHA_256:
98+
return PublicKeyAlgorithm.ECDSA;
99+
}
100+
throw new IllegalStateException("Unknown algorithm: " + algorithm);
101+
}
102+
89103
public static RekorEntry toRekorEntry(TransparencyLogEntry tle) {
90104
ImmutableRekorEntry.Builder builder = ImmutableRekorEntry.builder();
91105

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*
2+
* Copyright 2025 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;
17+
18+
import static org.junit.jupiter.api.Assertions.assertEquals;
19+
import static org.junit.jupiter.api.Assertions.assertThrows;
20+
21+
import java.security.KeyPairGenerator;
22+
import java.security.PublicKey;
23+
import java.security.spec.ECGenParameterSpec;
24+
import org.junit.jupiter.api.Test;
25+
26+
public class AlgorithmRegistryTest {
27+
28+
@Test
29+
public void testGetHashAlgorithm_Unsupported() throws Exception {
30+
var keyPairGen = KeyPairGenerator.getInstance("DSA");
31+
keyPairGen.initialize(2048);
32+
var publicKey = keyPairGen.generateKeyPair().getPublic();
33+
assertThrows(
34+
UnsupportedAlgorithmException.class,
35+
() -> AlgorithmRegistry.getSigningAlgorithm(publicKey));
36+
}
37+
38+
@Test
39+
public void testGetHashAlgorithm_RSA_2048() throws Exception {
40+
var keyPairGen = KeyPairGenerator.getInstance("RSA");
41+
keyPairGen.initialize(2048);
42+
var publicKey = keyPairGen.generateKeyPair().getPublic();
43+
assertEquals(
44+
AlgorithmRegistry.SigningAlgorithm.PKIX_RSA_PKCS1V15_2048_SHA256,
45+
AlgorithmRegistry.getSigningAlgorithm(publicKey));
46+
}
47+
48+
@Test
49+
public void testGetHashAlgorithm_RSA_3072() throws Exception {
50+
var keyPairGen = KeyPairGenerator.getInstance("RSA");
51+
keyPairGen.initialize(3072);
52+
var publicKey = keyPairGen.generateKeyPair().getPublic();
53+
assertEquals(
54+
AlgorithmRegistry.SigningAlgorithm.PKIX_RSA_PKCS1V15_3072_SHA256,
55+
AlgorithmRegistry.getSigningAlgorithm(publicKey));
56+
}
57+
58+
@Test
59+
public void testGetHashAlgorithm_RSA_4096() throws Exception {
60+
var keyPairGen = KeyPairGenerator.getInstance("RSA");
61+
keyPairGen.initialize(4096);
62+
var publicKey = keyPairGen.generateKeyPair().getPublic();
63+
assertEquals(
64+
AlgorithmRegistry.SigningAlgorithm.PKIX_RSA_PKCS1V15_4096_SHA256,
65+
AlgorithmRegistry.getSigningAlgorithm(publicKey));
66+
}
67+
68+
@Test
69+
public void testGetHashAlgorithm_EC_P256() throws Exception {
70+
var keyPairGen = KeyPairGenerator.getInstance("EC");
71+
keyPairGen.initialize(new ECGenParameterSpec("secp256r1"));
72+
var publicKey = keyPairGen.generateKeyPair().getPublic();
73+
assertEquals(
74+
AlgorithmRegistry.SigningAlgorithm.PKIX_ECDSA_P256_SHA_256,
75+
AlgorithmRegistry.getSigningAlgorithm(publicKey));
76+
}
77+
78+
@Test
79+
public void testGetHashAlgorithm_RSA_UnsupportedLength() throws Exception {
80+
var keyPairGen = KeyPairGenerator.getInstance("RSA");
81+
keyPairGen.initialize(1024);
82+
var publicKey = keyPairGen.generateKeyPair().getPublic();
83+
assertThrows(
84+
UnsupportedAlgorithmException.class,
85+
() -> AlgorithmRegistry.getSigningAlgorithm(publicKey));
86+
}
87+
88+
@Test
89+
public void testGetHashAlgorithm_RSA_NotRsaPublicKey() {
90+
PublicKey mockRsaKey =
91+
new PublicKey() {
92+
@Override
93+
public String getAlgorithm() {
94+
return "RSA";
95+
}
96+
97+
@Override
98+
public String getFormat() {
99+
return null;
100+
}
101+
102+
@Override
103+
public byte[] getEncoded() {
104+
return null;
105+
}
106+
};
107+
assertThrows(
108+
IllegalStateException.class, () -> AlgorithmRegistry.getSigningAlgorithm(mockRsaKey));
109+
}
110+
111+
@Test
112+
public void testGetHashAlgorithm_EC_UnsupportedSize() throws Exception {
113+
var keyPairGen = KeyPairGenerator.getInstance("EC");
114+
keyPairGen.initialize(new ECGenParameterSpec("secp384r1"));
115+
var publicKey = keyPairGen.generateKeyPair().getPublic();
116+
assertThrows(
117+
UnsupportedAlgorithmException.class,
118+
() -> AlgorithmRegistry.getSigningAlgorithm(publicKey));
119+
}
120+
121+
@Test
122+
public void testGetHashAlgorithm_EC_NotEcPublicKey() {
123+
PublicKey mockEcKey =
124+
new PublicKey() {
125+
@Override
126+
public String getAlgorithm() {
127+
return "EC";
128+
}
129+
130+
@Override
131+
public String getFormat() {
132+
return null;
133+
}
134+
135+
@Override
136+
public byte[] getEncoded() {
137+
return null;
138+
}
139+
};
140+
assertThrows(
141+
IllegalStateException.class, () -> AlgorithmRegistry.getSigningAlgorithm(mockEcKey));
142+
}
143+
}

0 commit comments

Comments
 (0)