|
15 | 15 | */
|
16 | 16 | package dev.sigstore.encryption;
|
17 | 17 |
|
18 |
| -import java.io.ByteArrayInputStream; |
19 |
| -import java.io.IOException; |
20 |
| -import java.io.InputStreamReader; |
21 |
| -import java.nio.charset.StandardCharsets; |
22 | 18 | import java.security.KeyFactory;
|
23 | 19 | import java.security.NoSuchAlgorithmException;
|
| 20 | +import java.security.NoSuchProviderException; |
24 | 21 | import java.security.PublicKey;
|
25 | 22 | import java.security.Security;
|
26 | 23 | import java.security.spec.InvalidKeySpecException;
|
27 | 24 | import java.security.spec.RSAPublicKeySpec;
|
28 | 25 | import java.security.spec.X509EncodedKeySpec;
|
29 |
| -import java.util.List; |
30 | 26 | import org.bouncycastle.asn1.ASN1Integer;
|
31 | 27 | import org.bouncycastle.asn1.ASN1Sequence;
|
32 |
| -import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; |
33 | 28 | import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
34 |
| -import org.bouncycastle.openssl.PEMParser; |
35 |
| -import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; |
36 |
| -import org.bouncycastle.util.encoders.DecoderException; |
37 | 29 |
|
38 | 30 | /** For internal use. Key related utility functions. */
|
39 | 31 | public class Keys {
|
40 | 32 |
|
41 |
| - private static final List<String> SUPPORTED_KEY_TYPES = |
42 |
| - List.of("ECDSA", "EC", "RSA", "Ed25519", "EdDSA"); |
43 |
| - |
44 | 33 | static {
|
45 | 34 | Security.addProvider(new BouncyCastleProvider());
|
46 | 35 | }
|
47 | 36 |
|
48 | 37 | /**
|
49 |
| - * Takes a PEM formatted public key in bytes and constructs a {@code PublicKey} with it. |
| 38 | + * Takes a PKIX DER formatted ECDSA public key in bytes and constructs a {@code PublicKey} with |
| 39 | + * it. |
50 | 40 | *
|
51 |
| - * <p>This method supports the follow public key algorithms: RSA, EdDSA, EC. |
| 41 | + * @param contents the public key bytes |
| 42 | + * @return a PublicKey object |
| 43 | + * @throws InvalidKeySpecException if the public key material is invalid |
| 44 | + */ |
| 45 | + public static PublicKey parseEcdsa(byte[] contents) throws InvalidKeySpecException { |
| 46 | + return parse(contents, "ECDSA"); |
| 47 | + } |
| 48 | + |
| 49 | + /** |
| 50 | + * Takes a PKIX DER formatted Ed25519 public key in bytes and constructs a {@code PublicKey} with |
| 51 | + * it. |
52 | 52 | *
|
53 |
| - * @throws InvalidKeySpecException if the PEM does not contain just one public key. |
54 |
| - * @throws NoSuchAlgorithmException if the public key is using an unsupported algorithm. |
| 53 | + * @param contents the public key bytes |
| 54 | + * @return a PublicKey object |
| 55 | + * @throws InvalidKeySpecException if the public key material is invalid |
55 | 56 | */
|
56 |
| - public static PublicKey parsePublicKey(byte[] keyBytes) |
57 |
| - throws InvalidKeySpecException, IOException, NoSuchAlgorithmException { |
58 |
| - try (PEMParser pemParser = |
59 |
| - new PEMParser( |
60 |
| - new InputStreamReader(new ByteArrayInputStream(keyBytes), StandardCharsets.UTF_8))) { |
61 |
| - var keyObj = pemParser.readObject(); // throws DecoderException |
62 |
| - if (keyObj == null) { |
63 |
| - throw new InvalidKeySpecException( |
64 |
| - "sigstore public keys must be only a single PEM encoded public key"); |
65 |
| - } |
66 |
| - JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); |
67 |
| - converter.setProvider(BouncyCastleProvider.PROVIDER_NAME); |
68 |
| - if (keyObj instanceof SubjectPublicKeyInfo) { |
69 |
| - PublicKey pk = converter.getPublicKey((SubjectPublicKeyInfo) keyObj); |
70 |
| - if (!SUPPORTED_KEY_TYPES.contains(pk.getAlgorithm())) { |
71 |
| - throw new NoSuchAlgorithmException("Unsupported key type: " + pk.getAlgorithm()); |
72 |
| - } |
73 |
| - return pk; |
74 |
| - } |
75 |
| - throw new InvalidKeySpecException("Could not parse PEM section into public key"); |
76 |
| - } catch (DecoderException e) { |
77 |
| - throw new InvalidKeySpecException("Invalid key, could not parse PEM section"); |
78 |
| - } |
| 57 | + public static PublicKey parseEd25519(byte[] contents) throws InvalidKeySpecException { |
| 58 | + return parse(contents, "Ed25519"); |
79 | 59 | }
|
80 | 60 |
|
81 | 61 | /**
|
82 |
| - * Takes a PKIX DER formatted public key in bytes and constructs a {@code PublicKey} with it. |
| 62 | + * Takes a PKIX DER formatted RSA public key in bytes and constructs a {@code PublicKey} with it. |
83 | 63 | *
|
84 |
| - * <p>This method is known to work with keys algorithms: RSA, EdDSA, EC. |
| 64 | + * @param contents the public key bytes |
| 65 | + * @return a PublicKey object |
| 66 | + * @throws InvalidKeySpecException if the public key material is invalid |
| 67 | + */ |
| 68 | + public static PublicKey parseRsa(byte[] contents) throws InvalidKeySpecException { |
| 69 | + return parse(contents, "RSA"); |
| 70 | + } |
| 71 | + |
| 72 | + /** |
| 73 | + * Takes a PKCS1 DER formatted RSA public key in bytes and constructs a {@code PublicKey} with it. |
85 | 74 | *
|
86 | 75 | * @param contents the public key bytes
|
87 |
| - * @param algorithm the key algorithm |
88 | 76 | * @return a PublicKey object
|
89 |
| - * @throws NoSuchAlgorithmException if we don't support the scheme provided |
90 | 77 | * @throws InvalidKeySpecException if the public key material is invalid
|
91 | 78 | */
|
92 |
| - public static PublicKey parsePkixPublicKey(byte[] contents, String algorithm) |
93 |
| - throws NoSuchAlgorithmException, InvalidKeySpecException { |
94 |
| - X509EncodedKeySpec spec = new X509EncodedKeySpec(contents); |
95 |
| - KeyFactory factory = KeyFactory.getInstance(algorithm); |
96 |
| - return factory.generatePublic(spec); |
| 79 | + public static PublicKey parseRsaPkcs1(byte[] contents) throws InvalidKeySpecException { |
| 80 | + try { |
| 81 | + ASN1Sequence sequence = ASN1Sequence.getInstance(contents); |
| 82 | + ASN1Integer modulus = ASN1Integer.getInstance(sequence.getObjectAt(0)); |
| 83 | + ASN1Integer exponent = ASN1Integer.getInstance(sequence.getObjectAt(1)); |
| 84 | + RSAPublicKeySpec keySpec = |
| 85 | + new RSAPublicKeySpec(modulus.getPositiveValue(), exponent.getPositiveValue()); |
| 86 | + KeyFactory factory = KeyFactory.getInstance("RSA", "BC"); |
| 87 | + return factory.generatePublic(keySpec); |
| 88 | + } catch (IllegalArgumentException e) { |
| 89 | + throw new InvalidKeySpecException("Failed to parse pkcs1 rsa key", e); |
| 90 | + } catch (NoSuchProviderException | NoSuchAlgorithmException e) { |
| 91 | + throw new RuntimeException(e); |
| 92 | + } |
97 | 93 | }
|
98 | 94 |
|
99 |
| - public static PublicKey parsePkcs1RsaPublicKey(byte[] contents) |
100 |
| - throws NoSuchAlgorithmException, InvalidKeySpecException { |
101 |
| - ASN1Sequence sequence = ASN1Sequence.getInstance(contents); |
102 |
| - ASN1Integer modulus = ASN1Integer.getInstance(sequence.getObjectAt(0)); |
103 |
| - ASN1Integer exponent = ASN1Integer.getInstance(sequence.getObjectAt(1)); |
104 |
| - RSAPublicKeySpec keySpec = |
105 |
| - new RSAPublicKeySpec(modulus.getPositiveValue(), exponent.getPositiveValue()); |
106 |
| - KeyFactory factory = KeyFactory.getInstance("RSA"); |
107 |
| - return factory.generatePublic(keySpec); |
| 95 | + private static PublicKey parse(byte[] contents, String type) throws InvalidKeySpecException { |
| 96 | + try { |
| 97 | + var keySpec = new X509EncodedKeySpec(contents); |
| 98 | + var factory = KeyFactory.getInstance(type, BouncyCastleProvider.PROVIDER_NAME); |
| 99 | + return factory.generatePublic(keySpec); |
| 100 | + } catch (NoSuchProviderException | NoSuchAlgorithmException e) { |
| 101 | + throw new RuntimeException(e); |
| 102 | + } |
108 | 103 | }
|
109 | 104 | }
|
0 commit comments