2424import java .util .ArrayList ;
2525import java .util .Collections ;
2626import java .util .List ;
27+ import java .util .Locale ;
2728
29+ import com .fasterxml .jackson .annotation .JsonIgnoreProperties ;
2830import com .fasterxml .jackson .annotation .JsonProperty ;
2931
3032import org .springframework .lang .Nullable ;
3537/**
3638 * Value object representing a certificate bundle consisting of a private key, the
3739 * certificate and the issuer certificate. Certificate and keys can be either DER or PEM
38- * encoded. DER-encoded certificates can be converted to a {@link KeySpec} and
39- * {@link X509Certificate}.
40+ * encoded. RSA and Elliptic Curve keys and certificates can be converted to a
41+ * {@link KeySpec} respective {@link X509Certificate} object. Supports creation of
42+ * {@link #createKeyStore(String) key stores} that contain the key and the certificate
43+ * chain.
4044 *
4145 * @author Mark Paluch
4246 * @author Alex Bremora
4347 * @see #getPrivateKeySpec()
4448 * @see #getX509Certificate()
4549 * @see #getIssuingCaCertificate()
50+ * @see PemObject
4651 */
52+ @ JsonIgnoreProperties (ignoreUnknown = true )
4753public class CertificateBundle extends Certificate {
4854
4955 private final String privateKey ;
@@ -135,26 +141,39 @@ public String getPrivateKeyType() {
135141 }
136142
137143 /**
138- * Retrieve the private key as {@link KeySpec}. Only supported if private key is
139- * DER-encoded.
144+ * @return the required private key type, can be {@literal null}.
145+ * @since 2.4
146+ * @throws IllegalStateException if the private key type is {@literal null}
147+ */
148+ public String getRequiredPrivateKeyType () {
149+
150+ String privateKeyType = getPrivateKeyType ();
151+
152+ if (privateKeyType == null ) {
153+ throw new IllegalStateException ("Private key type is not set" );
154+ }
155+
156+ return privateKeyType ;
157+ }
158+
159+ /**
160+ * Retrieve the private key as {@link KeySpec}.
140161 * @return the private {@link KeySpec}. {@link java.security.KeyFactory} can generate
141162 * a {@link java.security.PrivateKey} from this {@link KeySpec}.
142163 */
143164 public KeySpec getPrivateKeySpec () {
144165
145166 try {
146- byte [] bytes = Base64Utils .decodeFromString (getPrivateKey ());
147- return KeystoreUtil .getRSAPrivateKeySpec (bytes );
167+ return getPrivateKey (getPrivateKey (), getRequiredPrivateKeyType ());
148168 }
149- catch (IOException e ) {
169+ catch (IOException | GeneralSecurityException e ) {
150170 throw new VaultException ("Cannot create KeySpec from private key" , e );
151171 }
152172 }
153173
154174 /**
155175 * Create a {@link KeyStore} from this {@link CertificateBundle} containing the
156- * private key and certificate chain. Only supported if certificate and private key
157- * are DER-encoded.
176+ * private key and certificate chain.
158177 * @param keyAlias the key alias to use.
159178 * @return the {@link KeyStore} containing the private key and certificate chain.
160179 */
@@ -164,8 +183,7 @@ public KeyStore createKeyStore(String keyAlias) {
164183
165184 /**
166185 * Create a {@link KeyStore} from this {@link CertificateBundle} containing the
167- * private key and certificate chain. Only supported if certificate and private key
168- * are DER-encoded.
186+ * private key and certificate chain.
169187 * @param keyAlias the key alias to use.
170188 * @param includeCaChain whether to include the certificate authority chain instead of
171189 * just the issuer certificate.
@@ -197,8 +215,7 @@ public KeyStore createKeyStore(String keyAlias, boolean includeCaChain) {
197215 }
198216
199217 /**
200- * Retrieve the issuing CA certificates as list of {@link X509Certificate}. Only
201- * supported if certificates are DER-encoded.
218+ * Retrieve the issuing CA certificates as list of {@link X509Certificate}.
202219 * @return the issuing CA {@link X509Certificate}.
203220 * @since 2.3.3
204221 */
@@ -208,8 +225,7 @@ public List<X509Certificate> getX509IssuerCertificates() {
208225
209226 for (String data : caChain ) {
210227 try {
211- byte [] bytes = Base64Utils .decodeFromString (data );
212- certificates .add (KeystoreUtil .getCertificate (bytes ));
228+ certificates .addAll (getCertificates (data ));
213229 }
214230 catch (CertificateException e ) {
215231 throw new VaultException ("Cannot create Certificate from issuing CA certificate" , e );
@@ -219,4 +235,41 @@ public List<X509Certificate> getX509IssuerCertificates() {
219235 return certificates ;
220236 }
221237
238+ private static KeySpec getPrivateKey (String privateKey , String keyType )
239+ throws GeneralSecurityException , IOException {
240+
241+ Assert .hasText (privateKey , "Private key must not be empty" );
242+ Assert .hasText (keyType , "Private key type must not be empty" );
243+
244+ if (PemObject .isPemEncoded (privateKey )) {
245+
246+ List <PemObject > pemObjects = PemObject .parse (privateKey );
247+
248+ for (PemObject pemObject : pemObjects ) {
249+
250+ if (pemObject .isPrivateKey ()) {
251+ return getPrivateKey (pemObject .getContent (), keyType );
252+ }
253+ }
254+
255+ throw new IllegalArgumentException ("No private key found in PEM-encoded key spec" );
256+ }
257+
258+ return getPrivateKey (Base64Utils .decodeFromString (privateKey ), keyType );
259+ }
260+
261+ private static KeySpec getPrivateKey (byte [] privateKey , String keyType )
262+ throws GeneralSecurityException , IOException {
263+
264+ switch (keyType .toLowerCase (Locale .ROOT )) {
265+ case "rsa" :
266+ return KeyFactories .RSA_PRIVATE .getKey (privateKey );
267+ case "ec" :
268+ return KeyFactories .EC .getKey (privateKey );
269+ }
270+
271+ throw new IllegalArgumentException (
272+ String .format ("Key type %s not supported. Supported types are: rsa, ec." , keyType ));
273+ }
274+
222275}
0 commit comments