From bd5db5444a30c04c2087cb439471c61953986ba3 Mon Sep 17 00:00:00 2001
From: Andreas Kretschmer <38779775+Akretsch@users.noreply.github.com>
Date: Wed, 24 Jul 2024 10:34:12 +0200
Subject: [PATCH] again fix protection of NESTED responses (#114)
---
CHANGELOG.md | 3 +
pom.xml | 2 +-
.../msggeneration/MsgOutputProtector.java | 18 +-
.../msggeneration/PkiMessageGenerator.java | 16 +-
.../msgprocessing/CmpRaUpstream.java | 12 +-
.../msgprocessing/RaDownstream.java | 332 ++++++++----------
.../msgprocessing/ServiceImplementation.java | 2 +-
.../msgvalidation/MessageBodyValidator.java | 21 +-
.../protection/MacProtection.java | 5 +-
.../protection/NoProtection.java | 5 +-
.../protection/ProtectionProvider.java | 13 +-
.../protection/SignatureBasedProtection.java | 5 +-
...tCentralKeyGenerationWithKeyAgreement.java | 106 ++++--
.../cmpclientcomponent/test/TestP10Cr.java | 2 +-
.../test/TestRefusingInventory.java | 28 +-
.../test/TestUpdateByInventory.java | 165 +++++++++
.../test/CkgOnlineEnrollmentTestcaseBase.java | 2 -
.../test/TestMessageBodyValidator.java | 93 +++++
.../test/TestSupportMessages.java | 41 ++-
.../test/framework/CmpCaMock.java | 1 -
.../test/framework/ConfigurationFactory.java | 35 ++
.../test/framework/HeaderProviderForTest.java | 2 +-
.../framework/TrustChainAndPrivateKey.java | 7 +-
23 files changed, 664 insertions(+), 252 deletions(-)
create mode 100644 src/test/java/com/siemens/pki/cmpclientcomponent/test/TestUpdateByInventory.java
create mode 100644 src/test/java/com/siemens/pki/cmpracomponent/test/TestMessageBodyValidator.java
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 28c6831f..7cbca55a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -140,3 +140,6 @@ feat: add logging while accessing configuration data
fix: handling of NestedEndpointContext.isIncomingRecipientValid
+### 4.1.4 (Jul 23 2024)
+
+fix: again fix protection of NESTED responses
diff --git a/pom.xml b/pom.xml
index 1adc8c55..c8ab98ac 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,7 +8,7 @@
com.siemens.pki
CmpRaComponent
jar
- 4.1.3
+ 4.1.4
UTF-8
.
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/MsgOutputProtector.java b/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/MsgOutputProtector.java
index 44c88d8d..514ba40c 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/MsgOutputProtector.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/MsgOutputProtector.java
@@ -29,6 +29,7 @@
import com.siemens.pki.cmpracomponent.protection.ProtectionProvider;
import com.siemens.pki.cmpracomponent.protection.ProtectionProviderFactory;
import com.siemens.pki.cmpracomponent.util.ConfigLogger;
+import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.Collections;
@@ -123,9 +124,11 @@ public MsgOutputProtector(final NestedEndpointContext config, final String inter
* @param headerProvider the header to use
* @param body body of new message
* @return new message
- * @throws Exception in case of error
+ * @throws IOException in case of encoding problem
+ * @throws GeneralSecurityException in case of error
*/
- public PKIMessage createOutgoingMessage(final HeaderProvider headerProvider, PKIBody body) throws Exception {
+ public PKIMessage createOutgoingMessage(final HeaderProvider headerProvider, PKIBody body)
+ throws GeneralSecurityException, IOException {
switch (reprotectMode) {
case reprotect:
case keep:
@@ -145,9 +148,11 @@ public PKIMessage createOutgoingMessage(final HeaderProvider headerProvider, PKI
* @param request request to answer
* @param body body of new message
* @return new message
- * @throws Exception in case of error
+ * @throws GeneralSecurityException in case of error
+ * @throws IOException in case of encoding error
*/
- public PKIMessage generateAndProtectResponseTo(PKIMessage request, final PKIBody body) throws Exception {
+ public PKIMessage generateAndProtectResponseTo(PKIMessage request, final PKIBody body)
+ throws GeneralSecurityException, IOException {
return stripRedundantExtraCerts(PkiMessageGenerator.generateAndProtectMessage(
PkiMessageGenerator.buildRespondingHeaderProvider(request), protector, recipient, body, null));
}
@@ -166,10 +171,11 @@ public ProtectionProvider getProtector() {
* @param issuingChain trust chain of issued certificate to add to extracerts or
* null
* @return protected message ready to send
- * @throws Exception in case of processing error
+ * @throws IOException in case of encoding problem
+ * @throws GeneralSecurityException in case of processing error
*/
public PKIMessage protectOutgoingMessage(final PKIMessage in, final List issuingChain)
- throws Exception {
+ throws GeneralSecurityException, IOException {
switch (reprotectMode) {
case reprotect:
return stripRedundantExtraCerts(PkiMessageGenerator.generateAndProtectMessage(
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 470e5d73..74b3c2ea 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/PkiMessageGenerator.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/PkiMessageGenerator.java
@@ -29,6 +29,7 @@
import com.siemens.pki.cmpracomponent.util.MessageDumper;
import java.io.IOException;
import java.math.BigInteger;
+import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.Collections;
@@ -257,7 +258,8 @@ public ASN1OctetString getTransactionID() {
* @param issuingChain chain of enrolled certificate to append at the
* extraCerts
* @return a fully build and protected message
- * @throws Exception in case of error
+ * @throws GeneralSecurityException in case of error
+ * @throws IOException in case of encoding error
*/
public static PKIMessage generateAndProtectMessage(
final HeaderProvider headerProvider,
@@ -265,7 +267,7 @@ public static PKIMessage generateAndProtectMessage(
GeneralName newRecipient,
final PKIBody body,
final List issuingChain)
- throws Exception {
+ throws GeneralSecurityException, IOException {
synchronized (protectionProvider) {
final GeneralName recipient = computeDefaultIfNull(newRecipient, headerProvider::getRecipient);
final GeneralName sender = computeDefaultIfNull(protectionProvider.getSender(), headerProvider::getSender);
@@ -422,11 +424,12 @@ public static PKIBody generateIpCpKupErrorBody(final int bodyType, final int fai
* @param privateKey private key to build the POPO, if set to null, POPO is
* set to raVerified
* @return a IR, CR or KUR body
- * @throws Exception in case of error
+ * @throws GeneralSecurityException in case of error
+ * @throws IOException in case of encoding error
*/
public static PKIBody generateIrCrKurBody(
final int bodyType, final CertTemplate certTemplate, final Controls controls, final PrivateKey privateKey)
- throws Exception {
+ throws GeneralSecurityException, IOException {
final CertRequest certReq = new CertRequest(CERT_REQ_ID_0, certTemplate, controls);
if (privateKey == null) {
return new PKIBody(bodyType, new CertReqMessages(new CertReqMsg(certReq, new ProofOfPossession(), null)));
@@ -542,10 +545,11 @@ public static PKIBody generateRrBody(final X500Name issuer, final ASN1Integer se
* @param headerProvider PKI header
* @param body message body
* @return a fully build and not protected message
- * @throws Exception in case of error
+ * @throws GeneralSecurityException in case of error
+ * @throws IOException in case of encoding error
*/
public static PKIMessage generateUnprotectMessage(final HeaderProvider headerProvider, final PKIBody body)
- throws Exception {
+ throws GeneralSecurityException, IOException {
return generateAndProtectMessage(headerProvider, ProtectionProvider.NO_PROTECTION, null, body, null);
}
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 cdecce70..a1bb81d0 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/CmpRaUpstream.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/CmpRaUpstream.java
@@ -32,6 +32,8 @@
import com.siemens.pki.cmpracomponent.persistency.PersistencyContextManager;
import com.siemens.pki.cmpracomponent.util.CmpFuncEx;
import com.siemens.pki.cmpracomponent.util.ConfigLogger;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
@@ -76,19 +78,17 @@ class CmpRaUpstream implements RaUpstream {
* @param persistencyContextManager persistency interface
* @param config specific configuration
* @param upstreamExchange upstream function
- * @throws Exception in case of error
*/
CmpRaUpstream(
final PersistencyContextManager persistencyContextManager,
final Configuration config,
- final CmpFuncEx upstreamExchange)
- throws Exception {
+ final CmpFuncEx upstreamExchange) {
this.persistencyContextManager = persistencyContextManager;
this.config = config;
this.upstreamMsgHandler = upstreamExchange;
}
- void gotResponseAtUpstream(final PKIMessage responseMessage) throws Exception {
+ void gotResponseAtUpstream(final PKIMessage responseMessage) throws IOException, CmpProcessingException {
final PersistencyContext persistencyContext = persistencyContextManager.loadPersistencyContext(
responseMessage.getHeader().getTransactionID().getOctets());
if (persistencyContext == null) {
@@ -162,7 +162,7 @@ public PKIMessage handleRequest(final PKIMessage in, final PersistencyContext pe
PkiMessageGenerator.generateResponseBodyWithWaiting(sentMessage.getBody(), INTERFACE_NAME));
}
// synchronous transfer
- if (receivedMessage.getBody().getType() == PKIBody.TYPE_NESTED) {
+ if (receivedMessage.getBody().getType() == PKIBody.TYPE_NESTED && nestedEndpointContext != null) {
final MessageHeaderValidator nestedHeaderValidator = new MessageHeaderValidator(NESTED_INTERFACE_NAME);
nestedHeaderValidator.validate(receivedMessage);
final ProtectionValidator nestedProtectionValidator = new ProtectionValidator(
@@ -220,7 +220,7 @@ public PKIMessage handleRequest(final PKIMessage in, final PersistencyContext pe
}
private PKIMessage handlePollReq(final PKIMessage in, final PersistencyContext persistencyContext)
- throws Exception {
+ throws BaseCmpException, GeneralSecurityException, IOException {
final PKIMessage delayedResponse = persistencyContext.getPendingDelayedResponse();
if (delayedResponse != null) {
final InputValidator inputValidator = new InputValidator(
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 02ebd05b..765dfe21 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/RaDownstream.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/RaDownstream.java
@@ -48,6 +48,7 @@
import com.siemens.pki.cmpracomponent.protection.SignatureBasedProtection;
import com.siemens.pki.cmpracomponent.util.ConfigLogger;
import com.siemens.pki.cmpracomponent.util.MessageDumper;
+import com.siemens.pki.cmpracomponent.util.NullUtil.ExFunction;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
@@ -56,13 +57,13 @@
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
-import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
+import java.util.function.Function;
import java.util.stream.Collectors;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1Integer;
@@ -107,6 +108,16 @@
*/
class RaDownstream {
+ private static Function wrap(ExFunction checkedFunction) {
+ return t -> {
+ try {
+ return checkedFunction.apply(t);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ };
+ }
+
private static final String INTERFACE_NAME = "downstream";
private static final String NESTED_INTERFACE_NAME = "nested " + INTERFACE_NAME;
@@ -129,7 +140,6 @@ class RaDownstream {
* @param config specific configuration
* @param upstream related upstream interface handler
* @param supportedmessagetypes
- * @throws Exception in case of error
*/
RaDownstream(
final PersistencyContextManager persistencyContextManager,
@@ -147,7 +157,7 @@ protected CmsEncryptorBase buildEncryptor(
final CkgContext ckgConfiguration,
final int initialRequestType,
final String interfaceName)
- throws GeneralSecurityException, CmpProcessingException, CmpEnrollmentException {
+ throws GeneralSecurityException, BaseCmpException {
final ASN1ObjectIdentifier protectingAlgOID =
incomingRequest.getHeader().getProtectionAlg().getAlgorithm();
if (CMPObjectIdentifiers.passwordBasedMac.equals(protectingAlgOID)
@@ -177,29 +187,10 @@ protected CmsEncryptorBase buildEncryptor(
return new KeyTransportEncryptor(ckgConfiguration, recipientCert, initialRequestType, interfaceName);
}
- private MsgOutputProtector getOutputProtector(final PersistencyContext persistencyContext, final int bodyType)
- throws Exception {
- return new MsgOutputProtector(
- ConfigLogger.log(
- INTERFACE_NAME,
- "Configuration.getDownstreamConfiguration",
- config::getDownstreamConfiguration,
- persistencyContext.getCertProfile(),
- bodyType),
- INTERFACE_NAME,
- persistencyContext);
- }
-
- /**
- * special handling for CR, IR, KUR
- *
- * @param incomingCertificateRequest
- * @param outputProtector
- * @return handled message
- * @throws Exception in case of error
- */
+ // special handling for CR, IR, KUR
private PKIMessage handleCrmfCertificateRequest(
- final PKIMessage incomingCertificateRequest, final PersistencyContext persistencyContext) throws Exception {
+ final PKIMessage incomingCertificateRequest, final PersistencyContext persistencyContext)
+ throws BaseCmpException, GeneralSecurityException, IOException {
final PKIBody requestBody = incomingCertificateRequest.getBody();
final PKIBody body = requestBody;
@@ -233,21 +224,17 @@ private PKIMessage handleCrmfCertificateRequest(
}
final String requesterDnFinal = requesterDn;
final CertTemplate certTemplateFinal = certTemplate;
+ byte[] encodedTemplate = certTemplateFinal.getEncoded();
+ byte[] encodedRequest = incomingCertificateRequest.getEncoded();
final CheckAndModifyResult checkResult = ConfigLogger.logOptional(
INTERFACE_NAME,
"InventoryInterface.checkAndModifyCertRequest(byte[], String, byte[], String, byte[])",
- () -> {
- try {
- return inventory.checkAndModifyCertRequest(
- persistencyContext.getTransactionId(),
- requesterDnFinal,
- certTemplateFinal.getEncoded(),
- ifNotNull(certTemplateFinal.getSubject(), X500Name::toString),
- incomingCertificateRequest.getEncoded());
- } catch (IOException | RuntimeException e) {
- return null;
- }
- });
+ () -> inventory.checkAndModifyCertRequest(
+ persistencyContext.getTransactionId(),
+ requesterDnFinal,
+ encodedTemplate,
+ ifNotNull(certTemplateFinal.getSubject(), X500Name::toString),
+ encodedRequest));
if (checkResult == null
|| !ConfigLogger.log(INTERFACE_NAME, "CheckAndModifyResult.isGranted()", checkResult::isGranted)) {
@@ -395,7 +382,7 @@ PKIMessage handleInputMessage(final PKIMessage in) {
try {
try {
byte[] transactionId =
- ifNotNull(in, m -> m.getHeader().getTransactionID().getEncoded());
+ ifNotNull(in, m -> m.getHeader().getTransactionID().getOctets());
if (transactionId == null) {
MsgOutputProtector protector = new MsgOutputProtector(
ConfigLogger.log(
@@ -411,14 +398,31 @@ PKIMessage handleInputMessage(final PKIMessage in) {
PkiMessageGenerator.generateErrorBody(
PKIFailureInfo.badDataFormat, "transactionId missing"));
}
- persistencyContext = persistencyContextManager.loadCreatePersistencyContext(
- in.getHeader().getTransactionID().getOctets());
+ persistencyContext = persistencyContextManager.loadCreatePersistencyContext(transactionId);
+ final int inBodyType = in.getBody().getType();
+ if (inBodyType == PKIBody.TYPE_NESTED) {
+ PersistencyContext nestedPersistencyContext = persistencyContext;
+ // suppress persistency update for NESTED messages
+ persistencyContext = null;
+ return handleNestedRequest(in, nestedPersistencyContext);
+ }
+ final InputValidator inputValidator = new InputValidator(
+ INTERFACE_NAME,
+ config::getDownstreamConfiguration,
+ config::isRaVerifiedAcceptable,
+ supportedMessageTypes,
+ persistencyContext);
+ inputValidator.validate(in);
- PKIMessage responseFromUpstream = handleInputRequest(in, persistencyContext);
+ PKIMessage responseFromUpstream = handleValidatedRequest(in, persistencyContext);
// apply downstream protection and nesting
List issuingChain = null;
responseBodyType = responseFromUpstream.getBody().getType();
switch (responseBodyType) {
+ case PKIBody.TYPE_NESTED:
+ // never nest a nested message
+ persistencyContext = null;
+ return responseFromUpstream;
case PKIBody.TYPE_INIT_REP:
case PKIBody.TYPE_CERT_REP:
case PKIBody.TYPE_KEY_UPDATE_REP:
@@ -432,8 +436,14 @@ PKIMessage handleInputMessage(final PKIMessage in) {
break;
default:
}
-
- PKIMessage protectedResponse = getOutputProtector(persistencyContext, responseBodyType)
+ final CmpMessageInterface downstreamConfiguration = ConfigLogger.log(
+ INTERFACE_NAME,
+ "Configuration.getDownstreamConfiguration",
+ config::getDownstreamConfiguration,
+ ifNotNull(persistencyContext, PersistencyContext::getCertProfile),
+ responseBodyType);
+ PKIMessage protectedResponse = new MsgOutputProtector(
+ downstreamConfiguration, INTERFACE_NAME, persistencyContext)
.protectOutgoingMessage(
new PKIMessage(
responseFromUpstream.getHeader(),
@@ -441,16 +451,7 @@ PKIMessage handleInputMessage(final PKIMessage in) {
responseFromUpstream.getProtection(),
responseFromUpstream.getExtraCerts()),
issuingChain);
- if (responseBodyType == PKIBody.TYPE_NESTED) {
- // never nest a nested message
- return protectedResponse;
- }
- final CmpMessageInterface downstreamConfiguration = ConfigLogger.log(
- INTERFACE_NAME,
- "Configuration.getDownstreamConfiguration",
- config::getDownstreamConfiguration,
- null,
- responseBodyType);
+
final NestedEndpointContext nestedEndpointContext = ConfigLogger.logOptional(
INTERFACE_NAME,
"CmpMessageInterface.getNestedEndpointContext()",
@@ -465,11 +466,23 @@ PKIMessage handleInputMessage(final PKIMessage in) {
new PKIBody(PKIBody.TYPE_NESTED, new PKIMessages(protectedResponse)));
} catch (final BaseCmpException e) {
final PKIBody errorBody = e.asErrorBody();
- return getOutputProtector(persistencyContext, responseBodyType)
+ final CmpMessageInterface downstreamConfiguration = ConfigLogger.log(
+ INTERFACE_NAME,
+ "Configuration.getDownstreamConfiguration",
+ config::getDownstreamConfiguration,
+ ifNotNull(persistencyContext, PersistencyContext::getCertProfile),
+ errorBody.getType());
+ return new MsgOutputProtector(downstreamConfiguration, INTERFACE_NAME, persistencyContext)
.generateAndProtectResponseTo(in, errorBody);
} catch (final RuntimeException ex) {
final PKIBody errorBody = new CmpProcessingException(INTERFACE_NAME, ex).asErrorBody();
- return getOutputProtector(persistencyContext, responseBodyType)
+ final CmpMessageInterface downstreamConfiguration = ConfigLogger.log(
+ INTERFACE_NAME,
+ "Configuration.getDownstreamConfiguration",
+ config::getDownstreamConfiguration,
+ ifNotNull(persistencyContext, PersistencyContext::getCertProfile),
+ errorBody.getType());
+ return new MsgOutputProtector(downstreamConfiguration, INTERFACE_NAME, persistencyContext)
.generateAndProtectResponseTo(in, errorBody);
} finally {
if (persistencyContext != null) {
@@ -477,7 +490,7 @@ PKIMessage handleInputMessage(final PKIMessage in) {
INTERFACE_NAME,
"Configuration.getDownstreamTimeout",
config::getDownstreamTimeout,
- ifNotNull(persistencyContext, PersistencyContext::getCertProfile),
+ persistencyContext.getCertProfile(),
responseBodyType);
if (offset == 0) {
offset = Integer.MAX_VALUE / 2;
@@ -493,75 +506,63 @@ PKIMessage handleInputMessage(final PKIMessage in) {
}
}
- private PKIMessage handleInputRequest(final PKIMessage in, final PersistencyContext persistencyContext)
- throws Exception {
-
- final int inBodyType = in.getBody().getType();
- if (inBodyType == PKIBody.TYPE_NESTED) {
- final CmpMessageInterface downstreamConfiguration = ConfigLogger.log(
- INTERFACE_NAME,
- "Configuration.getDownstreamConfiguration",
- config::getDownstreamConfiguration,
- null,
- PKIBody.TYPE_NESTED);
- final NestedEndpointContext nestedEndpointContext = ConfigLogger.logOptional(
- INTERFACE_NAME,
- "CmpMessageInterface.getNestedEndpointContext()",
- downstreamConfiguration::getNestedEndpointContext);
- if (nestedEndpointContext != null) {
- final MessageHeaderValidator nestedHeaderValidator = new MessageHeaderValidator(NESTED_INTERFACE_NAME);
- nestedHeaderValidator.validate(in);
- final ProtectionValidator nestedProtectionValidator = new ProtectionValidator(
- NESTED_INTERFACE_NAME,
- ConfigLogger.logOptional(
- NESTED_INTERFACE_NAME,
- "NestedEndpointContext.getInputVerification()",
- nestedEndpointContext::getInputVerification));
- nestedProtectionValidator.validate(in);
- PKIHeader inHeader = in.getHeader();
- boolean isIncomingRecipientValid = ConfigLogger.log(
- NESTED_INTERFACE_NAME,
- "NestedEndpointContext.isIncomingRecipientValid()",
- () -> nestedEndpointContext.isIncomingRecipientValid(
- inHeader.getRecipient().getName().toString()));
- if (!isIncomingRecipientValid) {
- return upstreamHandler.handleRequest(in, persistencyContext);
- }
- final PKIMessage[] embeddedMessages =
- PKIMessages.getInstance(in.getBody().getContent()).toPKIMessageArray();
- if (embeddedMessages == null || embeddedMessages.length == 0) {
- throw new CmpProcessingException(
- NESTED_INTERFACE_NAME,
- PKIFailureInfo.badMessageCheck,
- "no embedded messages inside NESTED message");
- }
- // wrapped protection case
- if (embeddedMessages.length == 1) {
- return handleInputMessage(embeddedMessages[0]);
- }
- // batching
- final PKIMessage[] responses = Arrays.stream(embeddedMessages)
- .map(this::handleInputMessage)
- .toArray(PKIMessage[]::new);
- // batched responses needs to be wrapped in a new NESTED response
- MsgOutputProtector nestedOutputProtector =
- new MsgOutputProtector(nestedEndpointContext, INTERFACE_NAME);
- return nestedOutputProtector.generateAndProtectResponseTo(
- in, new PKIBody(PKIBody.TYPE_NESTED, new PKIMessages(responses)));
- }
- }
- final InputValidator inputValidator = new InputValidator(
+ private PKIMessage handleNestedRequest(final PKIMessage in, final PersistencyContext persistencyContext)
+ throws BaseCmpException, GeneralSecurityException, IOException {
+ final CmpMessageInterface downstreamConfiguration = ConfigLogger.log(
INTERFACE_NAME,
+ "Configuration.getDownstreamConfiguration",
config::getDownstreamConfiguration,
- config::isRaVerifiedAcceptable,
- supportedMessageTypes,
- persistencyContext);
- inputValidator.validate(in);
- return handleValidatedRequest(in, persistencyContext);
+ null,
+ PKIBody.TYPE_NESTED);
+ final NestedEndpointContext nestedEndpointContext = ConfigLogger.logOptional(
+ INTERFACE_NAME,
+ "CmpMessageInterface.getNestedEndpointContext()",
+ downstreamConfiguration::getNestedEndpointContext);
+ if (nestedEndpointContext == null) {
+ return upstreamHandler.handleRequest(in, persistencyContext);
+ }
+ final MessageHeaderValidator nestedHeaderValidator = new MessageHeaderValidator(NESTED_INTERFACE_NAME);
+ nestedHeaderValidator.validate(in);
+ final ProtectionValidator nestedProtectionValidator = new ProtectionValidator(
+ NESTED_INTERFACE_NAME,
+ ConfigLogger.logOptional(
+ NESTED_INTERFACE_NAME,
+ "NestedEndpointContext.getInputVerification()",
+ nestedEndpointContext::getInputVerification));
+ nestedProtectionValidator.validate(in);
+ PKIHeader inHeader = in.getHeader();
+ boolean isIncomingRecipientValid = ConfigLogger.log(
+ NESTED_INTERFACE_NAME,
+ "NestedEndpointContext.isIncomingRecipientValid()",
+ () -> nestedEndpointContext.isIncomingRecipientValid(
+ inHeader.getRecipient().getName().toString()));
+ if (!isIncomingRecipientValid) {
+ return upstreamHandler.handleRequest(in, persistencyContext);
+ }
+ final PKIMessage[] embeddedMessages =
+ PKIMessages.getInstance(in.getBody().getContent()).toPKIMessageArray();
+ if (embeddedMessages == null || embeddedMessages.length == 0) {
+ throw new CmpProcessingException(
+ NESTED_INTERFACE_NAME,
+ PKIFailureInfo.badMessageCheck,
+ "no embedded messages inside NESTED message");
+ }
+ // wrapped protection case
+ if (embeddedMessages.length == 1) {
+ return handleInputMessage(embeddedMessages[0]);
+ }
+ // batching
+ final PKIMessage[] responses =
+ Arrays.stream(embeddedMessages).map(this::handleInputMessage).toArray(PKIMessage[]::new);
+ // batched responses needs to be wrapped in a new NESTED response
+ MsgOutputProtector nestedOutputProtector = new MsgOutputProtector(nestedEndpointContext, INTERFACE_NAME);
+ return nestedOutputProtector.generateAndProtectResponseTo(
+ in, new PKIBody(PKIBody.TYPE_NESTED, new PKIMessages(responses)));
}
private PKIMessage handleP10CertificateRequest(
- final PKIMessage incomingP10Request, final PersistencyContext persistencyContext) throws BaseCmpException {
+ final PKIMessage incomingP10Request, final PersistencyContext persistencyContext)
+ throws BaseCmpException, IOException {
try {
final PKIBody body = incomingP10Request.getBody();
persistencyContext.setRequestType(body.getType());
@@ -593,24 +594,18 @@ private PKIMessage handleP10CertificateRequest(
requesterDn = sender.toString();
}
}
- final PKCS10CertificationRequest p10RequestFinal = p10Request;
- final PKIMessage incomingP10RequestFinal = incomingP10Request;
+ final byte[] encodedP10Request = p10Request.getEncoded();
+ final byte[] encodedIncomingP10Request = incomingP10Request.getEncoded();
final String requesterDnFinal = requesterDn;
if (!ConfigLogger.log(
INTERFACE_NAME,
"InventoryInterface.checkP10CertRequest(byte[], String, byte[], String, byte[])",
- () -> {
- try {
- return inventory.checkP10CertRequest(
- persistencyContext.getTransactionId(),
- requesterDnFinal,
- p10RequestFinal.getEncoded(),
- p10RequestFinal.getSubject().toString(),
- incomingP10RequestFinal.getEncoded());
- } catch (final IOException e) {
- throw new RuntimeException(e);
- }
- })) {
+ () -> inventory.checkP10CertRequest(
+ persistencyContext.getTransactionId(),
+ requesterDnFinal,
+ encodedP10Request,
+ p10Request.getSubject().toString(),
+ encodedIncomingP10Request))) {
throw new CmpValidationException(
INTERFACE_NAME, PKIFailureInfo.badCertTemplate, "request refused by external inventory");
}
@@ -622,7 +617,7 @@ private PKIMessage handleP10CertificateRequest(
}
private PKIMessage handleRevocationRequest(PKIMessage incomingRequest, PersistencyContext persistencyContext)
- throws BaseCmpException {
+ throws BaseCmpException, IOException {
final PKIBody body = incomingRequest.getBody();
final int requestType = body.getType();
persistencyContext.setRequestType(requestType);
@@ -635,25 +630,20 @@ private PKIMessage handleRevocationRequest(PKIMessage incomingRequest, Persisten
if (inventory != null) {
final CertTemplate revTemplate =
((RevReqContent) body.getContent()).toRevDetailsArray()[0].getCertDetails();
+ final byte[] encodedIncomingRequest = incomingRequest.getEncoded();
if (!ConfigLogger.log(
INTERFACE_NAME,
"InventoryInterface.checkRevocationRequest(byte[], String, String, String, byte[])",
- () -> {
- try {
- return inventory.checkRevocationRequest(
- persistencyContext.getTransactionId(),
- ifNotNull(incomingRequest.getHeader().getSender(), sender -> X500Name.getInstance(
- sender.getName())
- .toString()),
- ifNotNull(revTemplate, template -> template.getSerialNumber()
- .toString()),
- ifNotNull(revTemplate, template -> template.getIssuer()
- .toString()),
- incomingRequest.getEncoded());
- } catch (final IOException e) {
- throw new RuntimeException(e);
- }
- })) {
+ () -> inventory.checkRevocationRequest(
+ persistencyContext.getTransactionId(),
+ ifNotNull(incomingRequest.getHeader().getSender(), sender -> X500Name.getInstance(
+ sender.getName())
+ .toString()),
+ ifNotNull(revTemplate, template -> template.getSerialNumber()
+ .toString()),
+ ifNotNull(revTemplate, template -> template.getIssuer()
+ .toString()),
+ encodedIncomingRequest))) {
throw new CmpValidationException(
INTERFACE_NAME, PKIFailureInfo.badRequest, "request refused by external inventory");
}
@@ -662,7 +652,8 @@ private PKIMessage handleRevocationRequest(PKIMessage incomingRequest, Persisten
}
private PKIMessage handleValidatedRequest(
- final PKIMessage incomingRequest, final PersistencyContext persistencyContext) throws Exception {
+ final PKIMessage incomingRequest, final PersistencyContext persistencyContext)
+ throws BaseCmpException, IOException {
// request pre processing
// by default there is no pre processing
PKIMessage preprocessedRequest = incomingRequest;
@@ -794,13 +785,7 @@ private PKIMessage processCertResponse(
}
final List issuingChain = issuingChainAsX509.stream()
.filter(x -> !x.equals(enrolledCertificateAsX509))
- .map(x -> {
- try {
- return CMPCertificate.getInstance(x.getEncoded());
- } catch (final CertificateEncodingException e) {
- throw new RuntimeException(e);
- }
- })
+ .map(wrap(x -> CMPCertificate.getInstance(x.getEncoded())))
.collect(Collectors.toList());
persistencyContext.setIssuingChain(issuingChain);
@@ -812,27 +797,20 @@ private PKIMessage processCertResponse(
persistencyContext.getCertProfile(),
responseType);
if (inventory != null) {
+ final byte[] encodedEnrolledCertificate = enrolledCertificate.getEncoded();
if (!ConfigLogger.log(
INTERFACE_NAME,
"InventoryInterface.learnEnrollmentResult(byte[], byte[], String, String, String)",
- () -> {
- try {
- return inventory.learnEnrollmentResult(
- persistencyContext.getTransactionId(),
- enrolledCertificate.getEncoded(),
- enrolledCertificateAsX509
- .getSerialNumber()
- .toString(),
- enrolledCertificateAsX509
- .getSubjectX500Principal()
- .toString(),
- enrolledCertificateAsX509
- .getIssuerX500Principal()
- .toString());
- } catch (final IOException e) {
- throw new RuntimeException(e);
- }
- })) {
+ () -> inventory.learnEnrollmentResult(
+ persistencyContext.getTransactionId(),
+ encodedEnrolledCertificate,
+ enrolledCertificateAsX509.getSerialNumber().toString(),
+ enrolledCertificateAsX509
+ .getSubjectX500Principal()
+ .toString(),
+ enrolledCertificateAsX509
+ .getIssuerX500Principal()
+ .toString()))) {
throw new CmpEnrollmentException(
incomingRequest.getBody().getType(),
INTERFACE_NAME,
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 2ff2c274..d3eae4e8 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/ServiceImplementation.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/ServiceImplementation.java
@@ -76,7 +76,7 @@ class ServiceImplementation {
* @param config specific configuration
* @throws Exception in case of error
*/
- ServiceImplementation(final Configuration config) throws Exception {
+ ServiceImplementation(final Configuration config) {
this.config = config;
}
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/msgvalidation/MessageBodyValidator.java b/src/main/java/com/siemens/pki/cmpracomponent/msgvalidation/MessageBodyValidator.java
index c2aa925c..99f884ec 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/msgvalidation/MessageBodyValidator.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/msgvalidation/MessageBodyValidator.java
@@ -189,15 +189,17 @@ private void assertValueNotNull(final Object value, final int failInfo, final St
@Override
public String validate(final PKIMessage message) throws BaseCmpException {
try {
- final ASN1GeneralizedTime messageTime = message.getHeader().getMessageTime();
- if (messageTime != null) {
- final long diffInMillis = messageTime.getDate().getTime() - new Date().getTime();
- if (!ConfigLogger.log(
- interfaceName,
- "CmpMessageInterface.isMessageTimeDeviationAllowed(long)",
- () -> cmpInterfaceConfig.isMessageTimeDeviationAllowed(diffInMillis / 1000L))) {
- throw new CmpValidationException(
- interfaceName, PKIFailureInfo.badTime, "message time out of allowed range");
+ if (cmpInterfaceConfig != null) {
+ final ASN1GeneralizedTime messageTime = message.getHeader().getMessageTime();
+ if (messageTime != null) {
+ final long diffInMillis = messageTime.getDate().getTime() - new Date().getTime();
+ if (!ConfigLogger.log(
+ interfaceName,
+ "CmpMessageInterface.isMessageTimeDeviationAllowed(long)",
+ () -> cmpInterfaceConfig.isMessageTimeDeviationAllowed(diffInMillis / 1000L))) {
+ throw new CmpValidationException(
+ interfaceName, PKIFailureInfo.badTime, "message time out of allowed range");
+ }
}
}
@@ -301,6 +303,7 @@ private void validateCrmfCertReq(
final CertRequest certReq = certReqMsg.getCertReq();
assertEnrollmentEqual(enrollmentType, certReq.getCertReqId(), ASN1INTEGER_0, CERT_REQ_ID_MUST_BE_0);
final CertTemplate certTemplate = certReq.getCertTemplate();
+ assertEnrollmentValueNotNull(enrollmentType, certTemplate, PKIFailureInfo.badCertTemplate, "cert template");
final int versionInTemplate = certTemplate.getVersion();
if (versionInTemplate != -1 && versionInTemplate != 2) {
throw new CmpEnrollmentException(
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/protection/MacProtection.java b/src/main/java/com/siemens/pki/cmpracomponent/protection/MacProtection.java
index c47c032a..0dbe205b 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/protection/MacProtection.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/protection/MacProtection.java
@@ -23,6 +23,8 @@
import com.siemens.pki.cmpracomponent.configuration.SharedSecretCredentialContext;
import com.siemens.pki.cmpracomponent.cryptoservices.WrappedMac;
import com.siemens.pki.cmpracomponent.util.ConfigLogger;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
import java.util.List;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.DERBitString;
@@ -61,7 +63,8 @@ public AlgorithmIdentifier getProtectionAlg() {
}
@Override
- public synchronized DERBitString getProtectionFor(final ProtectedPart protectedPart) throws Exception {
+ public synchronized DERBitString getProtectionFor(final ProtectedPart protectedPart)
+ throws GeneralSecurityException, IOException {
return new DERBitString(protectingMac.calculateMac(protectedPart.getEncoded(ASN1Encoding.DER)));
}
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/protection/NoProtection.java b/src/main/java/com/siemens/pki/cmpracomponent/protection/NoProtection.java
index dead1cd4..ae0d3306 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/protection/NoProtection.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/protection/NoProtection.java
@@ -17,6 +17,8 @@
*/
package com.siemens.pki.cmpracomponent.protection;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
import java.util.List;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DEROctetString;
@@ -46,7 +48,8 @@ public AlgorithmIdentifier getProtectionAlg() {
}
@Override
- public DERBitString getProtectionFor(final ProtectedPart protectedPart) throws Exception {
+ public DERBitString getProtectionFor(final ProtectedPart protectedPart)
+ throws GeneralSecurityException, IOException {
return null;
}
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/protection/ProtectionProvider.java b/src/main/java/com/siemens/pki/cmpracomponent/protection/ProtectionProvider.java
index e4dac948..6cbb23ef 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/protection/ProtectionProvider.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/protection/ProtectionProvider.java
@@ -17,6 +17,9 @@
*/
package com.siemens.pki.cmpracomponent.protection;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.cert.CertificateException;
import java.util.List;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DEROctetString;
@@ -39,9 +42,10 @@ public interface ProtectionProvider {
/**
* get extra certs used for protection
* @return extra certs used for protection
- * @throws Exception in case of error
+ * @throws CertificateException in case of error
+ * @throws GeneralSecurityException in case of error
*/
- List getProtectingExtraCerts() throws Exception;
+ List getProtectingExtraCerts() throws GeneralSecurityException;
/**
* get protection algorithm
@@ -54,9 +58,10 @@ public interface ProtectionProvider {
*
* @param protectedPart message part covered by protection
* @return the protection string
- * @throws Exception in case of error
+ * @throws IOException in case of encoding error
+ * @throws GeneralSecurityException in case of error
*/
- DERBitString getProtectionFor(ProtectedPart protectedPart) throws Exception;
+ DERBitString getProtectionFor(ProtectedPart protectedPart) throws GeneralSecurityException, IOException;
/**
* get sender to use for protected message
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/protection/SignatureBasedProtection.java b/src/main/java/com/siemens/pki/cmpracomponent/protection/SignatureBasedProtection.java
index 3199130a..e2d2bef9 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/protection/SignatureBasedProtection.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/protection/SignatureBasedProtection.java
@@ -20,6 +20,8 @@
import com.siemens.pki.cmpracomponent.configuration.SignatureCredentialContext;
import com.siemens.pki.cmpracomponent.cryptoservices.BaseCredentialService;
import com.siemens.pki.cmpracomponent.cryptoservices.CertUtility;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
import java.security.Signature;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
@@ -76,7 +78,8 @@ public AlgorithmIdentifier getProtectionAlg() {
}
@Override
- public DERBitString getProtectionFor(final ProtectedPart protectedPart) throws Exception {
+ public DERBitString getProtectionFor(final ProtectedPart protectedPart)
+ throws GeneralSecurityException, IOException {
final Signature sig = Signature.getInstance(getSignatureAlgorithmName());
sig.initSign(getPrivateKey());
sig.update(protectedPart.getEncoded(ASN1Encoding.DER));
diff --git a/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCentralKeyGenerationWithKeyAgreement.java b/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCentralKeyGenerationWithKeyAgreement.java
index bd527b60..981c5b6a 100644
--- a/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCentralKeyGenerationWithKeyAgreement.java
+++ b/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCentralKeyGenerationWithKeyAgreement.java
@@ -41,13 +41,24 @@
import com.siemens.pki.cmpracomponent.test.framework.TestUtils;
import com.siemens.pki.cmpracomponent.test.framework.TrustChainAndPrivateKey;
import com.siemens.pki.cmpracomponent.util.MessageDumper;
+import java.io.IOException;
import java.math.BigInteger;
+import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.cmp.PKIBody;
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -67,52 +78,67 @@ public class TestCentralKeyGenerationWithKeyAgreement extends EnrollmentTestcase
private static final Logger LOGGER = LoggerFactory.getLogger(TestCentralKeyGenerationWithKeyAgreement.class);
public static Object[][] inputList = {
+ {
+ DEFAULT_KEY_AGREEMENT,
+ DEFAULT_KEY_ENCRYPTION,
+ X9ObjectIdentifiers.id_ecPublicKey.getId(),
+ SECObjectIdentifiers.secp256r1.getId()
+ },
+ {DEFAULT_KEY_AGREEMENT, DEFAULT_KEY_ENCRYPTION, EdECObjectIdentifiers.id_Ed448.getId(), null},
//
- {DEFAULT_KEY_AGREEMENT, DEFAULT_KEY_ENCRYPTION},
+ {DEFAULT_KEY_AGREEMENT, DEFAULT_KEY_ENCRYPTION, EdECObjectIdentifiers.id_Ed25519.getId(), null},
//
- {DEFAULT_KEY_AGREEMENT, "2.16.840.1.101.3.4.1.5"},
+ {DEFAULT_KEY_AGREEMENT, DEFAULT_KEY_ENCRYPTION, PKCSObjectIdentifiers.rsaEncryption.getId(), null},
//
- {DEFAULT_KEY_AGREEMENT, "2.16.840.1.101.3.4.1.25"},
+ {DEFAULT_KEY_AGREEMENT, DEFAULT_KEY_ENCRYPTION, null, null},
//
- {DEFAULT_KEY_AGREEMENT, "2.16.840.1.101.3.4.1.45"},
+ {DEFAULT_KEY_AGREEMENT, "2.16.840.1.101.3.4.1.5", null, null},
//
+ {DEFAULT_KEY_AGREEMENT, "2.16.840.1.101.3.4.1.25", null, null},
//
- {"1.3.132.1.11.0", DEFAULT_KEY_ENCRYPTION},
+ {DEFAULT_KEY_AGREEMENT, "2.16.840.1.101.3.4.1.45", null, null},
//
- {"1.3.132.1.11.1", DEFAULT_KEY_ENCRYPTION},
//
- {"1.3.132.1.11.2", DEFAULT_KEY_ENCRYPTION},
+ {"1.3.132.1.11.0", DEFAULT_KEY_ENCRYPTION, null, null},
//
- {"1.3.132.1.11.3", DEFAULT_KEY_ENCRYPTION},
+ {"1.3.132.1.11.1", DEFAULT_KEY_ENCRYPTION, null, null},
//
+ {"1.3.132.1.11.2", DEFAULT_KEY_ENCRYPTION, null, null},
//
- {"1.3.132.1.14.0", DEFAULT_KEY_ENCRYPTION},
+ {"1.3.132.1.11.3", DEFAULT_KEY_ENCRYPTION, null, null},
//
- {"1.3.132.1.14.1", DEFAULT_KEY_ENCRYPTION},
//
- {"1.3.132.1.14.2", DEFAULT_KEY_ENCRYPTION},
+ {"1.3.132.1.14.0", DEFAULT_KEY_ENCRYPTION, null, null},
//
- {"1.3.132.1.14.3", DEFAULT_KEY_ENCRYPTION},
+ {"1.3.132.1.14.1", DEFAULT_KEY_ENCRYPTION, null, null},
//
+ {"1.3.132.1.14.2", DEFAULT_KEY_ENCRYPTION, null, null},
//
- {"1.3.132.1.15.0", DEFAULT_KEY_ENCRYPTION},
+ {"1.3.132.1.14.3", DEFAULT_KEY_ENCRYPTION, null, null},
//
- {"1.3.132.1.15.1", DEFAULT_KEY_ENCRYPTION},
//
- {"1.3.132.1.15.2", DEFAULT_KEY_ENCRYPTION},
+ {"1.3.132.1.15.0", DEFAULT_KEY_ENCRYPTION, null, null},
//
- {"1.3.132.1.15.3", DEFAULT_KEY_ENCRYPTION},
+ {"1.3.132.1.15.1", DEFAULT_KEY_ENCRYPTION, null, null},
+ //
+ {"1.3.132.1.15.2", DEFAULT_KEY_ENCRYPTION, null, null},
+ //
+ {"1.3.132.1.15.3", DEFAULT_KEY_ENCRYPTION, null, null},
//
//
};
- @Parameters(name = "{index}: keyAgreement=>{0}, keyEncryption=>{1}")
+ @Parameters(name = "{index}: keyAgreement=>{0}, keyEncryption=>{1}, EnrollmentKeyType=>{2}")
public static List