From 70d854cc60b941820727d93ffa0ea7297faa52d6 Mon Sep 17 00:00:00 2001 From: Andreas Kretschmer Date: Wed, 8 Nov 2023 10:22:33 +0100 Subject: [PATCH 1/5] add recipient to CmpMessgeInterface config --- .../configuration/ClientContext.java | 9 ---- .../configuration/CmpMessageInterface.java | 45 +++++++++++-------- .../configuration/NestedEndpointContext.java | 13 ++++-- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/siemens/pki/cmpclientcomponent/configuration/ClientContext.java b/src/main/java/com/siemens/pki/cmpclientcomponent/configuration/ClientContext.java index 2691c2e2..06b58b6e 100644 --- a/src/main/java/com/siemens/pki/cmpclientcomponent/configuration/ClientContext.java +++ b/src/main/java/com/siemens/pki/cmpclientcomponent/configuration/ClientContext.java @@ -30,15 +30,6 @@ public interface ClientContext { */ EnrollmentContext getEnrollmentContext(); - /** - * CMP message recipient or null if NULL_DN should be used - * - * @return CMP message recipient - */ - default String getRecipient() { - return null; - } - /** * get revocation specific configuration * diff --git a/src/main/java/com/siemens/pki/cmpracomponent/configuration/CmpMessageInterface.java b/src/main/java/com/siemens/pki/cmpracomponent/configuration/CmpMessageInterface.java index 89832789..06181ecb 100644 --- a/src/main/java/com/siemens/pki/cmpracomponent/configuration/CmpMessageInterface.java +++ b/src/main/java/com/siemens/pki/cmpracomponent/configuration/CmpMessageInterface.java @@ -23,6 +23,24 @@ */ public interface CmpMessageInterface { + /** + * the {@link ReprotectMode} controls how an outgoing message is protected + */ + enum ReprotectMode { + /** + * the outgoing message will be reprotected in any case. + */ + reprotect, + /** + * any protection is removed from the outgoing message + */ + strip, + /** + * an existing protection of a forwarded message is preserved, if possible + */ + keep + } + /** * configure trust for protection validation of incoming messages * @@ -48,6 +66,15 @@ public interface CmpMessageInterface { */ CredentialContext getOutputCredentials(); + /** + * allow to set new recipient for outgoing messages. The recipient is only updated if no or + * a new protection will be applied. + * @return new recipient in RDN notation or null if already set recipient should remain. + */ + default String getRecipient() { + return null; + } + /** * provide configuration for protection mode of outgoing messages * @@ -82,22 +109,4 @@ public interface CmpMessageInterface { * @return allowed time offset in seconds */ boolean isMessageTimeDeviationAllowed(long deviation); - - /** - * the {@link ReprotectMode} controls how an outgoing message is protected - */ - enum ReprotectMode { - /** - * the outgoing message will be reprotected in any case. - */ - reprotect, - /** - * any protection is removed from the outgoing message - */ - strip, - /** - * an existing protection of a forwarded message is preserved, if possible - */ - keep - } } diff --git a/src/main/java/com/siemens/pki/cmpracomponent/configuration/NestedEndpointContext.java b/src/main/java/com/siemens/pki/cmpracomponent/configuration/NestedEndpointContext.java index 36fe8da6..d033bcc2 100644 --- a/src/main/java/com/siemens/pki/cmpracomponent/configuration/NestedEndpointContext.java +++ b/src/main/java/com/siemens/pki/cmpracomponent/configuration/NestedEndpointContext.java @@ -35,12 +35,19 @@ public interface NestedEndpointContext { /** * configure protection for outgoing messages * - * @return a protection configuration, if outgoing message should be wrapped in - * nested messages or null if outgoing messaged shouldn't - * be wrapped. + * @return a protection configuration for the wrapping message or null if the wrapping message shouldn't + * be protected. */ CredentialContext getOutputCredentials(); + /** + * allow to set new recipient for outgoing nested messages. + * @return new recipient in RDN notation or null if the recipient of the wrapped message should be used. + */ + default String getRecipient() { + return null; + } + /** * configure handling of incoming nested messages per recipient. * From 441b42b95d50e16e007c504b952e7704f94097ec Mon Sep 17 00:00:00 2001 From: Andreas Kretschmer Date: Wed, 8 Nov 2023 12:24:03 +0100 Subject: [PATCH 2/5] implement configurable recipient --- CHANGELOG.md | 4 + pom.xml | 2 +- .../main/ClientRequestHandler.java | 80 ++-- .../cmpclientcomponent/main/CmpClient.java | 8 +- .../configuration/CmpMessageInterface.java | 2 +- .../configuration/NestedEndpointContext.java | 2 +- .../MsgOutputProtector.java | 155 +++++-- .../msggeneration/PkiMessageGenerator.java | 44 +- .../msgprocessing/CmpRaUpstream.java | 41 +- .../msgprocessing/P10X509RaUpstream.java | 4 +- .../msgprocessing/RaDownstream.java | 12 +- .../msgprocessing/ServiceImplementation.java | 95 ++-- .../test/CmpClientTestcaseBase.java | 145 +++--- ...tCentralKeyGenerationWithKeyTransport.java | 4 +- .../TestCrWithConfiguredClientRecipient.java | 121 +++++ .../test/TestCrWithConfiguredRaRecipient.java | 412 ++++++++++++++++++ .../test/framework/CmpCaMock.java | 183 ++++---- 17 files changed, 967 insertions(+), 347 deletions(-) rename src/main/java/com/siemens/pki/cmpracomponent/{msgprocessing => msggeneration}/MsgOutputProtector.java (63%) create mode 100644 src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCrWithConfiguredClientRecipient.java create mode 100644 src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCrWithConfiguredRaRecipient.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 23e1f585..73f8592e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -119,3 +119,7 @@ feat: provide CMP client implementation ### 3.0.1 (Nov 07 2023) fix: update some dependencies + +### 4.0.0 (Nov 8 2023) + +feat: implement configurable recipient diff --git a/pom.xml b/pom.xml index ad8563eb..3822cf1c 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ com.siemens.pki CmpRaComponent jar - 3.0.1 + 4.0.0 UTF-8 . diff --git a/src/main/java/com/siemens/pki/cmpclientcomponent/main/ClientRequestHandler.java b/src/main/java/com/siemens/pki/cmpclientcomponent/main/ClientRequestHandler.java index 281225ba..05db0f48 100644 --- a/src/main/java/com/siemens/pki/cmpclientcomponent/main/ClientRequestHandler.java +++ b/src/main/java/com/siemens/pki/cmpclientcomponent/main/ClientRequestHandler.java @@ -21,21 +21,20 @@ import com.siemens.pki.cmpclientcomponent.configuration.ClientContext; import com.siemens.pki.cmpracomponent.configuration.CmpMessageInterface; -import com.siemens.pki.cmpracomponent.configuration.CredentialContext; import com.siemens.pki.cmpracomponent.configuration.NestedEndpointContext; import com.siemens.pki.cmpracomponent.configuration.VerificationContext; import com.siemens.pki.cmpracomponent.cryptoservices.CertUtility; import com.siemens.pki.cmpracomponent.main.CmpRaComponent.UpstreamExchange; import com.siemens.pki.cmpracomponent.msggeneration.HeaderProvider; +import com.siemens.pki.cmpracomponent.msggeneration.MsgOutputProtector; import com.siemens.pki.cmpracomponent.msggeneration.PkiMessageGenerator; import com.siemens.pki.cmpracomponent.msgvalidation.BaseCmpException; +import com.siemens.pki.cmpracomponent.msgvalidation.CmpProcessingException; import com.siemens.pki.cmpracomponent.msgvalidation.CmpValidationException; import com.siemens.pki.cmpracomponent.msgvalidation.MessageBodyValidator; import com.siemens.pki.cmpracomponent.msgvalidation.MessageHeaderValidator; import com.siemens.pki.cmpracomponent.msgvalidation.ProtectionValidator; import com.siemens.pki.cmpracomponent.msgvalidation.ValidatorIF; -import com.siemens.pki.cmpracomponent.protection.ProtectionProvider; -import com.siemens.pki.cmpracomponent.protection.ProtectionProviderFactory; import com.siemens.pki.cmpracomponent.util.FileTracer; import com.siemens.pki.cmpracomponent.util.MessageDumper; import java.security.GeneralSecurityException; @@ -57,7 +56,6 @@ import org.bouncycastle.asn1.cmp.PKIMessages; import org.bouncycastle.asn1.cmp.PKIStatus; import org.bouncycastle.asn1.cmp.PollRepContent; -import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.GeneralName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -71,7 +69,7 @@ class ClientRequestHandler { class ValidatorAndProtector { - private final ProtectionProvider outputProtection; + private final MsgOutputProtector outputProtection; private final ProtectionValidator protectionValidator; @@ -81,46 +79,31 @@ class ValidatorAndProtector { private final VerificationContext inputVerification; - public ValidatorAndProtector(NestedEndpointContext nestedEndpoint) throws GeneralSecurityException { - this( - NESTED_INTERFACE_NAME, - nestedEndpoint.getInputVerification(), - nestedEndpoint.getOutputCredentials(), - null); + public ValidatorAndProtector(NestedEndpointContext nestedEndpoint) + throws GeneralSecurityException, CmpProcessingException { + final VerificationContext inputVerification = nestedEndpoint.getInputVerification(); + headerValidator = new MessageHeaderValidator(NESTED_INTERFACE_NAME); + outputProtection = new MsgOutputProtector(nestedEndpoint, NESTED_INTERFACE_NAME); + this.inputVerification = inputVerification; + protectionValidator = new ProtectionValidator(NESTED_INTERFACE_NAME, inputVerification); + bodyValidator = new MessageBodyValidator(NESTED_INTERFACE_NAME, (x, y) -> false, null, certProfile); } private ValidatorAndProtector(String certProfile, final CmpMessageInterface upstreamConfiguration) - throws GeneralSecurityException { - this( - INTERFACE_NAME, - upstreamConfiguration.getInputVerification(), - upstreamConfiguration.getOutputCredentials(), - upstreamConfiguration); - } - - private ValidatorAndProtector( - String intefaceName, - VerificationContext inputVerification, - CredentialContext outputCredentials, - CmpMessageInterface upstreamConfiguration) - throws GeneralSecurityException { - headerValidator = new MessageHeaderValidator(intefaceName); - outputProtection = ProtectionProviderFactory.createProtectionProvider(outputCredentials); - this.inputVerification = inputVerification; - protectionValidator = new ProtectionValidator(intefaceName, inputVerification); - if (upstreamConfiguration != null) { - bodyValidator = - new MessageBodyValidator(intefaceName, (x, y) -> false, upstreamConfiguration, certProfile); - } else { - bodyValidator = DUMMY_VALIDATOR; - } + throws GeneralSecurityException, CmpProcessingException { + this.inputVerification = upstreamConfiguration.getInputVerification(); + headerValidator = new MessageHeaderValidator(INTERFACE_NAME); + outputProtection = new MsgOutputProtector(upstreamConfiguration, INTERFACE_NAME, null); + protectionValidator = new ProtectionValidator(INTERFACE_NAME, inputVerification); + bodyValidator = + new MessageBodyValidator(INTERFACE_NAME, (x, y) -> false, upstreamConfiguration, certProfile); } public VerificationContext getInputVerification() { return inputVerification; } - public ProtectionProvider getOutputProtection() { + public MsgOutputProtector getOutputProtection() { return outputProtection; } @@ -132,8 +115,6 @@ private void validateResponse(final PKIMessage response) throws BaseCmpException } } - private static final ValidatorIF DUMMY_VALIDATOR = messageToValidate -> null; - private static final int DEFAULT_PVNO = PKIHeader.CMP_2000; private static final String INTERFACE_NAME = "ClientUpstream"; @@ -147,8 +128,6 @@ private void validateResponse(final PKIMessage response) throws BaseCmpException private final UpstreamExchange upstreamExchange; - private final GeneralName recipient; - private final String certProfile; private final ValidatorAndProtector nestedValidatorAndProtector; @@ -165,16 +144,15 @@ private void validateResponse(final PKIMessage response) throws BaseCmpException * towards the CA * * @param clientContext client specific configuration - * @throws GeneralSecurityException + * @throws Exception */ ClientRequestHandler( String certProfile, final UpstreamExchange upstreamExchange, final CmpMessageInterface upstreamConfiguration, final ClientContext clientContext) - throws GeneralSecurityException { + throws Exception { this.upstreamExchange = upstreamExchange; - recipient = ifNotNull(clientContext.getRecipient(), r -> new GeneralName(new X500Name(r))); this.certProfile = certProfile; validatorAndProtector = new ValidatorAndProtector(certProfile, upstreamConfiguration); nestedValidatorAndProtector = @@ -234,7 +212,7 @@ public int getPvno() { @Override public GeneralName getRecipient() { - return recipient; + return null; } @Override @@ -257,15 +235,14 @@ public ASN1OctetString getTransactionID() { return transactionId; } }; - return PkiMessageGenerator.generateAndProtectMessage( - headerProvider, validatorAndProtector.getOutputProtection(), body); + return validatorAndProtector.getOutputProtection().createOutgoingMessage(headerProvider, body); } public VerificationContext getInputVerification() { return validatorAndProtector.getInputVerification(); } - public ProtectionProvider getOutputProtection() { + public MsgOutputProtector getOutputProtection() { return validatorAndProtector.getOutputProtection(); } @@ -307,10 +284,11 @@ PKIBody sendReceiveInitialBody(final PKIBody body, final boolean withImplicitCon PKIMessage sendReceiveValidateMessage(PKIMessage request, final int firstRequestType) throws Exception { if (nestedValidatorAndProtector != null) { - request = PkiMessageGenerator.generateAndProtectMessage( - PkiMessageGenerator.buildForwardingHeaderProvider(request), - nestedValidatorAndProtector.getOutputProtection(), - new PKIBody(PKIBody.TYPE_NESTED, new PKIMessages(request))); + request = nestedValidatorAndProtector + .getOutputProtection() + .createOutgoingMessage( + PkiMessageGenerator.buildForwardingHeaderProvider(request), + new PKIBody(PKIBody.TYPE_NESTED, new PKIMessages(request))); } FileTracer.logMessage(request, INTERFACE_NAME); byte[] rawresponse = upstreamExchange.sendReceiveMessage(request.getEncoded(), certProfile, firstRequestType); diff --git a/src/main/java/com/siemens/pki/cmpclientcomponent/main/CmpClient.java b/src/main/java/com/siemens/pki/cmpclientcomponent/main/CmpClient.java index 6cfbf9a6..088f41b5 100644 --- a/src/main/java/com/siemens/pki/cmpclientcomponent/main/CmpClient.java +++ b/src/main/java/com/siemens/pki/cmpclientcomponent/main/CmpClient.java @@ -41,7 +41,6 @@ import com.siemens.pki.cmpracomponent.util.MessageDumper; import java.io.ByteArrayInputStream; import java.io.IOException; -import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.PrivateKey; import java.security.cert.CertificateException; @@ -149,14 +148,14 @@ public interface EnrollmentResult { * towards the CA * * @param clientContext client specific configuration - * @throws GeneralSecurityException in case of error + * @throws Exception in case of error */ public CmpClient( String certProfile, final UpstreamExchange upstreamExchange, final CmpMessageInterface upstreamConfiguration, final ClientContext clientContext) - throws GeneralSecurityException { + throws Exception { requestHandler = new ClientRequestHandler(certProfile, upstreamExchange, upstreamConfiguration, clientContext); this.clientContext = clientContext; } @@ -498,7 +497,8 @@ public EnrollmentResult invokeEnrollment() { if (enrollmentType != PKIBody.TYPE_P10_CERT_REQ && enrolledPrivateKey == null) { // central key generation in place, decrypt private key CmsDecryptor decryptor = null; - final ProtectionProvider outputProtection = requestHandler.getOutputProtection(); + final ProtectionProvider outputProtection = + requestHandler.getOutputProtection().getProtector(); if (outputProtection instanceof SignatureBasedProtection) { final SignatureBasedProtection sigProtector = (SignatureBasedProtection) outputProtection; decryptor = new CmsDecryptor(sigProtector.getEndCertificate(), sigProtector.getPrivateKey(), null); diff --git a/src/main/java/com/siemens/pki/cmpracomponent/configuration/CmpMessageInterface.java b/src/main/java/com/siemens/pki/cmpracomponent/configuration/CmpMessageInterface.java index 06181ecb..b5de3229 100644 --- a/src/main/java/com/siemens/pki/cmpracomponent/configuration/CmpMessageInterface.java +++ b/src/main/java/com/siemens/pki/cmpracomponent/configuration/CmpMessageInterface.java @@ -72,7 +72,7 @@ enum ReprotectMode { * @return new recipient in RDN notation or null if already set recipient should remain. */ default String getRecipient() { - return null; + return null; } /** diff --git a/src/main/java/com/siemens/pki/cmpracomponent/configuration/NestedEndpointContext.java b/src/main/java/com/siemens/pki/cmpracomponent/configuration/NestedEndpointContext.java index d033bcc2..0d887f31 100644 --- a/src/main/java/com/siemens/pki/cmpracomponent/configuration/NestedEndpointContext.java +++ b/src/main/java/com/siemens/pki/cmpracomponent/configuration/NestedEndpointContext.java @@ -45,7 +45,7 @@ public interface NestedEndpointContext { * @return new recipient in RDN notation or null if the recipient of the wrapped message should be used. */ default String getRecipient() { - return null; + return null; } /** diff --git a/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/MsgOutputProtector.java b/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/MsgOutputProtector.java similarity index 63% rename from src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/MsgOutputProtector.java rename to src/main/java/com/siemens/pki/cmpracomponent/msggeneration/MsgOutputProtector.java index 62c434f4..5d87f03b 100644 --- a/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/MsgOutputProtector.java +++ b/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/MsgOutputProtector.java @@ -15,26 +15,32 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package com.siemens.pki.cmpracomponent.msgprocessing; +package com.siemens.pki.cmpracomponent.msggeneration; import static com.siemens.pki.cmpracomponent.util.NullUtil.defaultIfNull; +import static com.siemens.pki.cmpracomponent.util.NullUtil.ifNotNull; import com.siemens.pki.cmpracomponent.configuration.CmpMessageInterface; import com.siemens.pki.cmpracomponent.configuration.CmpMessageInterface.ReprotectMode; import com.siemens.pki.cmpracomponent.configuration.CredentialContext; -import com.siemens.pki.cmpracomponent.msggeneration.HeaderProvider; -import com.siemens.pki.cmpracomponent.msggeneration.PkiMessageGenerator; +import com.siemens.pki.cmpracomponent.configuration.NestedEndpointContext; import com.siemens.pki.cmpracomponent.msgvalidation.CmpProcessingException; import com.siemens.pki.cmpracomponent.persistency.PersistencyContext; import com.siemens.pki.cmpracomponent.protection.ProtectionProvider; import com.siemens.pki.cmpracomponent.protection.ProtectionProviderFactory; import java.security.GeneralSecurityException; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; import java.util.stream.Stream; import org.bouncycastle.asn1.cmp.CMPCertificate; import org.bouncycastle.asn1.cmp.PKIBody; import org.bouncycastle.asn1.cmp.PKIFailureInfo; import org.bouncycastle.asn1.cmp.PKIMessage; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.GeneralName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,7 +50,7 @@ */ public class MsgOutputProtector { - private static final CMPCertificate[] EMPTY_CERTIFCATE_ARRAY = new CMPCertificate[0]; + private static final CMPCertificate[] EMPTY_CERTIFCATE_ARRAY = {}; private static final Logger LOGGER = LoggerFactory.getLogger(MsgOutputProtector.class); @@ -52,22 +58,27 @@ public class MsgOutputProtector { private final ProtectionProvider protector; private final PersistencyContext persistencyContext; - private final CmpMessageInterface config; + + private final boolean suppressRedundantExtraCerts; + + private final GeneralName recipient; /** + * ctor * @param config specific configuration - * @param interfaceName + * @param interfaceName name of interface used in logging messages * @param persistencyContext reference to transaction specific * {@link PersistencyContext} * @throws CmpProcessingException in case of inconsistent configuration * @throws GeneralSecurityException in case of broken configuration */ - MsgOutputProtector( + public MsgOutputProtector( final CmpMessageInterface config, final String interfaceName, final PersistencyContext persistencyContext) throws CmpProcessingException, GeneralSecurityException { this.persistencyContext = persistencyContext; - this.config = config; + suppressRedundantExtraCerts = config.getSuppressRedundantExtraCerts(); reprotectMode = config.getReprotectMode(); + recipient = ifNotNull(config.getRecipient(), rec -> new GeneralName(new X500Name(rec))); final CredentialContext outputCredentials = config.getOutputCredentials(); if (reprotectMode == ReprotectMode.reprotect && outputCredentials == null) { throw new CmpProcessingException( @@ -78,67 +89,87 @@ public class MsgOutputProtector { protector = ProtectionProviderFactory.createProtectionProvider(outputCredentials); } - private synchronized PKIMessage stripRedundantExtraCerts(PKIMessage msg) { - if (!config.getSuppressRedundantExtraCerts() || persistencyContext == null) { - return msg; - } - final CMPCertificate[] extraCerts = msg.getExtraCerts(); - if (extraCerts == null || extraCerts.length <= 0) { - LOGGER.debug("no extra certs, no stripping"); - return msg; - } - - final List extraCertsAsList = new LinkedList<>(Arrays.asList(extraCerts)); - final Set alreadySentExtraCerts = persistencyContext.getAlreadySentExtraCerts(); + /** + * ctor + * @param config specific configuration + * @param interfaceName name of interface used in logging messages + * @throws CmpProcessingException in case of inconsistent configuration + * @throws GeneralSecurityException in case of broken configuration + */ + public MsgOutputProtector(final NestedEndpointContext config, final String interfaceName) + throws CmpProcessingException, GeneralSecurityException { + this.persistencyContext = null; + suppressRedundantExtraCerts = false; + reprotectMode = ReprotectMode.reprotect; + recipient = ifNotNull(config.getRecipient(), rec -> new GeneralName(new X500Name(rec))); + protector = ProtectionProviderFactory.createProtectionProvider(config.getOutputCredentials()); + } - if (extraCertsAsList.removeAll(alreadySentExtraCerts)) { - // were able to drop some extra certs - if (LOGGER.isDebugEnabled()) { - // avoid unnecessary string processing, if debug isn't enabled - LOGGER.debug("drop from " + msg.getExtraCerts().length + " to " + extraCertsAsList.size()); - } - msg = new PKIMessage( - msg.getHeader(), - msg.getBody(), - msg.getProtection(), - extraCertsAsList.isEmpty() - ? null - : extraCertsAsList.toArray(new CMPCertificate[extraCertsAsList.size()])); + /** + * generate and protect a request + * @param headerProvider the header to use + * @param body body of new message + * @return new message + * @throws Exception in case of error + */ + public PKIMessage createOutgoingMessage(final HeaderProvider headerProvider, PKIBody body) throws Exception { + switch (reprotectMode) { + case reprotect: + case keep: + return stripRedundantExtraCerts(PkiMessageGenerator.generateAndProtectMessage( + headerProvider, protector, recipient, body, null)); + case strip: + return PkiMessageGenerator.generateAndProtectMessage( + headerProvider, ProtectionProvider.NO_PROTECTION, recipient, body, null); + default: + throw new IllegalArgumentException("internal error: invalid reprotectMode mode"); } - alreadySentExtraCerts.addAll(extraCertsAsList); - return msg; } /** - * generate and protect a new message + * generate and protect a response to a request * - * @param headerProvider header of new message - * @param body body of new message + * @param request request to answer + * @param body body of new message * @return new message * @throws Exception in case of error */ - PKIMessage generateAndProtectMessage(final HeaderProvider headerProvider, final PKIBody body) throws Exception { - return stripRedundantExtraCerts(PkiMessageGenerator.generateAndProtectMessage(headerProvider, protector, body)); + public PKIMessage generateAndProtectResponseTo(PKIMessage request, final PKIBody body) throws Exception { + return stripRedundantExtraCerts(PkiMessageGenerator.generateAndProtectMessage( + PkiMessageGenerator.buildRespondingHeaderProvider(request), protector, recipient, body, null)); + } + /** + * get used ProtectionProvider + * @return ProtectionProvider + */ + public ProtectionProvider getProtector() { + return protector; } /** - * protect and forward a PKI message + * protect a PKI message before sending out * - * @param in message to forward + * @param in message to send * @param issuingChain trust chain of issued certificate to add to extracerts or * null - * @return protected message + * @return protected message ready to send * @throws Exception in case of processing error */ - PKIMessage protectAndForwardMessage(final PKIMessage in, final List issuingChain) throws Exception { + public PKIMessage protectOutgoingMessage(final PKIMessage in, final List issuingChain) + throws Exception { switch (reprotectMode) { case reprotect: return stripRedundantExtraCerts(PkiMessageGenerator.generateAndProtectMessage( - PkiMessageGenerator.buildForwardingHeaderProvider(in), protector, in.getBody(), issuingChain)); + PkiMessageGenerator.buildForwardingHeaderProvider(in), + protector, + recipient, + in.getBody(), + issuingChain)); case strip: return PkiMessageGenerator.generateAndProtectMessage( PkiMessageGenerator.buildForwardingHeaderProvider(in), ProtectionProvider.NO_PROTECTION, + recipient, in.getBody(), issuingChain); case keep: @@ -147,6 +178,7 @@ PKIMessage protectAndForwardMessage(final PKIMessage in, final List extraCertsAsList = new LinkedList<>(Arrays.asList(extraCerts)); + final Set alreadySentExtraCerts = persistencyContext.getAlreadySentExtraCerts(); + + if (extraCertsAsList.removeAll(alreadySentExtraCerts)) { + // were able to drop some extra certs + if (LOGGER.isDebugEnabled()) { + // avoid unnecessary string processing, if debug isn't enabled + LOGGER.debug("drop from " + msg.getExtraCerts().length + " to " + extraCertsAsList.size()); + } + msg = new PKIMessage( + msg.getHeader(), + msg.getBody(), + msg.getProtection(), + extraCertsAsList.isEmpty() + ? null + : extraCertsAsList.toArray(new CMPCertificate[extraCertsAsList.size()])); + } + alreadySentExtraCerts.addAll(extraCertsAsList); + return msg; + } } diff --git a/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/PkiMessageGenerator.java b/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/PkiMessageGenerator.java index 0844fc13..869fa03b 100644 --- a/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/PkiMessageGenerator.java +++ b/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/PkiMessageGenerator.java @@ -251,21 +251,8 @@ public ASN1OctetString getTransactionID() { * * @param headerProvider PKI header * @param protectionProvider PKI protection - * @param body message body - * @return a fully build and protected message - * @throws Exception in case of error - */ - public static PKIMessage generateAndProtectMessage( - final HeaderProvider headerProvider, final ProtectionProvider protectionProvider, final PKIBody body) - throws Exception { - return generateAndProtectMessage(headerProvider, protectionProvider, body, null); - } - - /** - * generate and protect a new CMP message - * - * @param headerProvider PKI header - * @param protectionProvider PKI protection + * @param newRecipient outgoing recipient or null recipient + * from headerProvider should be used * @param body message body * @param issuingChain chain of enrolled certificate to append at the * extraCerts @@ -275,15 +262,15 @@ public static PKIMessage generateAndProtectMessage( public static PKIMessage generateAndProtectMessage( final HeaderProvider headerProvider, final ProtectionProvider protectionProvider, + GeneralName newRecipient, final PKIBody body, final List issuingChain) throws Exception { synchronized (protectionProvider) { + final GeneralName recipient = computeDefaultIfNull(newRecipient, headerProvider::getRecipient); final GeneralName sender = computeDefaultIfNull(protectionProvider.getSender(), headerProvider::getSender); final PKIHeaderBuilder headerBuilder = new PKIHeaderBuilder( - headerProvider.getPvno(), - defaultIfNull(sender, NULL_DN), - defaultIfNull(headerProvider.getRecipient(), NULL_DN)); + headerProvider.getPvno(), defaultIfNull(sender, NULL_DN), defaultIfNull(recipient, NULL_DN)); headerBuilder.setMessageTime(headerProvider.getMessageTime()); headerBuilder.setProtectionAlg(protectionProvider.getProtectionAlg()); headerBuilder.setSenderKID(protectionProvider.getSenderKID()); @@ -305,6 +292,21 @@ public static PKIMessage generateAndProtectMessage( } } + /** + * generate and protect a new CMP message + * + * @param headerProvider PKI header + * @param protectionProvider PKI protection + * @param body message body + * @return a fully build and protected message + * @throws Exception in case of error + */ + public static PKIMessage generateAndProtectMessage( + final HeaderProvider headerProvider, final ProtectionProvider protectionProvider, final PKIBody body) + throws Exception { + return generateAndProtectMessage(headerProvider, protectionProvider, null, body, null); + } + /** * generate a CertConf body * @@ -518,8 +520,8 @@ public static PKIBody generateRrBody(final X500Name issuer, final ASN1Integer se /** * generate a RR body * - * @param issuer issuer of certificate to revoke - * @param serialNumber serialNumber of certificate to revoke + * @param issuer issuer of certificate to revoke + * @param serialNumber serialNumber of certificate to revoke * @param revocationReason the reason for this revocation * @return generated RR body * @throws IOException in case of ASN.1 processing errors @@ -544,7 +546,7 @@ public static PKIBody generateRrBody(final X500Name issuer, final ASN1Integer se */ public static PKIMessage generateUnprotectMessage(final HeaderProvider headerProvider, final PKIBody body) throws Exception { - return generateAndProtectMessage(headerProvider, ProtectionProvider.NO_PROTECTION, body, null); + return generateAndProtectMessage(headerProvider, ProtectionProvider.NO_PROTECTION, null, body, null); } private PkiMessageGenerator() {} diff --git a/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/CmpRaUpstream.java b/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/CmpRaUpstream.java index 86ee2c6c..98bd512a 100644 --- a/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/CmpRaUpstream.java +++ b/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/CmpRaUpstream.java @@ -18,8 +18,8 @@ package com.siemens.pki.cmpracomponent.msgprocessing; import com.siemens.pki.cmpracomponent.configuration.Configuration; -import com.siemens.pki.cmpracomponent.configuration.CredentialContext; import com.siemens.pki.cmpracomponent.configuration.NestedEndpointContext; +import com.siemens.pki.cmpracomponent.msggeneration.MsgOutputProtector; import com.siemens.pki.cmpracomponent.msggeneration.PkiMessageGenerator; import com.siemens.pki.cmpracomponent.msgvalidation.BaseCmpException; import com.siemens.pki.cmpracomponent.msgvalidation.CmpProcessingException; @@ -27,8 +27,6 @@ import com.siemens.pki.cmpracomponent.msgvalidation.InputValidator; import com.siemens.pki.cmpracomponent.persistency.PersistencyContext; import com.siemens.pki.cmpracomponent.persistency.PersistencyContextManager; -import com.siemens.pki.cmpracomponent.protection.ProtectionProvider; -import com.siemens.pki.cmpracomponent.protection.ProtectionProviderFactory; import com.siemens.pki.cmpracomponent.util.CmpFuncEx; import java.util.Arrays; import java.util.Collection; @@ -83,6 +81,16 @@ class CmpRaUpstream implements RaUpstream { this.upstreamMsgHandler = upstreamExchange; } + void gotResponseAtUpstream(final PKIMessage responseMessage) throws Exception { + final PersistencyContext persistencyContext = persistencyContextManager.loadPersistencyContext( + responseMessage.getHeader().getTransactionID().getOctets()); + if (persistencyContext == null) { + throw new IllegalStateException("no related request known for provided response"); + } + persistencyContext.setPendingDelayedResponse(responseMessage); + persistencyContext.flush(); + } + @Override public PKIMessage handleRequest(final PKIMessage in, final PersistencyContext pesistencyContext) throws BaseCmpException { @@ -145,20 +153,21 @@ public PKIMessage handleRequest(final PKIMessage in, final PersistencyContext pe certProfile, in.getBody().getType()), INTERFACE_NAME, pesistencyContext); - sentMessage = outputProtector.protectAndForwardMessage(in, null); + sentMessage = outputProtector.protectOutgoingMessage(in, null); } final NestedEndpointContext nestedEndpointContext = config.getUpstreamConfiguration( certProfile, in.getBody().getType()) .getNestedEndpointContext(); if (nestedEndpointContext != null) { + final MsgOutputProtector nestedProtector = + new MsgOutputProtector(nestedEndpointContext, "NESTED CMP upstream"); // wrap into nested message - final CredentialContext nestedOutputCredentials = nestedEndpointContext.getOutputCredentials(); - final ProtectionProvider nestedProtector = - ProtectionProviderFactory.createProtectionProvider(nestedOutputCredentials); - sentMessage = PkiMessageGenerator.generateAndProtectMessage( - PkiMessageGenerator.buildForwardingHeaderProvider(sentMessage), - nestedProtector, - new PKIBody(PKIBody.TYPE_NESTED, new PKIMessages(sentMessage))); + sentMessage = nestedProtector.protectOutgoingMessage( + new PKIMessage( + sentMessage.getHeader(), + new PKIBody(PKIBody.TYPE_NESTED, new PKIMessages(sentMessage)), + null), + null); } final PKIMessage receivedMessage = upstreamMsgHandler.apply(sentMessage, certProfile, pesistencyContext.getRequestType()); @@ -197,14 +206,4 @@ public PKIMessage handleRequest(final PKIMessage in, final PersistencyContext pe throw new CmpProcessingException(INTERFACE_NAME, PKIFailureInfo.systemFailure, ex); } } - - void gotResponseAtUpstream(final PKIMessage responseMessage) throws Exception { - final PersistencyContext persistencyContext = persistencyContextManager.loadPersistencyContext( - responseMessage.getHeader().getTransactionID().getOctets()); - if (persistencyContext == null) { - throw new IllegalStateException("no related request known for provided response"); - } - persistencyContext.setPendingDelayedResponse(responseMessage); - persistencyContext.flush(); - } } diff --git a/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/P10X509RaUpstream.java b/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/P10X509RaUpstream.java index dfda3b03..aafa5272 100644 --- a/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/P10X509RaUpstream.java +++ b/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/P10X509RaUpstream.java @@ -22,7 +22,6 @@ import com.siemens.pki.cmpracomponent.msgvalidation.CmpProcessingException; import com.siemens.pki.cmpracomponent.msgvalidation.CmpValidationException; import com.siemens.pki.cmpracomponent.persistency.PersistencyContext; -import com.siemens.pki.cmpracomponent.protection.ProtectionProvider; import com.siemens.pki.cmpracomponent.util.CmpFuncEx; import com.siemens.pki.cmpracomponent.util.MessageDumper; import org.bouncycastle.asn1.cmp.CMPCertificate; @@ -80,9 +79,8 @@ public PKIMessage handleRequest(final PKIMessage in, final PersistencyContext pe INTERFACE_NAME, PKIFailureInfo.systemUnavail, "got no response from upstream"); } - return PkiMessageGenerator.generateAndProtectMessage( + return PkiMessageGenerator.generateUnprotectMessage( PkiMessageGenerator.buildRespondingHeaderProvider(in), - ProtectionProvider.NO_PROTECTION, PkiMessageGenerator.generateIpCpKupBody(PKIBody.TYPE_CERT_REP, responseFromUpstream)); default: throw new CmpValidationException( diff --git a/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/RaDownstream.java b/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/RaDownstream.java index 8da75948..c619db69 100644 --- a/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/RaDownstream.java +++ b/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/RaDownstream.java @@ -34,6 +34,7 @@ import com.siemens.pki.cmpracomponent.cryptoservices.KeyTransportEncryptor; import com.siemens.pki.cmpracomponent.cryptoservices.PasswordEncryptor; import com.siemens.pki.cmpracomponent.cryptoservices.TrustCredentialAdapter; +import com.siemens.pki.cmpracomponent.msggeneration.MsgOutputProtector; import com.siemens.pki.cmpracomponent.msggeneration.PkiMessageGenerator; import com.siemens.pki.cmpracomponent.msgvalidation.BaseCmpException; import com.siemens.pki.cmpracomponent.msgvalidation.CmpEnrollmentException; @@ -385,9 +386,8 @@ PKIMessage handleInputMessage(final PKIMessage in) { .map(this::handleInputMessage) .toArray(PKIMessage[]::new); return getOutputProtector(persistencyContext, PKIBody.TYPE_NESTED) - .generateAndProtectMessage( - PkiMessageGenerator.buildRespondingHeaderProvider(in), - new PKIBody(PKIBody.TYPE_NESTED, new PKIMessages(responses))); + .generateAndProtectResponseTo( + in, new PKIBody(PKIBody.TYPE_NESTED, new PKIMessages(responses))); } } final InputValidator inputValidator = new InputValidator( @@ -418,7 +418,7 @@ PKIMessage handleInputMessage(final PKIMessage in) { issuingChain = null; } return getOutputProtector(persistencyContext, responseBodyType) - .protectAndForwardMessage( + .protectOutgoingMessage( new PKIMessage( responseFromUpstream.getHeader(), responseFromUpstream.getBody(), @@ -429,12 +429,12 @@ PKIMessage handleInputMessage(final PKIMessage in) { final PKIBody errorBody = e.asErrorBody(); responseBodyType = errorBody.getType(); return getOutputProtector(persistencyContext, responseBodyType) - .generateAndProtectMessage(PkiMessageGenerator.buildRespondingHeaderProvider(in), errorBody); + .generateAndProtectResponseTo(in, errorBody); } catch (final RuntimeException ex) { final PKIBody errorBody = new CmpProcessingException(INTERFACE_NAME, ex).asErrorBody(); responseBodyType = errorBody.getType(); return getOutputProtector(persistencyContext, responseBodyType) - .generateAndProtectMessage(PkiMessageGenerator.buildRespondingHeaderProvider(in), errorBody); + .generateAndProtectResponseTo(in, errorBody); } finally { if (persistencyContext != null) { int offset = config.getDownstreamTimeout( diff --git a/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/ServiceImplementation.java b/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/ServiceImplementation.java index 6ea79cc5..852aed69 100644 --- a/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/ServiceImplementation.java +++ b/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/ServiceImplementation.java @@ -19,14 +19,18 @@ import static com.siemens.pki.cmpracomponent.util.NullUtil.ifNotNull; -import com.siemens.pki.cmpracomponent.configuration.*; +import com.siemens.pki.cmpracomponent.configuration.Configuration; +import com.siemens.pki.cmpracomponent.configuration.CrlUpdateRetrievalHandler; +import com.siemens.pki.cmpracomponent.configuration.GetCaCertificatesHandler; +import com.siemens.pki.cmpracomponent.configuration.GetCertificateRequestTemplateHandler; +import com.siemens.pki.cmpracomponent.configuration.GetRootCaCertificateUpdateHandler; import com.siemens.pki.cmpracomponent.configuration.GetRootCaCertificateUpdateHandler.RootCaCertificateUpdateResponse; +import com.siemens.pki.cmpracomponent.configuration.SupportMessageHandlerInterface; import com.siemens.pki.cmpracomponent.cryptoservices.CertUtility; -import com.siemens.pki.cmpracomponent.msggeneration.PkiMessageGenerator; +import com.siemens.pki.cmpracomponent.msggeneration.MsgOutputProtector; import com.siemens.pki.cmpracomponent.msgvalidation.BaseCmpException; import com.siemens.pki.cmpracomponent.msgvalidation.CmpProcessingException; import com.siemens.pki.cmpracomponent.persistency.PersistencyContext; -import com.siemens.pki.cmpracomponent.protection.ProtectionProviderFactory; import java.io.IOException; import java.security.cert.CRLException; import java.security.cert.CertificateException; @@ -34,11 +38,30 @@ import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.List; -import org.bouncycastle.asn1.*; -import org.bouncycastle.asn1.cmp.*; +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.cmp.CMPCertificate; +import org.bouncycastle.asn1.cmp.CMPObjectIdentifiers; +import org.bouncycastle.asn1.cmp.CRLSource; +import org.bouncycastle.asn1.cmp.CRLStatus; +import org.bouncycastle.asn1.cmp.GenMsgContent; +import org.bouncycastle.asn1.cmp.GenRepContent; +import org.bouncycastle.asn1.cmp.InfoTypeAndValue; +import org.bouncycastle.asn1.cmp.PKIBody; +import org.bouncycastle.asn1.cmp.PKIFailureInfo; +import org.bouncycastle.asn1.cmp.PKIMessage; +import org.bouncycastle.asn1.cmp.RootCaKeyUpdateContent; import org.bouncycastle.asn1.x500.RDN; import org.bouncycastle.asn1.x500.X500Name; -import org.bouncycastle.asn1.x509.*; +import org.bouncycastle.asn1.x509.CertificateList; +import org.bouncycastle.asn1.x509.DistributionPointName; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.Time; /** * implementation of a GENM service handler @@ -63,30 +86,6 @@ private String[] generalNamesToStrings(final GeneralNames generalNames) { .toArray(String[]::new); } - private PKIBody handleGetRootCaCertificateUpdate( - final InfoTypeAndValue itav, final GetRootCaCertificateUpdateHandler messageHandler) - throws CertificateException { - final CMPCertificate oldRoot = ifNotNull(itav.getInfoValue(), CMPCertificate::getInstance); - final RootCaCertificateUpdateResponse response = - messageHandler.getRootCaCertificateUpdate(ifNotNull(oldRoot, CertUtility::asX509Certificate)); - if (response != null && response.getNewWithNew() != null) { - final X509Certificate newWithNew = response.getNewWithNew(); - final X509Certificate newWithOld = response.getNewWithOld(); - final X509Certificate oldWithNew = response.getOldWithNew(); - return new PKIBody( - PKIBody.TYPE_GEN_REP, - new GenRepContent(new InfoTypeAndValue( - CMPObjectIdentifiers.id_it_rootCaKeyUpdate, - new RootCaKeyUpdateContent( - ifNotNull(newWithNew, CertUtility::asCmpCertificate), - ifNotNull(newWithOld, CertUtility::asCmpCertificate), - ifNotNull(oldWithNew, CertUtility::asCmpCertificate))))); - } - return new PKIBody( - PKIBody.TYPE_GEN_REP, - new GenRepContent(new InfoTypeAndValue(CMPObjectIdentifiers.id_it_rootCaKeyUpdate))); - } - private PKIBody handleCrlUpdateRetrieval( final InfoTypeAndValue itav, final CrlUpdateRetrievalHandler messageHandler) throws CmpProcessingException { try { @@ -175,6 +174,30 @@ private PKIBody handleGetCertificateRequestTemplate( return new PKIBody(PKIBody.TYPE_GEN_REP, new GenRepContent(new InfoTypeAndValue(infoType))); } + private PKIBody handleGetRootCaCertificateUpdate( + final InfoTypeAndValue itav, final GetRootCaCertificateUpdateHandler messageHandler) + throws CertificateException { + final CMPCertificate oldRoot = ifNotNull(itav.getInfoValue(), CMPCertificate::getInstance); + final RootCaCertificateUpdateResponse response = + messageHandler.getRootCaCertificateUpdate(ifNotNull(oldRoot, CertUtility::asX509Certificate)); + if (response != null && response.getNewWithNew() != null) { + final X509Certificate newWithNew = response.getNewWithNew(); + final X509Certificate newWithOld = response.getNewWithOld(); + final X509Certificate oldWithNew = response.getOldWithNew(); + return new PKIBody( + PKIBody.TYPE_GEN_REP, + new GenRepContent(new InfoTypeAndValue( + CMPObjectIdentifiers.id_it_rootCaKeyUpdate, + new RootCaKeyUpdateContent( + ifNotNull(newWithNew, CertUtility::asCmpCertificate), + ifNotNull(newWithOld, CertUtility::asCmpCertificate), + ifNotNull(oldWithNew, CertUtility::asCmpCertificate))))); + } + return new PKIBody( + PKIBody.TYPE_GEN_REP, + new GenRepContent(new InfoTypeAndValue(CMPObjectIdentifiers.id_it_rootCaKeyUpdate))); + } + protected PKIMessage handleValidatedInputMessage(final PKIMessage msg, final PersistencyContext persistencyContext) throws BaseCmpException { try { @@ -203,12 +226,12 @@ protected PKIMessage handleValidatedInputMessage(final PKIMessage msg, final Per // no specific processing found, return empty response body = new PKIBody(PKIBody.TYPE_GEN_REP, new GenRepContent(new InfoTypeAndValue(infoType))); } - return PkiMessageGenerator.generateAndProtectMessage( - PkiMessageGenerator.buildRespondingHeaderProvider(msg), - ProtectionProviderFactory.createProtectionProvider(config.getDownstreamConfiguration( - ifNotNull(persistencyContext, PersistencyContext::getCertProfile), body.getType()) - .getOutputCredentials()), - body); + final MsgOutputProtector protector = new MsgOutputProtector( + config.getDownstreamConfiguration( + ifNotNull(persistencyContext, PersistencyContext::getCertProfile), body.getType()), + INTERFACE_NAME, + persistencyContext); + return protector.generateAndProtectResponseTo(msg, body); } catch (final BaseCmpException ex) { throw ex; } catch (final Exception e) { diff --git a/src/test/java/com/siemens/pki/cmpclientcomponent/test/CmpClientTestcaseBase.java b/src/test/java/com/siemens/pki/cmpclientcomponent/test/CmpClientTestcaseBase.java index c75bcc05..69bc3320 100644 --- a/src/test/java/com/siemens/pki/cmpclientcomponent/test/CmpClientTestcaseBase.java +++ b/src/test/java/com/siemens/pki/cmpclientcomponent/test/CmpClientTestcaseBase.java @@ -39,7 +39,6 @@ import com.siemens.pki.cmpracomponent.test.framework.SignatureValidationCredentials; import java.io.File; import java.io.IOException; -import java.security.GeneralSecurityException; import java.security.Security; import java.security.cert.X509Certificate; import java.util.Collection; @@ -55,6 +54,65 @@ public class CmpClientTestcaseBase { ConfigFileLoader.setConfigFileBase(CONFIG_DIRECTORY); } + protected static CmpMessageInterface getPasswordBasedUpstreamconfiguration( + SharedSecretCredentialContext protection, SignatureValidationCredentials keyValidationCredentials) { + return new CmpMessageInterface() { + + final PasswordValidationCredentials passwordUpstreamTrust = + new PasswordValidationCredentials(protection.getSharedSecret()); + + @Override + public VerificationContext getInputVerification() { + return new VerificationContext() { + @Override + public Collection getAdditionalCerts() { + return keyValidationCredentials.getAdditionalCerts(); + } + + @Override + public byte[] getSharedSecret(byte[] senderKID) { + return passwordUpstreamTrust.getSharedSecret(senderKID); + } + + @Override + public Collection getTrustedCertificates() { + return keyValidationCredentials.getTrustedCertificates(); + } + }; + } + + @Override + public NestedEndpointContext getNestedEndpointContext() { + return null; + } + + @Override + public CredentialContext getOutputCredentials() { + return protection; + } + + @Override + public ReprotectMode getReprotectMode() { + return ReprotectMode.reprotect; + } + + @Override + public boolean getSuppressRedundantExtraCerts() { + return false; + } + + @Override + public boolean isCacheExtraCerts() { + return false; + } + + @Override + public boolean isMessageTimeDeviationAllowed(final long deviation) { + return deviation < 10; + } + }; + } + protected static CmpMessageInterface getSignatureBasedUpstreamconfiguration(final String upstreamTrustPath) { return new CmpMessageInterface() { @@ -110,9 +168,21 @@ public static void setUpBeforeClass() throws Exception { protected UpstreamExchange upstreamExchange; + protected CmpClient getPasswordBasedCmpClient( + String certProfile, + final ClientContext clientContext, + SharedSecretCredentialContext protection, + SignatureValidationCredentials keyValidationCredentials) + throws Exception { + return new CmpClient( + certProfile, + getUpstreamExchange(), + getPasswordBasedUpstreamconfiguration(protection, keyValidationCredentials), + clientContext); + } + protected CmpClient getSignatureBasedCmpClient( - String certProfile, final ClientContext clientContext, final String upstreamTrustPath) - throws GeneralSecurityException { + String certProfile, final ClientContext clientContext, final String upstreamTrustPath) throws Exception { return new CmpClient( certProfile, getUpstreamExchange(), @@ -183,73 +253,4 @@ protected UpstreamExchange launchP10X509Ra( }; return upstreamExchange; } - - protected CmpClient getPasswordBasedCmpClient( - String certProfile, - final ClientContext clientContext, - SharedSecretCredentialContext protection, - SignatureValidationCredentials keyValidationCredentials) - throws GeneralSecurityException { - return new CmpClient( - certProfile, - getUpstreamExchange(), - getPasswordBasedUpstreamconfiguration(protection, keyValidationCredentials), - clientContext); - } - - protected static CmpMessageInterface getPasswordBasedUpstreamconfiguration( - SharedSecretCredentialContext protection, SignatureValidationCredentials keyValidationCredentials) { - return new CmpMessageInterface() { - - final PasswordValidationCredentials passwordUpstreamTrust = - new PasswordValidationCredentials(protection.getSharedSecret()); - - @Override - public VerificationContext getInputVerification() { - return new VerificationContext() { - public byte[] getSharedSecret(byte[] senderKID) { - return passwordUpstreamTrust.getSharedSecret(senderKID); - } - - public Collection getAdditionalCerts() { - return keyValidationCredentials.getAdditionalCerts(); - } - - public Collection getTrustedCertificates() { - return keyValidationCredentials.getTrustedCertificates(); - } - }; - } - - @Override - public NestedEndpointContext getNestedEndpointContext() { - return null; - } - - @Override - public CredentialContext getOutputCredentials() { - return protection; - } - - @Override - public ReprotectMode getReprotectMode() { - return ReprotectMode.reprotect; - } - - @Override - public boolean getSuppressRedundantExtraCerts() { - return false; - } - - @Override - public boolean isCacheExtraCerts() { - return false; - } - - @Override - public boolean isMessageTimeDeviationAllowed(final long deviation) { - return deviation < 10; - } - }; - } } diff --git a/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCentralKeyGenerationWithKeyTransport.java b/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCentralKeyGenerationWithKeyTransport.java index cdeae2b8..90b2397d 100644 --- a/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCentralKeyGenerationWithKeyTransport.java +++ b/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCentralKeyGenerationWithKeyTransport.java @@ -44,7 +44,6 @@ import com.siemens.pki.cmpracomponent.test.framework.TrustChainAndPrivateKey; import com.siemens.pki.cmpracomponent.util.MessageDumper; import java.math.BigInteger; -import java.security.GeneralSecurityException; import java.security.cert.X509Certificate; import org.bouncycastle.asn1.cmp.PKIBody; import org.junit.Before; @@ -350,8 +349,7 @@ public boolean isRaVerifiedAcceptable(final String certProfile, final int bodyTy @Override protected CmpClient getSignatureBasedCmpClient( - String certProfile, final ClientContext clientContext, final String upstreamTrustPath) - throws GeneralSecurityException { + String certProfile, final ClientContext clientContext, final String upstreamTrustPath) throws Exception { final CmpMessageInterface upstreamconfiguration = new CmpMessageInterface() { final SignatureValidationCredentials upstreamTrust = diff --git a/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCrWithConfiguredClientRecipient.java b/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCrWithConfiguredClientRecipient.java new file mode 100644 index 00000000..41f1991c --- /dev/null +++ b/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCrWithConfiguredClientRecipient.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2023 Siemens AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package com.siemens.pki.cmpclientcomponent.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import com.siemens.pki.cmpclientcomponent.main.CmpClient; +import com.siemens.pki.cmpclientcomponent.main.CmpClient.EnrollmentResult; +import com.siemens.pki.cmpracomponent.configuration.CmpMessageInterface; +import com.siemens.pki.cmpracomponent.configuration.CredentialContext; +import com.siemens.pki.cmpracomponent.configuration.NestedEndpointContext; +import com.siemens.pki.cmpracomponent.configuration.VerificationContext; +import com.siemens.pki.cmpracomponent.main.CmpRaComponent.UpstreamExchange; +import com.siemens.pki.cmpracomponent.test.framework.CmpCaMock; +import com.siemens.pki.cmpracomponent.test.framework.ConfigurationFactory; +import com.siemens.pki.cmpracomponent.test.framework.SignatureValidationCredentials; +import org.bouncycastle.asn1.cmp.PKIBody; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.GeneralName; +import org.junit.Before; +import org.junit.Test; + +public class TestCrWithConfiguredClientRecipient extends EnrollmentTestcaseBase { + + private static final String UPSTREAM_TRUST_PATH = "credentials/CMP_CA_Root.pem"; + + private CmpCaMock cmpCaMock; + + private UpstreamExchange upstreamRa; + + @Before + public void setUp() throws Exception { + cmpCaMock = new CmpCaMock("credentials/ENROLL_Keystore.p12", "credentials/CMP_CA_Keystore.p12"); + upstreamRa = launchCmpRa( + ConfigurationFactory.buildSignatureBasedDownstreamConfiguration(), cmpCaMock::sendReceiveMessage); + } + + @Test + public void testCrWithRecipientByCient() throws Exception { + final EnrollmentResult ret = new CmpClient( + "theCertProfileForOnlineEnrollment", + upstreamRa, + new CmpMessageInterface() { + + final SignatureValidationCredentials upstreamTrust = + new SignatureValidationCredentials(UPSTREAM_TRUST_PATH, null); + + @Override + public VerificationContext getInputVerification() { + return upstreamTrust; + } + + @Override + public NestedEndpointContext getNestedEndpointContext() { + return null; + } + + @Override + public CredentialContext getOutputCredentials() { + try { + return ConfigurationFactory.getEeSignaturebasedCredentials(); + } catch (final Exception e) { + fail(e.getLocalizedMessage()); + return null; + } + } + + @Override + public String getRecipient() { + return "CN=RecipientSetByClient"; + } + + @Override + public ReprotectMode getReprotectMode() { + return ReprotectMode.reprotect; + } + + @Override + public boolean getSuppressRedundantExtraCerts() { + return false; + } + + @Override + public boolean isCacheExtraCerts() { + return false; + } + + @Override + public boolean isMessageTimeDeviationAllowed(final long deviation) { + return deviation < 10; + } + }, + getClientContext( + PKIBody.TYPE_CERT_REQ, + ConfigurationFactory.getKeyGenerator().generateKeyPair(), + null)) + .invokeEnrollment(); + assertNotNull(ret); + assertEquals( + "recipient", + new GeneralName(new X500Name("CN=RecipientSetByClient")), + cmpCaMock.getReceivedRequestAt(1).getHeader().getRecipient()); + } +} diff --git a/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCrWithConfiguredRaRecipient.java b/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCrWithConfiguredRaRecipient.java new file mode 100644 index 00000000..66922e78 --- /dev/null +++ b/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCrWithConfiguredRaRecipient.java @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2023 Siemens AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package com.siemens.pki.cmpclientcomponent.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import com.siemens.pki.cmpclientcomponent.main.CmpClient; +import com.siemens.pki.cmpclientcomponent.main.CmpClient.EnrollmentResult; +import com.siemens.pki.cmpracomponent.configuration.CheckAndModifyResult; +import com.siemens.pki.cmpracomponent.configuration.CkgContext; +import com.siemens.pki.cmpracomponent.configuration.CmpMessageInterface; +import com.siemens.pki.cmpracomponent.configuration.CmpMessageInterface.ReprotectMode; +import com.siemens.pki.cmpracomponent.configuration.Configuration; +import com.siemens.pki.cmpracomponent.configuration.CredentialContext; +import com.siemens.pki.cmpracomponent.configuration.InventoryInterface; +import com.siemens.pki.cmpracomponent.configuration.NestedEndpointContext; +import com.siemens.pki.cmpracomponent.configuration.PersistencyInterface; +import com.siemens.pki.cmpracomponent.configuration.SupportMessageHandlerInterface; +import com.siemens.pki.cmpracomponent.configuration.VerificationContext; +import com.siemens.pki.cmpracomponent.main.CmpRaComponent.UpstreamExchange; +import com.siemens.pki.cmpracomponent.persistency.DefaultPersistencyImplementation; +import com.siemens.pki.cmpracomponent.test.framework.CmpCaMock; +import com.siemens.pki.cmpracomponent.test.framework.ConfigurationFactory; +import com.siemens.pki.cmpracomponent.test.framework.SignatureValidationCredentials; +import com.siemens.pki.cmpracomponent.test.framework.TrustChainAndPrivateKey; +import com.siemens.pki.cmpracomponent.util.MessageDumper; +import java.math.BigInteger; +import org.bouncycastle.asn1.cmp.PKIBody; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.GeneralName; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TestCrWithConfiguredRaRecipient extends EnrollmentTestcaseBase { + + private static final String UPSTREAM_TRUST_PATH = "credentials/CMP_CA_Root.pem"; + + private static final Logger LOGGER = LoggerFactory.getLogger(TestCrWithConfiguredRaRecipient.class); + + private static Configuration buildRaConfigurationWithRecipient( + final CredentialContext downstreamCredentials, + ReprotectMode reprotectMode, + final VerificationContext downstreamTrust, + final CredentialContext upstreamCredentials, + final VerificationContext upstreamTrust, + final SignatureValidationCredentials enrollmentTrust) { + return new Configuration() { + PersistencyInterface persistency = new DefaultPersistencyImplementation(5000); + + @Override + public CkgContext getCkgConfiguration(final String certProfile, final int bodyType) { + fail(String.format( + "getCkgConfiguration called with certprofile: {}, type: {}", + certProfile, + MessageDumper.msgTypeAsString(bodyType))); + return null; + } + + @Override + public CmpMessageInterface getDownstreamConfiguration(final String certProfile, final int bodyType) { + LOGGER.debug( + "getDownstreamConfiguration called with certprofile: {}, type: {}", + certProfile, + MessageDumper.msgTypeAsString(bodyType)); + return new CmpMessageInterface() { + + @Override + public VerificationContext getInputVerification() { + if (certProfile != null) { + switch (certProfile) { + case "certProfileForKur": + case "certProfileForRr": + return enrollmentTrust; + } + } + return downstreamTrust; + } + + @Override + public NestedEndpointContext getNestedEndpointContext() { + return null; + } + + @Override + public CredentialContext getOutputCredentials() { + try { + return downstreamCredentials; + } catch (final Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public ReprotectMode getReprotectMode() { + return reprotectMode; + } + + @Override + public boolean getSuppressRedundantExtraCerts() { + return false; + } + + @Override + public boolean isCacheExtraCerts() { + return false; + } + + @Override + public boolean isMessageTimeDeviationAllowed(final long deviation) { + return true; + } + }; + } + + @Override + public int getDownstreamTimeout(final String certProfile, final int bodyType) { + return 10; + } + + @Override + public VerificationContext getEnrollmentTrust(final String certProfile, final int bodyType) { + LOGGER.debug( + "getEnrollmentTrust called with certprofile: {}, type: {}", + certProfile, + MessageDumper.msgTypeAsString(bodyType)); + return enrollmentTrust; + } + + @Override + public boolean getForceRaVerifyOnUpstream(final String certProfile, final int bodyType) { + LOGGER.debug( + "getForceRaVerifyOnUpstream called with certprofile: {}, type: {}", + certProfile, + MessageDumper.msgTypeAsString(bodyType)); + return false; + } + + @Override + public InventoryInterface getInventory(final String certProfile, final int bodyType) { + LOGGER.debug( + "getInventory called with certprofile: {}, type: {}", + certProfile, + MessageDumper.msgTypeAsString(bodyType)); + return new InventoryInterface() { + + @Override + public CheckAndModifyResult checkAndModifyCertRequest( + final byte[] transactionID, + final String requesterDn, + final byte[] certTemplate, + final String requestedSubjectDn, + byte[] pkiMessage) { + LOGGER.debug( + "checkAndModifyCertRequest called with transactionID: {}, requesterDn: {}, requestedSubjectDn: {}", + new BigInteger(transactionID), + requesterDn, + requestedSubjectDn); + return new CheckAndModifyResult() { + + @Override + public byte[] getUpdatedCertTemplate() { + return null; + } + + @Override + public boolean isGranted() { + return true; + } + }; + } + + @Override + public boolean checkP10CertRequest( + final byte[] transactionID, + final String requesterDn, + final byte[] pkcs10CertRequest, + final String requestedSubjectDn, + byte[] pkiMessage) { + fail(String.format( + "checkP10CertRequest called with transactionID: {}, requesterDn: {}, requestedSubjectDn: {}", + new BigInteger(transactionID), + requesterDn, + requestedSubjectDn)); + return false; + } + + @Override + public boolean learnEnrollmentResult( + final byte[] transactionID, + final byte[] certificate, + final String serialNumber, + final String subjectDN, + final String issuerDN) { + LOGGER.debug( + "learnEnrollmentResult called with transactionID: {}, serialNumber: {}, subjectDN: {}, issuerDN: {}", + new BigInteger(transactionID), + serialNumber, + subjectDN, + issuerDN); + return true; + } + }; + } + + @Override + public PersistencyInterface getPersistency() { + return persistency; + } + + @Override + public int getRetryAfterTimeInSeconds(final String certProfile, final int bodyType) { + LOGGER.debug( + "getRetryAfterTimeInSeconds called with certprofile: {}, type: {}", + certProfile, + MessageDumper.msgTypeAsString(bodyType)); + return 1; + } + + @Override + public SupportMessageHandlerInterface getSupportMessageHandler( + final String certProfile, final String infoTypeOid) { + LOGGER.debug( + "getSupportMessageHandler called with certprofile: {}, infoTypeOid: {}", + certProfile, + infoTypeOid); + return null; + } + + @Override + public CmpMessageInterface getUpstreamConfiguration(final String certProfile, final int bodyType) { + LOGGER.debug( + "getUpstreamConfiguration called with certprofile: {}, type: {}", + certProfile, + MessageDumper.msgTypeAsString(bodyType)); + return new CmpMessageInterface() { + + @Override + public VerificationContext getInputVerification() { + return upstreamTrust; + } + + @Override + public NestedEndpointContext getNestedEndpointContext() { + return null; + } + + @Override + public CredentialContext getOutputCredentials() { + + try { + return upstreamCredentials; + } catch (final Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public String getRecipient() { + + return "CN=RecipientSetByRa"; + } + + @Override + public ReprotectMode getReprotectMode() { + return ReprotectMode.reprotect; + } + + @Override + public boolean getSuppressRedundantExtraCerts() { + return false; + } + + @Override + public boolean isCacheExtraCerts() { + return false; + } + + @Override + public boolean isMessageTimeDeviationAllowed(final long deviation) { + return true; + } + }; + } + + @Override + public boolean isRaVerifiedAcceptable(final String certProfile, final int bodyType) { + LOGGER.debug( + "isRaVerifiedAcceptable called with certprofile: {}, type: {}", + certProfile, + MessageDumper.msgTypeAsString(bodyType)); + return false; + } + }; + } + + private static Configuration buildSignatureBasedDownstreamConfiguration() throws Exception { + final TrustChainAndPrivateKey downstreamCredentials = + new TrustChainAndPrivateKey("credentials/CMP_LRA_DOWNSTREAM_Keystore.p12", "Password".toCharArray()); + final SignatureValidationCredentials downstreamTrust = + new SignatureValidationCredentials("credentials/CMP_EE_Root.pem", null); + final TrustChainAndPrivateKey upstreamCredentials = + new TrustChainAndPrivateKey("credentials/CMP_LRA_UPSTREAM_Keystore.p12", "Password".toCharArray()); + final SignatureValidationCredentials upstreamTrust = + new SignatureValidationCredentials("credentials/CMP_CA_Root.pem", null); + final SignatureValidationCredentials enrollmentTrust = + new SignatureValidationCredentials("credentials/ENROLL_Root.pem", null); + + return buildRaConfigurationWithRecipient( + downstreamCredentials, + ReprotectMode.keep, + downstreamTrust, + upstreamCredentials, + upstreamTrust, + enrollmentTrust); + } + + private CmpCaMock cmpCaMock; + + private UpstreamExchange upstreamRa; + + @Before + public void setUp() throws Exception { + cmpCaMock = new CmpCaMock("credentials/ENROLL_Keystore.p12", "credentials/CMP_CA_Keystore.p12"); + upstreamRa = launchCmpRa(buildSignatureBasedDownstreamConfiguration(), cmpCaMock::sendReceiveMessage); + } + + @Test + public void testCrWithRecipientByRa() throws Exception { + final EnrollmentResult ret = new CmpClient( + "theCertProfileForOnlineEnrollment", + upstreamRa, + new CmpMessageInterface() { + + final SignatureValidationCredentials upstreamTrust = + new SignatureValidationCredentials(UPSTREAM_TRUST_PATH, null); + + @Override + public VerificationContext getInputVerification() { + return upstreamTrust; + } + + @Override + public NestedEndpointContext getNestedEndpointContext() { + return null; + } + + @Override + public CredentialContext getOutputCredentials() { + try { + return ConfigurationFactory.getEeSignaturebasedCredentials(); + } catch (final Exception e) { + fail(e.getLocalizedMessage()); + return null; + } + } + + @Override + public String getRecipient() { + return "CN=RecipientSetByClient"; + } + + @Override + public ReprotectMode getReprotectMode() { + return ReprotectMode.reprotect; + } + + @Override + public boolean getSuppressRedundantExtraCerts() { + return false; + } + + @Override + public boolean isCacheExtraCerts() { + return false; + } + + @Override + public boolean isMessageTimeDeviationAllowed(final long deviation) { + return deviation < 10; + } + }, + getClientContext( + PKIBody.TYPE_CERT_REQ, + ConfigurationFactory.getKeyGenerator().generateKeyPair(), + null)) + .invokeEnrollment(); + assertNotNull(ret); + assertEquals( + "recipient", + new GeneralName(new X500Name("CN=RecipientSetByRa")), + cmpCaMock.getReceivedRequestAt(1).getHeader().getRecipient()); + } +} diff --git a/src/test/java/com/siemens/pki/cmpracomponent/test/framework/CmpCaMock.java b/src/test/java/com/siemens/pki/cmpracomponent/test/framework/CmpCaMock.java index c34c457b..191b9384 100644 --- a/src/test/java/com/siemens/pki/cmpracomponent/test/framework/CmpCaMock.java +++ b/src/test/java/com/siemens/pki/cmpracomponent/test/framework/CmpCaMock.java @@ -36,9 +36,16 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; +import java.util.LinkedList; import java.util.List; import javax.security.auth.x500.X500Principal; -import org.bouncycastle.asn1.cmp.*; +import org.bouncycastle.asn1.cmp.CMPCertificate; +import org.bouncycastle.asn1.cmp.PKIBody; +import org.bouncycastle.asn1.cmp.PKIFailureInfo; +import org.bouncycastle.asn1.cmp.PKIMessage; +import org.bouncycastle.asn1.cmp.PKIStatus; +import org.bouncycastle.asn1.cmp.PKIStatusInfo; +import org.bouncycastle.asn1.cmp.RevRepContentBuilder; import org.bouncycastle.asn1.crmf.CertReqMessages; import org.bouncycastle.asn1.crmf.CertTemplate; import org.bouncycastle.asn1.pkcs.CertificationRequest; @@ -70,6 +77,10 @@ public class CmpCaMock implements CmpRaComponent.UpstreamExchange { private static final JcaX509ContentVerifierProviderBuilder X509_CVPB = new JcaX509ContentVerifierProviderBuilder().setProvider(CertUtility.getBouncyCastleProvider()); private static final JcaPEMKeyConverter JCA_KEY_CONVERTER = new JcaPEMKeyConverter(); + private static final int MAX_LAST_RECEIVED = 10; + + private final LinkedList lastReceivedMessages = new LinkedList<>(); + private final ProtectionProvider caProtectionProvider; private final SignatureCredentialContext enrollmentCredentials; @@ -81,71 +92,6 @@ public CmpCaMock(final String enrollmentCredentials, final String protectionCred new TrustChainAndPrivateKey(protectionCredentials, TestUtils.PASSWORD_AS_CHAR_ARRAY)); } - public byte[] processP10CerticateRequest(final byte[] csr, final String certProfile) { - try { - final PKCS10CertificationRequest p10Request = new PKCS10CertificationRequest(csr); - if (!p10Request.isSignatureValid(X509_CVPB.build(p10Request.getSubjectPublicKeyInfo()))) { - LOGGER.error("invalid P10 Request"); - return null; - } - return ifNotNull(handleP10CerticateRequest(p10Request), CMPCertificate::getEncoded); - } catch (final Exception e) { - LOGGER.error("P10 processing error", e); - return null; - } - } - - @Override - public byte[] sendReceiveMessage( - final byte[] rawReceivedMessage, final String certProfile, final int bodyTypeOfFirstRequest) { - assertTrue( - "request message type", - Arrays.asList( - PKIBody.TYPE_INIT_REQ, - PKIBody.TYPE_CERT_REQ, - PKIBody.TYPE_KEY_UPDATE_REQ, - PKIBody.TYPE_REVOCATION_REQ) - .contains(bodyTypeOfFirstRequest)); - try { - final PKIMessage receivedMessage = PKIMessage.getInstance(rawReceivedMessage); - if (LOGGER.isDebugEnabled()) { - // avoid unnecessary call of MessageDumper.dumpPkiMessage, if debug isn't - // enabled - LOGGER.debug("CA: got:\n" + MessageDumper.dumpPkiMessage(receivedMessage)); - } - final PKIMessage ret; - switch (receivedMessage.getBody().getType()) { - case PKIBody.TYPE_INIT_REQ: - case PKIBody.TYPE_CERT_REQ: - case PKIBody.TYPE_KEY_UPDATE_REQ: - ret = handleCrmfCerticateRequest(receivedMessage); - break; - case PKIBody.TYPE_P10_CERT_REQ: - ret = handleP10CerticateRequest(receivedMessage); - break; - case PKIBody.TYPE_CERT_CONFIRM: - ret = handleCertConfirm(receivedMessage); - break; - case PKIBody.TYPE_REVOCATION_REQ: - ret = handleRevocationRequest(receivedMessage); - break; - default: - ret = generateError( - receivedMessage, - "unsuported message type " - + receivedMessage.getBody().getType()); - } - if (LOGGER.isDebugEnabled()) { - // avoid unnecessary call of MessageDumper.dumpPkiMessage, if debug isn't - // enabled - LOGGER.debug("CA: respond:\n" + MessageDumper.dumpPkiMessage(ret)); - } - return ret.getEncoded(); - } catch (final Exception e) { - throw new RuntimeException(e); - } - } - private CMPCertificate createCertificate( final X500Name subject, final SubjectPublicKeyInfo publicKey, final X509Certificate issuingCert) throws PEMException, NoSuchAlgorithmException, CertIOException, CertificateException, @@ -170,11 +116,9 @@ private CMPCertificate createCertificate( AlgorithmHelper.getSigningAlgNameFromKey(enrollmentCredentials.getPrivateKey())) .setProvider(TestCertUtility.BOUNCY_CASTLE_PROVIDER); - final CMPCertificate cmpCertificateFromCertificate = - TestCertUtility.cmpCertificateFromCertificate(new JcaX509CertificateConverter() - .setProvider(TestCertUtility.BOUNCY_CASTLE_PROVIDER) - .getCertificate(v3CertBldr.build(signerBuilder.build(enrollmentCredentials.getPrivateKey())))); - return cmpCertificateFromCertificate; + return TestCertUtility.cmpCertificateFromCertificate(new JcaX509CertificateConverter() + .setProvider(TestCertUtility.BOUNCY_CASTLE_PROVIDER) + .getCertificate(v3CertBldr.build(signerBuilder.build(enrollmentCredentials.getPrivateKey())))); } private PKIMessage generateError(final PKIMessage receivedMessage, final String errorDetails) throws Exception { @@ -184,6 +128,14 @@ private PKIMessage generateError(final PKIMessage receivedMessage, final String PkiMessageGenerator.generateErrorBody(PKIFailureInfo.badRequest, errorDetails)); } + public PKIMessage getLastReceivedRequest() { + return lastReceivedMessages.getFirst(); + } + + public PKIMessage getReceivedRequestAt(int index) { + return lastReceivedMessages.get(index); + } + private PKIMessage handleCertConfirm(final PKIMessage receivedMessage) throws Exception { return PkiMessageGenerator.generateAndProtectMessage( PkiMessageGenerator.buildRespondingHeaderProvider(receivedMessage), @@ -211,13 +163,21 @@ private PKIMessage handleCrmfCerticateRequest(final PKIMessage receivedMessage) for (final X509Certificate aktCert : issuingChain) { issuingChainForExtraCerts.add(TestCertUtility.cmpCertificateFromCertificate(aktCert)); } - final PKIMessage ret = PkiMessageGenerator.generateAndProtectMessage( + return PkiMessageGenerator.generateAndProtectMessage( PkiMessageGenerator.buildRespondingHeaderProvider(receivedMessage), caProtectionProvider, + null, PkiMessageGenerator.generateIpCpKupBody( receivedMessage.getBody().getType() + 1, cmpCertificateFromCertificate), issuingChainForExtraCerts); - return ret; + } + + CMPCertificate handleP10CerticateRequest(final PKCS10CertificationRequest certificationRequest) throws Exception { + // get copy of enrollment chain + final List issuingChain = enrollmentCredentials.getCertificateChain(); + final X509Certificate issuingCert = issuingChain.get(0); + return createCertificate( + certificationRequest.getSubject(), certificationRequest.getSubjectPublicKeyInfo(), issuingCert); } private PKIMessage handleP10CerticateRequest(final PKIMessage receivedMessage) throws Exception { @@ -236,12 +196,12 @@ private PKIMessage handleP10CerticateRequest(final PKIMessage receivedMessage) t for (final X509Certificate aktCert : issuingChain) { issuingChainForExtraCerts.add(TestCertUtility.cmpCertificateFromCertificate(aktCert)); } - final PKIMessage ret = PkiMessageGenerator.generateAndProtectMessage( + return PkiMessageGenerator.generateAndProtectMessage( PkiMessageGenerator.buildRespondingHeaderProvider(receivedMessage), caProtectionProvider, + null, PkiMessageGenerator.generateIpCpKupBody(PKIBody.TYPE_CERT_REP, cmpCertificateFromCertificate), issuingChainForExtraCerts); - return ret; } private PKIMessage handleRevocationRequest(final PKIMessage receivedMessage) throws Exception { @@ -253,11 +213,72 @@ private PKIMessage handleRevocationRequest(final PKIMessage receivedMessage) thr new PKIBody(PKIBody.TYPE_REVOCATION_REP, rrcb.build())); } - CMPCertificate handleP10CerticateRequest(final PKCS10CertificationRequest certificationRequest) throws Exception { - // get copy of enrollment chain - final List issuingChain = enrollmentCredentials.getCertificateChain(); - final X509Certificate issuingCert = issuingChain.get(0); - return createCertificate( - certificationRequest.getSubject(), certificationRequest.getSubjectPublicKeyInfo(), issuingCert); + public byte[] processP10CerticateRequest(final byte[] csr, final String certProfile) { + try { + final PKCS10CertificationRequest p10Request = new PKCS10CertificationRequest(csr); + if (!p10Request.isSignatureValid(X509_CVPB.build(p10Request.getSubjectPublicKeyInfo()))) { + LOGGER.error("invalid P10 Request"); + return null; + } + return ifNotNull(handleP10CerticateRequest(p10Request), CMPCertificate::getEncoded); + } catch (final Exception e) { + LOGGER.error("P10 processing error", e); + return null; + } + } + + @Override + public byte[] sendReceiveMessage( + final byte[] rawReceivedMessage, final String certProfile, final int bodyTypeOfFirstRequest) { + assertTrue( + "request message type", + Arrays.asList( + PKIBody.TYPE_INIT_REQ, + PKIBody.TYPE_CERT_REQ, + PKIBody.TYPE_KEY_UPDATE_REQ, + PKIBody.TYPE_REVOCATION_REQ) + .contains(bodyTypeOfFirstRequest)); + try { + final PKIMessage receivedMessage = PKIMessage.getInstance(rawReceivedMessage); + if (LOGGER.isDebugEnabled()) { + // avoid unnecessary call of MessageDumper.dumpPkiMessage, if debug isn't + // enabled + LOGGER.debug("CA: got:\n" + MessageDumper.dumpPkiMessage(receivedMessage)); + } + lastReceivedMessages.addFirst(receivedMessage); + while (lastReceivedMessages.size() > MAX_LAST_RECEIVED) { + lastReceivedMessages.removeLast(); + } + final PKIMessage ret; + switch (receivedMessage.getBody().getType()) { + case PKIBody.TYPE_INIT_REQ: + case PKIBody.TYPE_CERT_REQ: + case PKIBody.TYPE_KEY_UPDATE_REQ: + ret = handleCrmfCerticateRequest(receivedMessage); + break; + case PKIBody.TYPE_P10_CERT_REQ: + ret = handleP10CerticateRequest(receivedMessage); + break; + case PKIBody.TYPE_CERT_CONFIRM: + ret = handleCertConfirm(receivedMessage); + break; + case PKIBody.TYPE_REVOCATION_REQ: + ret = handleRevocationRequest(receivedMessage); + break; + default: + ret = generateError( + receivedMessage, + "unsuported message type " + + receivedMessage.getBody().getType()); + } + if (LOGGER.isDebugEnabled()) { + // avoid unnecessary call of MessageDumper.dumpPkiMessage, if debug isn't + // enabled + LOGGER.debug("CA: respond:\n" + MessageDumper.dumpPkiMessage(ret)); + } + return ret.getEncoded(); + } catch (final Exception e) { + throw new RuntimeException(e); + } } } From 7b0aa9c24c1e4b95d4705c0e692aa50f9fa92a96 Mon Sep 17 00:00:00 2001 From: "Mirdha, Kiron (T CST SEA-DE)" Date: Mon, 27 Nov 2023 10:56:28 +0100 Subject: [PATCH 3/5] Tweak javadoc --- .../pki/cmpracomponent/msggeneration/PkiMessageGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/PkiMessageGenerator.java b/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/PkiMessageGenerator.java index 869fa03b..470e5d73 100644 --- a/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/PkiMessageGenerator.java +++ b/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/PkiMessageGenerator.java @@ -251,7 +251,7 @@ public ASN1OctetString getTransactionID() { * * @param headerProvider PKI header * @param protectionProvider PKI protection - * @param newRecipient outgoing recipient or null recipient + * @param newRecipient outgoing recipient or null if recipient * from headerProvider should be used * @param body message body * @param issuingChain chain of enrolled certificate to append at the From 86bc5751fff37d9c28fb05597b567703eb31c357 Mon Sep 17 00:00:00 2001 From: Alexander Railean Date: Mon, 27 Nov 2023 11:10:41 +0100 Subject: [PATCH 4/5] Disable autoReleaseAfterClose in Nexus plugin --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3822cf1c..38e18dd6 100644 --- a/pom.xml +++ b/pom.xml @@ -171,7 +171,7 @@ ossrh https://s01.oss.sonatype.org/ - true + false From 743504f022e56c231bdce9573fa6d759a8c7da14 Mon Sep 17 00:00:00 2001 From: Alexander Railean Date: Mon, 27 Nov 2023 11:20:07 +0100 Subject: [PATCH 5/5] Use Java 17 for SonarLint analysis environment SonarCloud deprecated Java 11 --- .github/workflows/code-quality.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index fdd8da45..93a72ccb 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -18,10 +18,10 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: - java-version: 11 + java-version: 17 distribution: 'temurin' - name: Cache SonarCloud packages uses: actions/cache@v3