11/*
2- * Copyright (c) 2025 Siemens AG
2+ * Copyright (c) 2023 Siemens AG
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License"); you may
55 * not use this file except in compliance with the License.
4040import com .siemens .pki .cmpracomponent .protection .ProtectionProvider ;
4141import com .siemens .pki .cmpracomponent .protection .SignatureBasedProtection ;
4242import com .siemens .pki .cmpracomponent .util .MessageDumper ;
43- import java .io .ByteArrayInputStream ;
4443import java .io .IOException ;
4544import java .security .KeyPair ;
4645import java .security .PrivateKey ;
4746import java .security .cert .CertificateException ;
48- import java .security .cert .CertificateFactory ;
4947import java .security .cert .X509CRL ;
5048import java .security .cert .X509Certificate ;
5149import java .util .ArrayList ;
6361import org .bouncycastle .asn1 .cmp .CMPObjectIdentifiers ;
6462import org .bouncycastle .asn1 .cmp .CRLSource ;
6563import org .bouncycastle .asn1 .cmp .CRLStatus ;
64+ import org .bouncycastle .asn1 .cmp .CertOrEncCert ;
6665import org .bouncycastle .asn1 .cmp .CertRepMessage ;
6766import org .bouncycastle .asn1 .cmp .CertReqTemplateContent ;
6867import org .bouncycastle .asn1 .cmp .CertResponse ;
7675import org .bouncycastle .asn1 .cmp .PKIStatus ;
7776import org .bouncycastle .asn1 .cmp .RevRepContent ;
7877import org .bouncycastle .asn1 .cmp .RootCaKeyUpdateContent ;
78+ import org .bouncycastle .asn1 .cms .ContentInfo ;
7979import org .bouncycastle .asn1 .cms .EnvelopedData ;
8080import org .bouncycastle .asn1 .crmf .AttributeTypeAndValue ;
8181import org .bouncycastle .asn1 .crmf .CertId ;
8989import org .bouncycastle .asn1 .x509 .GeneralNames ;
9090import org .bouncycastle .asn1 .x509 .SubjectPublicKeyInfo ;
9191import org .bouncycastle .asn1 .x509 .Time ;
92+ import org .bouncycastle .cms .CMSEnvelopedData ;
93+ import org .bouncycastle .cms .CMSException ;
94+ import org .bouncycastle .cms .RecipientInformation ;
95+ import org .bouncycastle .cms .RecipientInformationStore ;
96+ import org .bouncycastle .cms .jcajce .JceKEMEnvelopedRecipient ;
9297import org .bouncycastle .pkcs .PKCS10CertificationRequest ;
9398import org .slf4j .Logger ;
9499import org .slf4j .LoggerFactory ;
95100
96- /** a CMP client implementation */
101+ /**
102+ * a CMP client implementation
103+ *
104+ */
97105public class CmpClient
98106 implements CrlUpdateRetrievalHandler ,
99107 GetCaCertificatesHandler ,
100108 GetCertificateRequestTemplateHandler ,
101109 GetRootCaCertificateUpdateHandler {
102- /** result of an enrollment transaction */
110+ /**
111+ * result of an enrollment transaction
112+ *
113+ */
103114 public interface EnrollmentResult {
104115 /**
105116 * get enrolled certificate
@@ -109,7 +120,8 @@ public interface EnrollmentResult {
109120 X509Certificate getEnrolledCertificate ();
110121
111122 /**
112- * get certificate chain (1st intermediate certificate up to root certificate) of the enrolled certificate
123+ * get certificate chain (1st intermediate certificate up to root certificate)
124+ * of the enrolled certificate
113125 *
114126 * @return the certificate chain of the enrolled certificate
115127 */
@@ -134,11 +146,17 @@ public interface EnrollmentResult {
134146 /**
135147 * ctor
136148 *
137- * @param certProfile certificate profile to be used for enrollment. <code>null</code> if no certificate profile
138- * should be used.
139- * @param upstreamExchange the {@link UpstreamExchange} interface implemented by the wrapping application.
140- * @param upstreamConfiguration configuration for the upstream CMP interface towards the CA
141- * @param clientContext client specific configuration
149+ * @param certProfile certificate profile to be used for enrollment.
150+ * <code>null</code> if no certificate profile
151+ * should be used.
152+ *
153+ * @param upstreamExchange the {@link UpstreamExchange} interface
154+ * implemented by the wrapping application.
155+ *
156+ * @param upstreamConfiguration configuration for the upstream CMP interface
157+ * towards the CA
158+ *
159+ * @param clientContext client specific configuration
142160 * @throws Exception in case of error
143161 */
144162 public CmpClient (
@@ -180,7 +198,9 @@ private Extension fetchSubjectAlternativeName(final X509Certificate cert) {
180198 return ret .length > 0 ? ret [0 ] : null ;
181199 }
182200
183- /** invoke a Get CA certificates GENM request {@inheritDoc} */
201+ /**
202+ * invoke a Get CA certificates GENM request {@inheritDoc}
203+ */
184204 @ Override
185205 public List <X509Certificate > getCaCertificates () {
186206 final PKIBody requestBody = new PKIBody (
@@ -205,7 +225,9 @@ public List<X509Certificate> getCaCertificates() {
205225 return null ;
206226 }
207227
208- /** invoke a Get certificate request template GENM request {@inheritDoc} */
228+ /**
229+ * invoke a Get certificate request template GENM request {@inheritDoc}
230+ */
209231 @ Override
210232 public byte [] getCertificateRequestTemplate () {
211233 final PKIBody requestBody = new PKIBody (
@@ -283,12 +305,11 @@ public List<X509CRL> getCrls(
283305 if (infoValue == null ) {
284306 return null ;
285307 }
286- final CertificateFactory certificateFactory = CertUtility .getCertificateFactory ();
287308 final ASN1Sequence crls = ASN1Sequence .getInstance (infoValue );
288309 final List <X509CRL > ret = new ArrayList <>(crls .size ());
289310 for (final ASN1Encodable aktCrl : crls ) {
290- ret .add (( X509CRL ) certificateFactory . generateCRL ( new ByteArrayInputStream (
291- aktCrl .toASN1Primitive ().getEncoded ()))) ;
311+ ret .add (CertUtility . parseCrl (
312+ aktCrl .toASN1Primitive ().getEncoded ()));
292313 }
293314 return ret ;
294315 }
@@ -302,7 +323,9 @@ public List<X509CRL> getCrls(
302323 return null ;
303324 }
304325
305- /** invoke a Get root CA certificate update GENM request {@inheritDoc} */
326+ /**
327+ * invoke a Get root CA certificate update GENM request {@inheritDoc}
328+ */
306329 @ Override
307330 public RootCaCertificateUpdateResponse getRootCaCertificateUpdate (final X509Certificate oldRootCaCertificate ) {
308331
@@ -479,8 +502,33 @@ public EnrollmentResult invokeEnrollment() {
479502 return null ;
480503 }
481504 final CertifiedKeyPair certifiedKeyPair = certResponse .getCertifiedKeyPair ();
482- final CMPCertificate enrolledCertificate =
483- certifiedKeyPair .getCertOrEncCert ().getCertificate ();
505+ CertOrEncCert certOrEncCert = certifiedKeyPair .getCertOrEncCert ();
506+ CMPCertificate enrolledCertificate = null ;
507+ if (certOrEncCert .hasEncryptedCertificate ()) {
508+ JceKEMEnvelopedRecipient jkr = new JceKEMEnvelopedRecipient (certificateKeypair .getPrivate ());
509+ EnvelopedData envelopedData =
510+ (EnvelopedData ) certOrEncCert .getEncryptedCert ().getValue ();
511+ final CMSEnvelopedData cmsEnvelopedData = new CMSEnvelopedData (
512+ new ContentInfo (envelopedData .getEncryptedContentInfo ().getContentType (), envelopedData ));
513+ final RecipientInformationStore recipients = cmsEnvelopedData .getRecipientInfos ();
514+ for (RecipientInformation recipient : recipients .getRecipients ()) {
515+ // in case of multiple recipients we try until we find a
516+ // recipient fitting our key
517+ try {
518+ byte [] content = recipient .getContent (jkr );
519+ enrolledCertificate = CMPCertificate .getInstance (content );
520+ break ;
521+ } catch (CMSException ex ) {
522+ LOGGER .debug ("unable to decrypt recipient, try next" , ex );
523+ }
524+ }
525+ } else {
526+ enrolledCertificate = certOrEncCert .getCertificate ();
527+ }
528+ if (enrolledCertificate == null ) {
529+ LOGGER .error ("could not extract enrolled certificate from response" );
530+ return null ;
531+ }
484532
485533 if (enrollmentType != PKIBody .TYPE_P10_CERT_REQ && enrolledPrivateKey == null ) {
486534 // central key generation in place, decrypt private key
0 commit comments