15
15
*/
16
16
package dev .sigstore .encryption ;
17
17
18
- import static org .bouncycastle .jce .ECPointUtil .* ;
18
+ import static org .bouncycastle .jce .ECPointUtil .decodePoint ;
19
19
20
- import com .google .common .annotations .VisibleForTesting ;
21
20
import java .io .ByteArrayInputStream ;
22
21
import java .io .IOException ;
23
22
import java .io .InputStreamReader ;
24
23
import java .nio .charset .StandardCharsets ;
25
- import java .security .*;
26
- import java .security .spec .*;
27
- import java .util .Locale ;
28
- import java .util .logging .Logger ;
24
+ import java .security .KeyFactory ;
25
+ import java .security .NoSuchAlgorithmException ;
26
+ import java .security .NoSuchProviderException ;
27
+ import java .security .PublicKey ;
28
+ import java .security .Security ;
29
+ import java .security .spec .ECPoint ;
30
+ import java .security .spec .ECPublicKeySpec ;
31
+ import java .security .spec .InvalidKeySpecException ;
32
+ import java .security .spec .RSAPublicKeySpec ;
33
+ import java .security .spec .X509EncodedKeySpec ;
34
+ import java .util .List ;
29
35
import org .bouncycastle .asn1 .ASN1Integer ;
30
36
import org .bouncycastle .asn1 .ASN1Sequence ;
31
- import org .bouncycastle .crypto .params .AsymmetricKeyParameter ;
32
- import org .bouncycastle .crypto .params .ECKeyParameters ;
33
- import org .bouncycastle .crypto .params .Ed25519PublicKeyParameters ;
34
- import org .bouncycastle .crypto .params .RSAKeyParameters ;
35
- import org .bouncycastle .crypto .util .PublicKeyFactory ;
37
+ import org .bouncycastle .asn1 .x509 .SubjectPublicKeyInfo ;
36
38
import org .bouncycastle .jce .ECNamedCurveTable ;
37
39
import org .bouncycastle .jce .provider .BouncyCastleProvider ;
38
40
import org .bouncycastle .jce .spec .ECNamedCurveParameterSpec ;
39
41
import org .bouncycastle .jce .spec .ECNamedCurveSpec ;
42
+ import org .bouncycastle .openssl .PEMParser ;
43
+ import org .bouncycastle .openssl .jcajce .JcaPEMKeyConverter ;
40
44
import org .bouncycastle .util .encoders .DecoderException ;
41
- import org .bouncycastle .util .io .pem .PemObject ;
42
- import org .bouncycastle .util .io .pem .PemReader ;
43
45
44
46
/** For internal use. Key related utility functions. */
45
47
public class Keys {
46
48
47
- private static final Logger log = Logger .getLogger (Keys .class .getName ());
49
+ private static final List <String > SUPPORTED_KEY_TYPES =
50
+ List .of ("ECDSA" , "EC" , "RSA" , "Ed25519" , "EdDSA" );
48
51
49
52
static {
50
53
Security .addProvider (new BouncyCastleProvider ());
@@ -60,48 +63,26 @@ public class Keys {
60
63
*/
61
64
public static PublicKey parsePublicKey (byte [] keyBytes )
62
65
throws InvalidKeySpecException , IOException , NoSuchAlgorithmException {
63
- PemReader pemReader =
64
- new PemReader (
65
- new InputStreamReader (new ByteArrayInputStream (keyBytes ), StandardCharsets .UTF_8 ));
66
- PemObject section = null ;
67
- try {
68
- section = pemReader .readPemObject ();
69
- if (pemReader .readPemObject () != null ) {
66
+ try (PEMParser pemParser =
67
+ new PEMParser (
68
+ new InputStreamReader (new ByteArrayInputStream (keyBytes ), StandardCharsets .UTF_8 ))) {
69
+ var keyObj = pemParser .readObject (); // throws DecoderException
70
+ if (keyObj == null ) {
70
71
throw new InvalidKeySpecException (
71
72
"sigstore public keys must be only a single PEM encoded public key" );
72
73
}
74
+ JcaPEMKeyConverter converter = new JcaPEMKeyConverter ();
75
+ if (keyObj instanceof SubjectPublicKeyInfo ) {
76
+ PublicKey pk = converter .getPublicKey ((SubjectPublicKeyInfo ) keyObj );
77
+ if (!SUPPORTED_KEY_TYPES .contains (pk .getAlgorithm ())) {
78
+ throw new NoSuchAlgorithmException ("Unsupported key type: " + pk .getAlgorithm ());
79
+ }
80
+ return pk ;
81
+ }
82
+ throw new InvalidKeySpecException ("Could not parse PEM section into public key" );
73
83
} catch (DecoderException e ) {
74
84
throw new InvalidKeySpecException ("Invalid key, could not parse PEM section" );
75
85
}
76
- // special handling for PKCS1 (rsa) public key
77
- // TODO: The length checking is not necessary after https://github.com/bcgit/bc-java/issues/1370
78
- // has been merged. Remove it when bc-java is updated with the merge.
79
- if ((section == null ) || (section .getContent () == null ) || (section .getContent ().length == 0 )) {
80
- throw new InvalidKeySpecException ("Invalid key, empty PEM section" );
81
- }
82
- if (section .getType ().equals ("RSA PUBLIC KEY" )) {
83
- return parsePkcs1RsaPublicKey (section .getContent ());
84
- }
85
-
86
- // otherwise, we are dealing with PKIX X509 encoded keys
87
- byte [] content = section .getContent ();
88
- EncodedKeySpec publicKeySpec = new X509EncodedKeySpec (content );
89
- AsymmetricKeyParameter keyParameters = null ;
90
-
91
- // Ensure PEM content can be parsed correctly
92
- try {
93
- keyParameters = PublicKeyFactory .createKey (content );
94
- } catch (IllegalStateException e ) {
95
- throw new InvalidKeySpecException ("Invalid key, could not parse PEM content" );
96
- }
97
- if (keyParameters == null ) {
98
- throw new InvalidKeySpecException ("Invalid key, could not parse PEM content" );
99
- }
100
-
101
- // get algorithm inspecting the created class
102
- String keyAlgorithm = extractKeyAlgorithm (keyParameters );
103
- KeyFactory keyFactory = KeyFactory .getInstance (keyAlgorithm );
104
- return keyFactory .generatePublic (publicKeySpec );
105
86
}
106
87
107
88
/**
@@ -184,34 +165,4 @@ public static PublicKey constructTufPublicKey(byte[] contents, String scheme)
184
165
throw new RuntimeException (scheme + " not currently supported" );
185
166
}
186
167
}
187
-
188
- // https://stackoverflow.com/questions/42911637/get-publickey-from-key-bytes-not-knowing-the-key-algorithm
189
- private static String extractKeyAlgorithm (AsymmetricKeyParameter keyParameters )
190
- throws NoSuchAlgorithmException {
191
- if (keyParameters instanceof RSAKeyParameters ) {
192
- return "RSA" ;
193
- } else if (keyParameters instanceof Ed25519PublicKeyParameters ) {
194
- return "EdDSA" ;
195
- } else if (keyParameters instanceof ECKeyParameters ) {
196
- return "EC" ;
197
- } else {
198
- String error =
199
- String .format (
200
- Locale .ROOT ,
201
- "The key provided was of type: %s. We only support RSA, EdDSA, and EC " ,
202
- keyParameters );
203
- log .warning (error );
204
- throw new NoSuchAlgorithmException (error );
205
- }
206
- }
207
-
208
- @ VisibleForTesting
209
- protected static int getJavaVersion () {
210
- return getJavaVersion (System .getProperty ("java.version" ));
211
- }
212
-
213
- @ VisibleForTesting
214
- protected static int getJavaVersion (String version ) {
215
- return Integer .parseInt (version .substring (0 , version .indexOf ("." )));
216
- }
217
168
}
0 commit comments