88import java .security .KeyFactory ;
99import java .security .KeyStore ;
1010import java .security .PrivateKey ;
11+ import java .security .PublicKey ;
1112import java .security .Signature ;
1213import java .security .cert .Certificate ;
14+ import java .security .cert .CertificateException ;
1315import java .security .cert .CertificateFactory ;
16+ import java .security .interfaces .ECPublicKey ;
17+ import java .security .interfaces .RSAPublicKey ;
1418import java .security .spec .InvalidKeySpecException ;
1519import java .security .spec .PKCS8EncodedKeySpec ;
1620import java .util .Base64 ;
@@ -43,9 +47,24 @@ private static Certificate[] readCertificateChain(Path path) {
4347 private static Certificate [] decodeCertificateChain (byte [] pem , Path source ) {
4448 try {
4549 CertificateFactory factory = CertificateFactory .getInstance ("X.509" );
46- Collection <? extends Certificate > certs =
47- factory .generateCertificates (new ByteArrayInputStream (pem ));
48- return certs .toArray (new Certificate [0 ]);
50+ Collection <? extends Certificate > certs ;
51+ try {
52+ certs = factory .generateCertificates (new ByteArrayInputStream (pem ));
53+ } catch (CertificateException e ) {
54+ // JDK X509Factory throws "No certificate data found" for input with no
55+ // BEGIN CERTIFICATE block. Treat that as an empty chain rather than a parse error.
56+ if (e .getMessage () != null && e .getMessage ().contains ("No certificate data found" )) {
57+ throw new IllegalStateException (
58+ "No certificates found in TLS certificate chain: " + source );
59+ }
60+ throw e ;
61+ }
62+ Certificate [] chain = certs .toArray (new Certificate [0 ]);
63+ if (chain .length == 0 ) {
64+ throw new IllegalStateException (
65+ "No certificates found in TLS certificate chain: " + source );
66+ }
67+ return chain ;
4968 } catch (GeneralSecurityException e ) {
5069 throw new IllegalStateException ("Failed to parse TLS certificate chain from " + source , e );
5170 }
@@ -92,6 +111,7 @@ private static PrivateKey decodePrivateKey(String pem, Path source) {
92111
93112 private static SSLContext buildSslContext (Certificate [] chain , PrivateKey key ) {
94113 verifyKeyMatchesCert (key , chain [0 ]);
114+ requireMinimumStrength (key , chain [0 ]);
95115 try {
96116 KeyStore ks = KeyStore .getInstance ("PKCS12" );
97117 ks .load (null , null );
@@ -106,6 +126,29 @@ private static SSLContext buildSslContext(Certificate[] chain, PrivateKey key) {
106126 }
107127 }
108128
129+ private static void requireMinimumStrength (PrivateKey key , Certificate cert ) {
130+ PublicKey publicKey = cert .getPublicKey ();
131+ switch (publicKey ) {
132+ case RSAPublicKey rsa -> {
133+ int bits = rsa .getModulus ().bitLength ();
134+ if (bits < 2048 ) {
135+ throw new IllegalStateException (
136+ "TLS RSA key below minimum strength: " + bits + " bits (require >= 2048)" );
137+ }
138+ }
139+ case ECPublicKey ec -> {
140+ int bits = ec .getParams ().getCurve ().getField ().getFieldSize ();
141+ if (bits < 256 ) {
142+ throw new IllegalStateException (
143+ "TLS EC key below minimum strength: " + bits + " bits (require >= 256)" );
144+ }
145+ }
146+ default ->
147+ throw new IllegalStateException (
148+ "Unsupported TLS public key algorithm: " + publicKey .getAlgorithm ());
149+ }
150+ }
151+
109152 private static void verifyKeyMatchesCert (PrivateKey key , Certificate cert ) {
110153 String algorithm =
111154 switch (key .getAlgorithm ()) {
0 commit comments