diff --git a/README.md b/README.md
index 492fc95a..f32355ca 100644
--- a/README.md
+++ b/README.md
@@ -332,3 +332,24 @@ at `target/site/apidocs/com/siemens/pki/cmpracomponent/main/CmpRaComponent.html`
# Acknowledgements
This work was partly funded by the German Federal Ministry of Education and Research in the project Quoryptan through grant number **16KIS2033**.
+
+# Support for Remote Attestation in Certificate Signing Requests (CSRs)
+
+This branch supports the internet drafts [Use of Remote Attestation with Certification Signing Requests](https://datatracker.ietf.org/doc/draft-ietf-lamps-csr-attestation/) and [Nonce-based Freshness for Remote Attestation in Certificate Signing Requests (CSRs) for the Certification Management Protocol (CMP) and for Enrollment over Secure Transport (EST) draft-ietf-lamps-attestation-freshness](https://datatracker.ietf.org/doc/draft-ietf-lamps-attestation-freshness/).
+
+## Design
+
+The [RA configuration interface](src/main/java/com/siemens/pki/cmpracomponent/configuration/Configuration.java) allows to
+register an [`com.siemens.pki.cmpracomponent.configuration.RatVerifierAdapter`](src\main\java\com\siemens\pki\cmpracomponent\configuration\RatVerifierAdapter.java) interface to an external Verifier. This interface is called
+by a modified [`com.siemens.pki.cmpracomponent.msgprocessing.ServiceImplementation`](src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/ServiceImplementation.java) to obtain a fresh RAT nonce via
+GENM/GENREP and also by the [`com.siemens.pki.cmpracomponent.msgprocessing.RaDownstream`](src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/RaDownstream.java) to process the subsequent CRMF template.
+
+The [Client configuration interface](./src/main/java/com/siemens/pki/cmpclientcomponent/configuration/ClientContext.java) allows to register an [`com.siemens.pki.cmpclientcomponent.configuration.ClientAttestationContext`](src/main/java/com/siemens/pki/cmpclientcomponent/configuration/ClientAttestationContext.java) interface to an external Attester.
+
+The package [`com.siemens.pki.verifieradapter.asn1`](src/main/java/com/siemens/pki/verifieradapter/asn1) supports some ASN.1 definitons from [Use of Remote Attestation with Certification Signing Requests](https://datatracker.ietf.org/doc/draft-ietf-lamps-csr-attestation/).
+
+## Test case
+
+The [`com.siemens.pki.cmpclientcomponent.test.TestCrWithRAT`](src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCrWithRAT.java) shows setup and execution of a RAT sequence.
+
+
diff --git a/src/main/java/com/siemens/pki/cmpclientcomponent/configuration/ClientAttestationContext.java b/src/main/java/com/siemens/pki/cmpclientcomponent/configuration/ClientAttestationContext.java
new file mode 100644
index 00000000..fd08ac32
--- /dev/null
+++ b/src/main/java/com/siemens/pki/cmpclientcomponent/configuration/ClientAttestationContext.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2024 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.configuration;
+
+import com.siemens.pki.verifieradapter.asn1.EvidenceBundle;
+import com.siemens.pki.verifieradapter.asn1.EvidenceStatement;
+import com.siemens.pki.verifieradapter.asn1.NonceResponseValue.NonceResponse;
+import java.math.BigInteger;
+import org.bouncycastle.asn1.x509.Certificate;
+
+/**
+ * attestation specific configuration
+ *
+ */
+public interface ClientAttestationContext {
+
+ /**
+ * obtain evidence statement from attestation
+ * @param attestationNonce DER encoded {@link NonceResponse}
+ * @return DER encoded {@link EvidenceStatement}
+ */
+ byte[] getEvidenceStatement(byte[] attestationNonce);
+
+ /**
+ * indicates which Verifier to request a nonce from
+ * @return hint or null
+ */
+ default String getNonceRequestHint() {
+ return null;
+ }
+
+ /**
+ * indicates the required length of the requested nonce
+ * @return length or null
+ */
+ default BigInteger getNonceRequestLen() {
+ return null;
+ }
+ /**
+ * indicates which Evidence type to request a nonce for
+ * @return OID formatted string or null
+ */
+ default String getNonceRequestType() {
+ return null;
+ }
+
+ /**
+ * Siemens proprietary extension to carry additional data
+ * @return additional data or null
+ */
+ default byte[] getNonceRequestVendorextension() {
+ return null;
+ }
+ /**
+ * get certs to include in {@link EvidenceBundle}
+ * @return certs
+ */
+ default Certificate[] getEvidenceBundleCerts() {
+ return null;
+ }
+}
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 06b58b6e..f9893541 100644
--- a/src/main/java/com/siemens/pki/cmpclientcomponent/configuration/ClientContext.java
+++ b/src/main/java/com/siemens/pki/cmpclientcomponent/configuration/ClientContext.java
@@ -23,6 +23,16 @@
*/
public interface ClientContext {
+ /**
+ * get remote attestation specific configuration
+ * @return remote attestation specific configuration
+ * or null is no remote attestation shall
+ * be used
+ */
+ default ClientAttestationContext getAttestationContext() {
+ return null;
+ }
+
/**
* get enrollment specific configuration
*
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 3bc3ab93..cf46beae 100644
--- a/src/main/java/com/siemens/pki/cmpclientcomponent/main/ClientRequestHandler.java
+++ b/src/main/java/com/siemens/pki/cmpclientcomponent/main/ClientRequestHandler.java
@@ -144,7 +144,7 @@ private void validateResponse(final PKIMessage response) throws BaseCmpException
private final ValidatorAndProtector nestedValidatorAndProtector;
/**
- * @param certProfile certificate profile to be used for enrollment.
+ * @param certProfile certificate profile to be used for enrollment or
* null if no certificate profile
* should be used.
*
@@ -184,6 +184,21 @@ PKIMessage buildFurtherRequest(final PKIMessage formerResponse, final PKIBody re
false);
}
+ PKIMessage buildFurtherRequest(
+ final PKIMessage formerResponse,
+ final PKIBody requestBody,
+ final boolean withImplicitConfirm,
+ final int pvno)
+ throws Exception {
+ final PKIHeader formerResponseHeader = formerResponse.getHeader();
+ return buildRequest(
+ requestBody,
+ formerResponseHeader.getTransactionID(),
+ formerResponseHeader.getSenderNonce(),
+ pvno,
+ withImplicitConfirm);
+ }
+
PKIMessage buildInitialRequest(final PKIBody requestBody, final boolean withImplicitConfirm) throws Exception {
return buildInitialRequest(requestBody, withImplicitConfirm, DEFAULT_PVNO);
}
@@ -297,6 +312,10 @@ PKIBody sendReceiveInitialBody(final PKIBody body, final boolean withImplicitCon
.getBody();
}
+ PKIMessage sendReceiveInitialMessage(final PKIBody body) throws Exception {
+ return sendReceiveValidateMessage(buildInitialRequest(body, false), body.getType());
+ }
+
PKIMessage sendReceiveValidateMessage(PKIMessage request, final int firstRequestType) throws Exception {
if (nestedValidatorAndProtector != null) {
request = nestedValidatorAndProtector
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 9d6b446c..965e8b8c 100644
--- a/src/main/java/com/siemens/pki/cmpclientcomponent/main/CmpClient.java
+++ b/src/main/java/com/siemens/pki/cmpclientcomponent/main/CmpClient.java
@@ -20,8 +20,10 @@
import static com.siemens.pki.cmpracomponent.util.NullUtil.defaultIfNull;
import static com.siemens.pki.cmpracomponent.util.NullUtil.ifNotNull;
+import com.siemens.pki.cmpclientcomponent.configuration.ClientAttestationContext;
import com.siemens.pki.cmpclientcomponent.configuration.ClientContext;
import com.siemens.pki.cmpclientcomponent.configuration.EnrollmentContext;
+import com.siemens.pki.cmpclientcomponent.configuration.EnrollmentContext.TemplateExtension;
import com.siemens.pki.cmpclientcomponent.configuration.RevocationContext;
import com.siemens.pki.cmpracomponent.configuration.CmpMessageInterface;
import com.siemens.pki.cmpracomponent.configuration.CrlUpdateRetrievalHandler;
@@ -40,6 +42,13 @@
import com.siemens.pki.cmpracomponent.protection.ProtectionProvider;
import com.siemens.pki.cmpracomponent.protection.SignatureBasedProtection;
import com.siemens.pki.cmpracomponent.util.MessageDumper;
+import com.siemens.pki.verifieradapter.asn1.AttestationObjectIdentifiers;
+import com.siemens.pki.verifieradapter.asn1.EvidenceBundle;
+import com.siemens.pki.verifieradapter.asn1.EvidenceStatement;
+import com.siemens.pki.verifieradapter.asn1.NonceRequestValue;
+import com.siemens.pki.verifieradapter.asn1.NonceRequestValue.NonceRequest;
+import com.siemens.pki.verifieradapter.asn1.NonceResponseValue;
+import com.siemens.pki.verifieradapter.asn1.NonceResponseValue.NonceResponse;
import java.io.IOException;
import java.security.KeyPair;
import java.security.PrivateKey;
@@ -146,7 +155,7 @@ public interface EnrollmentResult {
/**
* ctor
*
- * @param certProfile certificate profile to be used for enrollment.
+ * @param certProfile certificate profile to be used for enrollment or
* null if no certificate profile
* should be used.
*
@@ -409,6 +418,51 @@ private boolean grantsImplicitConfirm(final PKIMessage msg) {
public EnrollmentResult invokeEnrollment() {
try {
+ PKIMessage ratNonceResponse = null;
+ EvidenceBundle evidenceBundle = null;
+ final ClientAttestationContext attestationContext = clientContext.getAttestationContext();
+ if (attestationContext != null) {
+ NonceRequest nonceRequest = new NonceRequest(
+ attestationContext.getNonceRequestLen(),
+ attestationContext.getNonceRequestType(),
+ attestationContext.getNonceRequestHint(),
+ attestationContext.getNonceRequestVendorextension());
+ NonceRequestValue nonceRequestValue = new NonceRequestValue(new NonceRequest[] {nonceRequest});
+ ratNonceResponse = requestHandler.sendReceiveInitialMessage(new PKIBody(
+ PKIBody.TYPE_GEN_MSG,
+ new GenMsgContent(new InfoTypeAndValue(
+ AttestationObjectIdentifiers.id_it_NonceRequest, nonceRequestValue))));
+ final PKIBody ratNonceResponseBody = ratNonceResponse.getBody();
+ if (ratNonceResponseBody.getType() != PKIBody.TYPE_GEN_REP) {
+ logUnexpectedResponse(ratNonceResponseBody);
+ return null;
+ }
+
+ final GenRepContent content = (GenRepContent) ratNonceResponseBody.getContent();
+ final InfoTypeAndValue[] itav = content.toInfoTypeAndValueArray();
+ if (itav != null) {
+ for (final InfoTypeAndValue aktitav : itav) {
+ if (AttestationObjectIdentifiers.id_it_NonceResponse.equals(aktitav.getInfoType())) {
+ final ASN1Encodable infoValue = aktitav.getInfoValue();
+ if (infoValue == null) {
+ LOGGER.error("no RAT nonce received");
+ return null;
+ }
+ final NonceResponseValue ratNonce = NonceResponseValue.getInstance(infoValue);
+ NonceResponse[] nonceResponses = ratNonce.getNonceResponse();
+ EvidenceStatement[] evidenceStatements = new EvidenceStatement[nonceResponses.length];
+ for (int i = 0; i < nonceResponses.length; i++) {
+ NonceResponse aktnonce = nonceResponses[i];
+ evidenceStatements[i] = EvidenceStatement.getInstance(
+ attestationContext.getEvidenceStatement(aktnonce.getEncoded()));
+ }
+ evidenceBundle =
+ new EvidenceBundle(evidenceStatements, attestationContext.getEvidenceBundleCerts());
+ break;
+ }
+ }
+ }
+ }
final EnrollmentContext enrollmentContext = clientContext.getEnrollmentContext();
final KeyPair certificateKeypair = enrollmentContext.getCertificateKeypair();
@@ -459,16 +513,25 @@ public EnrollmentResult invokeEnrollment() {
case PKIBody.TYPE_CERT_REQ:
case PKIBody.TYPE_INIT_REQ: {
final String subject = enrollmentContext.getSubject();
- final Extension[] arrayOfExtensions =
- ifNotNull(enrollmentContext.getExtensions(), exts -> exts.stream()
- .map(ext -> new Extension(
- new ASN1ObjectIdentifier(ext.getId()), ext.isCritical(), ext.getValue()))
- .toArray(Extension[]::new));
- final Extensions extensions = ifNotNull(arrayOfExtensions, Extensions::new);
+ final List extensions = new ArrayList<>();
+
+ final List extensionsFromConfig = enrollmentContext.getExtensions();
+ if (extensionsFromConfig != null) {
+ extensionsFromConfig.stream()
+ .map(ext -> new Extension(
+ new ASN1ObjectIdentifier(ext.getId()), ext.isCritical(), ext.getValue()))
+ .forEach(extensions::add);
+ }
+ if (evidenceBundle != null) {
+ extensions.add(
+ Extension.create(AttestationObjectIdentifiers.id_aa_evidence, false, evidenceBundle));
+ }
final CertTemplateBuilder ctb = new CertTemplateBuilder()
.setSubject(ifNotNull(subject, X500Name::new))
- .setPublicKey(enrolledPublicKeyInfo)
- .setExtensions(extensions);
+ .setPublicKey(enrolledPublicKeyInfo);
+ if (!extensions.isEmpty()) {
+ ctb.setExtensions(new Extensions(extensions.toArray(new Extension[extensions.size()])));
+ }
requestBody = PkiMessageGenerator.generateIrCrKurBody(
enrollmentType, ctb.build(), null, enrolledPrivateKey);
pvno = enrolledPrivateKey == null ? PKIHeader.CMP_2021 : PKIHeader.CMP_2000;
@@ -478,9 +541,13 @@ public EnrollmentResult invokeEnrollment() {
LOGGER.error("EnrollmentType must be 0(ir), 2(cr), 7(kur) or 4(p10cr)");
return null;
}
- final PKIMessage responseMessage = requestHandler.sendReceiveValidateMessage(
- requestHandler.buildInitialRequest(requestBody, enrollmentContext.getRequestImplictConfirm(), pvno),
- enrollmentType);
+ final PKIMessage enrollmentRequestMessage = ratNonceResponse == null
+ ? requestHandler.buildInitialRequest(
+ requestBody, enrollmentContext.getRequestImplictConfirm(), pvno)
+ : requestHandler.buildFurtherRequest(
+ ratNonceResponse, requestBody, enrollmentContext.getRequestImplictConfirm(), pvno);
+ final PKIMessage responseMessage =
+ requestHandler.sendReceiveValidateMessage(enrollmentRequestMessage, enrollmentType);
final PKIBody responseBody = responseMessage.getBody();
final int responseMessageType = responseBody.getType();
if (enrollmentType == PKIBody.TYPE_P10_CERT_REQ) {
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/configuration/Configuration.java b/src/main/java/com/siemens/pki/cmpracomponent/configuration/Configuration.java
index a48f2e75..a8aab44e 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/configuration/Configuration.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/configuration/Configuration.java
@@ -89,6 +89,19 @@ public interface Configuration {
*/
InventoryInterface getInventory(String certProfile, int bodyType);
+ /**
+ * optionally access function to external remote attestation verify adapter
+ *
+ * @param certProfile certificate profile extracted from the CMP request header
+ * generalInfo field or null if no certificate
+ * profile was specified
+ * @param bodyType request/response PKI Message Body type
+ * @return external RatVerifierAdapter or null
+ */
+ default RatVerifierAdapter getVerifierAdapter(String certProfile, int bodyType) {
+ return null;
+ }
+
/**
* provide a persistence implementation
*
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/configuration/CrlUpdateRetrievalHandler.java b/src/main/java/com/siemens/pki/cmpracomponent/configuration/CrlUpdateRetrievalHandler.java
index b6255105..fe606076 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/configuration/CrlUpdateRetrievalHandler.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/configuration/CrlUpdateRetrievalHandler.java
@@ -37,7 +37,7 @@ public interface CrlUpdateRetrievalHandler extends SupportMessageHandlerInterfac
* null if absent in request
* @param issuer issuer from CRLSource or null
* if absent in request
- * @param thisUpdate thisUpdate time from CRLStatus in request
+ * @param thisUpdate thisUpdate time from CRLStatus in request or
* null if absent in request
* @return CRLs to be returned or null if the returned infoValue
* should be absent
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/configuration/GetFreshRatNonceHandler.java b/src/main/java/com/siemens/pki/cmpracomponent/configuration/GetFreshRatNonceHandler.java
new file mode 100644
index 00000000..4a110bc5
--- /dev/null
+++ b/src/main/java/com/siemens/pki/cmpracomponent/configuration/GetFreshRatNonceHandler.java
@@ -0,0 +1,27 @@
+/*
+ * 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.cmpracomponent.configuration;
+
+/**
+ * support message handler supporting Remote Attestation Procedures nonce genm
+ * requests, see https://datatracker.ietf.org/doc/draft-ietf-rats-reference-interaction-models/ ,
+ * and https://github.com/veraison/docs/blob/main/api/challenge-response/README.md
+ */
+public interface GetFreshRatNonceHandler extends SupportMessageHandlerInterface {
+ // up to now only a placeholder
+}
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/configuration/RatVerifierAdapter.java b/src/main/java/com/siemens/pki/cmpracomponent/configuration/RatVerifierAdapter.java
new file mode 100644
index 00000000..7cb76342
--- /dev/null
+++ b/src/main/java/com/siemens/pki/cmpracomponent/configuration/RatVerifierAdapter.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2025 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.cmpracomponent.configuration;
+
+import com.siemens.pki.verifieradapter.asn1.AttestationResult;
+import com.siemens.pki.verifieradapter.asn1.EvidenceStatement;
+import java.math.BigInteger;
+
+/**
+ * adapter to remote attestation verfier
+ */
+public interface RatVerifierAdapter {
+
+ /**
+ * turn evidence into verification result
+ * @param transactionId current CMP transactionId, used to map related calls to getFreshRatNonce and processRatVerification
+ * @param evidence evidence provided by EE as DER encoded {@link EvidenceStatement}
+ * @return verification result as DER encoded {@link AttestationResult}
+ */
+ byte[] processRatVerification(byte[] transactionId, byte[] evidence);
+
+ interface NonceResponseRet {
+
+ /**
+ * returns the nonce of length len provided by the Verifier indicated with hint
+ * @return nonce
+ */
+ byte[] getNonce();
+
+ /**
+ * indicates how long in seconds the Verifier considers the nonce valid
+ * @return time in seconds or null
+ */
+ default Integer getExpiry() {
+ return null;
+ }
+
+ /**
+ * indicates which Verifier to request a nonce from
+ * @return hint or null
+ */
+ default String getHint() {
+ return null;
+ }
+
+ /**
+ * indicates which Evidence type to request a nonce for
+ * @return OID or null
+ */
+ default String getType() {
+ return null;
+ }
+
+ /**
+ * Siemens proprietary extension to carry additional data
+ * @return additional data or null
+ */
+ default byte[] getVendorextension() {
+ return null;
+ }
+ }
+
+ /**
+ * Generate nonce
+ * @param transactionId current CMP transactionId, used to map related calls to getFreshRatNonce and processRatVerification
+ * @param len the required length of the requested nonce, maybe null
+ * @param type indicates which Evidence type to request a nonce for, OID as string or null
+ * @param hint indicates which Verifier to request a nonce from, maybe null
+ * @param vendorextension additional Siemens proprietary data, maybe null
+ * @param encodedNonceRequest encoded NonceRequest containing len, type and hint
+ * @return fresh BER encoded NonceResponse
+ */
+ NonceResponseRet generateNonce(
+ byte[] transactionId,
+ BigInteger len,
+ String type,
+ String hint,
+ byte[] vendorextension,
+ byte[] encodedNonceRequest);
+}
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 755c55e5..8011415a 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/RaDownstream.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/RaDownstream.java
@@ -26,6 +26,7 @@
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.RatVerifierAdapter;
import com.siemens.pki.cmpracomponent.configuration.SignatureCredentialContext;
import com.siemens.pki.cmpracomponent.cryptoservices.AlgorithmHelper;
import com.siemens.pki.cmpracomponent.cryptoservices.CertUtility;
@@ -52,6 +53,11 @@
import com.siemens.pki.cmpracomponent.util.ConfigLogger;
import com.siemens.pki.cmpracomponent.util.MessageDumper;
import com.siemens.pki.cmpracomponent.util.NullUtil.ExFunction;
+import com.siemens.pki.verifieradapter.asn1.AttestationObjectIdentifiers;
+import com.siemens.pki.verifieradapter.asn1.AttestationResult;
+import com.siemens.pki.verifieradapter.asn1.AttestationResultBundle;
+import com.siemens.pki.verifieradapter.asn1.EvidenceBundle;
+import com.siemens.pki.verifieradapter.asn1.EvidenceStatement;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
@@ -66,6 +72,7 @@
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
@@ -94,6 +101,8 @@
import org.bouncycastle.asn1.pkcs.CertificationRequest;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
@@ -194,7 +203,7 @@ protected CmsEncryptorBase buildEncryptor(
*/
private PKIMessage handleCrmfCertificateRequest(
final PKIMessage incomingCertificateRequest, final PersistencyContext persistencyContext)
- throws BaseCmpException, GeneralSecurityException, IOException {
+ throws BaseCmpException, GeneralSecurityException, IOException, InterruptedException {
final PKIBody requestBody = incomingCertificateRequest.getBody();
final PKIBody body = requestBody;
@@ -204,6 +213,38 @@ private PKIMessage handleCrmfCertificateRequest(
CertRequest certRequest = certReqMsg.getCertReq();
CertTemplate certTemplate = certRequest.getCertTemplate();
+ // process RAT verification
+ final RatVerifierAdapter verifyAdapter = ConfigLogger.logOptional(
+ INTERFACE_NAME,
+ "Configuration.getVerifierAdapter",
+ config::getVerifierAdapter,
+ persistencyContext.getCertProfile(),
+ requestBodyType);
+ if (verifyAdapter != null) {
+ final Extensions extensions = certTemplate.getExtensions();
+ final Extension ratExtension =
+ processRatVerification(verifyAdapter, persistencyContext.getTransactionId(), extensions);
+ if (ratExtension != null) {
+ final Extensions newExtensions = new Extensions(Stream.concat(
+ Arrays.stream(extensions.getExtensionOIDs())
+ .filter(oid -> !oid.equals(AttestationObjectIdentifiers.id_aa_evidence))
+ .map(extensions::getExtension),
+ Arrays.asList(ratExtension).stream())
+ .toArray(Extension[]::new));
+ certTemplate = new CertTemplateBuilder()
+ .setVersion(certTemplate.getVersion())
+ .setSerialNumber(certTemplate.getSerialNumber())
+ .setSigningAlg(certTemplate.getSigningAlg())
+ .setIssuer(certTemplate.getIssuer())
+ .setValidity(certTemplate.getValidity())
+ .setSubject(certTemplate.getSubject())
+ .setPublicKey(certTemplate.getPublicKey())
+ .setExtensions(newExtensions)
+ .build();
+ certRequest = new CertRequest(0, certTemplate, certRequest.getControls());
+ }
+ }
+
// check request against inventory
final InventoryInterface inventory = ConfigLogger.logOptional(
INTERFACE_NAME,
@@ -675,6 +716,8 @@ private PKIMessage handleValidatedRequest(final PKIMessage incomingRequest, fina
preprocessedRequest = handleCrmfCertificateRequest(incomingRequest, persistencyContext);
} catch (final BaseCmpException ex) {
throw ex;
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
} catch (final Exception ex) {
throw new CmpEnrollmentException(
incomingRequest.getBody().getType(),
@@ -692,9 +735,11 @@ private PKIMessage handleValidatedRequest(final PKIMessage incomingRequest, fina
case PKIBody.TYPE_GEN_MSG:
// try to handle locally
persistencyContext.setRequestType(incomingRequest.getBody().getType());
- final PKIMessage genmResponse =
- new ServiceImplementation(config).handleValidatedInputMessage(incomingRequest, messageContext);
+ persistencyContext.trackMessage(preprocessedRequest);
+ final PKIMessage genmResponse = new ServiceImplementation(config, persistencyContext)
+ .handleValidatedInputMessage(incomingRequest, messageContext);
if (genmResponse != null) {
+ persistencyContext.trackMessage(genmResponse);
return genmResponse;
}
break;
@@ -887,4 +932,27 @@ private PKIMessage processCertResponse(
"could not properly process certificate response: " + ex);
}
}
+
+ private Extension processRatVerification(
+ final RatVerifierAdapter verifyAdapter, final byte[] transactionId, Extensions extensions)
+ throws IOException {
+ if (extensions == null) {
+ return null;
+ }
+ final Extension evidenceItav = extensions.getExtension(AttestationObjectIdentifiers.id_aa_evidence);
+ if (evidenceItav == null) {
+ return null;
+ }
+ EvidenceBundle evidenceBundle = EvidenceBundle.getInstance(evidenceItav.getParsedValue());
+ EvidenceStatement[] evidences = evidenceBundle.getEvidences();
+ AttestationResult[] attestationResults = new AttestationResult[evidences.length];
+ for (int i = 0; i < evidences.length; i++) {
+ attestationResults[i] = AttestationResult.getInstance(
+ verifyAdapter.processRatVerification(transactionId, evidences[i].getEncoded()));
+ }
+ return Extension.create(
+ AttestationObjectIdentifiers.id_aa_ar,
+ false,
+ new AttestationResultBundle(attestationResults, evidenceBundle.getCerts()));
+ }
}
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 bb2b7328..a95c8085 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/ServiceImplementation.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/ServiceImplementation.java
@@ -23,8 +23,11 @@
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.GetFreshRatNonceHandler;
import com.siemens.pki.cmpracomponent.configuration.GetRootCaCertificateUpdateHandler;
import com.siemens.pki.cmpracomponent.configuration.GetRootCaCertificateUpdateHandler.RootCaCertificateUpdateResponse;
+import com.siemens.pki.cmpracomponent.configuration.RatVerifierAdapter;
+import com.siemens.pki.cmpracomponent.configuration.RatVerifierAdapter.NonceResponseRet;
import com.siemens.pki.cmpracomponent.configuration.SupportMessageHandlerInterface;
import com.siemens.pki.cmpracomponent.cryptoservices.CertUtility;
import com.siemens.pki.cmpracomponent.msggeneration.MsgOutputProtector;
@@ -33,6 +36,11 @@
import com.siemens.pki.cmpracomponent.msgvalidation.MessageContext;
import com.siemens.pki.cmpracomponent.persistency.PersistencyContext;
import com.siemens.pki.cmpracomponent.util.ConfigLogger;
+import com.siemens.pki.verifieradapter.asn1.AttestationObjectIdentifiers;
+import com.siemens.pki.verifieradapter.asn1.NonceRequestValue;
+import com.siemens.pki.verifieradapter.asn1.NonceRequestValue.NonceRequest;
+import com.siemens.pki.verifieradapter.asn1.NonceResponseValue;
+import com.siemens.pki.verifieradapter.asn1.NonceResponseValue.NonceResponse;
import java.io.IOException;
import java.security.cert.CRLException;
import java.security.cert.CertificateException;
@@ -42,9 +50,12 @@
import java.util.List;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1UTF8String;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.cmp.CMPCertificate;
import org.bouncycastle.asn1.cmp.CMPObjectIdentifiers;
@@ -72,13 +83,15 @@ class ServiceImplementation {
private static final String INTERFACE_NAME = "GENM service";
private final Configuration config;
+ private final PersistencyContext persistencyContext;
/**
* @param config specific configuration
* @throws Exception in case of error
*/
- ServiceImplementation(final Configuration config) {
+ ServiceImplementation(final Configuration config, PersistencyContext persistencyContext) {
this.config = config;
+ this.persistencyContext = persistencyContext;
}
private String[] generalNamesToStrings(final GeneralNames generalNames) {
@@ -254,6 +267,8 @@ protected PKIMessage handleValidatedInputMessage(final PKIMessage msg, final Mes
body = handleGetRootCaCertificateUpdate(itav, (GetRootCaCertificateUpdateHandler) messageHandler);
} else if (messageHandler instanceof CrlUpdateRetrievalHandler) {
body = handleCrlUpdateRetrieval(itav, (CrlUpdateRetrievalHandler) messageHandler);
+ } else if (messageHandler instanceof GetFreshRatNonceHandler) {
+ body = handleGetFreshRatNonce(msg, itav);
} else {
throw new CmpProcessingException(INTERFACE_NAME, PKIFailureInfo.systemFailure, "internal error");
}
@@ -277,4 +292,37 @@ protected PKIMessage handleValidatedInputMessage(final PKIMessage msg, final Mes
throw new CmpProcessingException(INTERFACE_NAME, e);
}
}
+
+ private PKIBody handleGetFreshRatNonce(final PKIMessage msg, final InfoTypeAndValue itav)
+ throws CmpProcessingException, RuntimeException, IOException {
+ final RatVerifierAdapter verifyAdapter = ConfigLogger.logOptional(
+ INTERFACE_NAME,
+ "Configuration.getVerifierAdapter",
+ config::getVerifierAdapter,
+ persistencyContext.getCertProfile(),
+ PKIBody.TYPE_GEN_MSG);
+ if (verifyAdapter == null) {
+ throw new CmpProcessingException(INTERFACE_NAME, PKIFailureInfo.systemFailure, "RAT not configured");
+ }
+ NonceRequestValue nonceRequestValue = NonceRequestValue.getInstance(itav.getInfoValue());
+ NonceRequest[] nonceRequests = nonceRequestValue.getNonceRequests();
+ NonceResponse[] responses = new NonceResponse[nonceRequests.length];
+ for (int i = 0; i < nonceRequests.length; i++) {
+ NonceRequest aktRequest = nonceRequests[i];
+ NonceResponseRet ret = verifyAdapter.generateNonce(
+ msg.getHeader().getTransactionID().getOctets(),
+ ifNotNull(aktRequest.getLen(), ASN1Integer::getValue),
+ ifNotNull(aktRequest.getType(), ASN1ObjectIdentifier::getId),
+ ifNotNull(aktRequest.getHint(), ASN1UTF8String::getString),
+ ifNotNull(aktRequest.getVendorextension(), ASN1OctetString::getOctets),
+ aktRequest.getEncoded());
+ responses[i] = new NonceResponse(
+ ret.getNonce(), ret.getExpiry(), ret.getType(), ret.getHint(), ret.getVendorextension());
+ }
+ persistencyContext.markAsPreparingGenm();
+ return new PKIBody(
+ PKIBody.TYPE_GEN_REP,
+ new GenRepContent(new InfoTypeAndValue(
+ AttestationObjectIdentifiers.id_it_NonceResponse, new NonceResponseValue(responses))));
+ }
}
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/persistency/PersistencyContext.java b/src/main/java/com/siemens/pki/cmpracomponent/persistency/PersistencyContext.java
index 7adabb7c..3936d92e 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/persistency/PersistencyContext.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/persistency/PersistencyContext.java
@@ -52,6 +52,7 @@ public class PersistencyContext {
private CMPCertificate enrolledCertificate;
private boolean implicitConfirmGranted;
private byte[] requestedPublicKey;
+ private boolean markedAsPreparingGenm;
@JsonIgnore
private List issuingChain;
@@ -216,6 +217,12 @@ public byte[] getTransactionId() {
public boolean isImplicitConfirmGranted() {
return implicitConfirmGranted;
}
+ /**
+ * mark the currently processed GENM as preliminary for a remaining transaction
+ */
+ public void markAsPreparingGenm() {
+ markedAsPreparingGenm = true;
+ }
/**
* store already sent extra certs in case of compression
@@ -374,4 +381,8 @@ public void setRespondedCertMustBeEncrypted() {
public boolean isRespondedCertMustBeEncrypted() {
return respondedCertMustBeEncrypted;
}
+
+ boolean isMarkedAsPreparingGenm() {
+ return markedAsPreparingGenm;
+ }
}
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/persistency/TransactionStateTracker.java b/src/main/java/com/siemens/pki/cmpracomponent/persistency/TransactionStateTracker.java
index 1ffa7b0e..eb336f01 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/persistency/TransactionStateTracker.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/persistency/TransactionStateTracker.java
@@ -148,19 +148,6 @@ private boolean isCertResponse(final PKIMessage msg) {
}
}
- private boolean isCertResponseWithWaitingIndication(final PKIMessage msg) {
- try {
- return ((CertRepMessage) msg.getBody().getContent())
- .getResponse()[0]
- .getStatus()
- .getStatus()
- .intValue()
- == PKIStatus.WAITING;
- } catch (final Exception ex) {
- return false;
- }
- }
-
private boolean isConfirmConfirm(final PKIMessage msg) {
return msg.getBody().getType() == PKIBody.TYPE_CONFIRM;
}
@@ -378,7 +365,7 @@ public void trackMessage(final PKIMessage message) throws BaseCmpException, IOEx
PKIFailureInfo.badMessageCheck,
"request was not answered by cert response for " + MessageDumper.msgAsShortString(message));
}
- if (isCertResponseWithWaitingIndication(message)) {
+ if (isWaitingIndication(message)) {
persistencyContext.setLastTransactionState(LastTransactionState.CERTIFICATE_POLLING);
return;
}
@@ -447,7 +434,8 @@ public void trackMessage(final PKIMessage message) throws BaseCmpException, IOEx
throw new CmpValidationException(
INTERFACE_NAME,
PKIFailureInfo.transactionIdInUse,
- "transaction in wrong state for " + MessageDumper.msgAsShortString(message));
+ "transaction in wrong state " + persistencyContext.getLastTransactionState() + " for "
+ + MessageDumper.msgAsShortString(message));
}
persistencyContext.setLastTransactionState(LastTransactionState.REVOCATION_CONFIRMED);
return;
@@ -459,7 +447,8 @@ public void trackMessage(final PKIMessage message) throws BaseCmpException, IOEx
throw new CmpValidationException(
INTERFACE_NAME,
PKIFailureInfo.transactionIdInUse,
- "transaction in wrong state for " + MessageDumper.msgAsShortString(message));
+ "transaction in wrong state " + persistencyContext.getLastTransactionState() + " for "
+ + MessageDumper.msgAsShortString(message));
}
persistencyContext.setLastTransactionState(LastTransactionState.REVOCATION_CONFIRMED);
return;
@@ -472,9 +461,14 @@ public void trackMessage(final PKIMessage message) throws BaseCmpException, IOEx
throw new CmpValidationException(
INTERFACE_NAME,
PKIFailureInfo.transactionIdInUse,
- "transaction in wrong state for " + MessageDumper.msgAsShortString(message));
+ "transaction in wrong state " + persistencyContext.getLastTransactionState() + " for "
+ + MessageDumper.msgAsShortString(message));
+ }
+ if (persistencyContext.isMarkedAsPreparingGenm()) {
+ persistencyContext.setLastTransactionState(LastTransactionState.INITIAL_STATE);
+ } else {
+ persistencyContext.setLastTransactionState(LastTransactionState.GENREP_RETURNED);
}
- persistencyContext.setLastTransactionState(LastTransactionState.GENREP_RETURNED);
return;
case GEN_POLLING:
if (isPollRequest(message) || isPollResponse(message)) {
@@ -484,7 +478,8 @@ public void trackMessage(final PKIMessage message) throws BaseCmpException, IOEx
throw new CmpValidationException(
INTERFACE_NAME,
PKIFailureInfo.transactionIdInUse,
- "transaction in wrong state for " + MessageDumper.msgAsShortString(message));
+ "transaction in wrong state " + persistencyContext.getLastTransactionState() + " for "
+ + MessageDumper.msgAsShortString(message));
}
persistencyContext.setLastTransactionState(LastTransactionState.GENREP_RETURNED);
return;
@@ -492,7 +487,7 @@ public void trackMessage(final PKIMessage message) throws BaseCmpException, IOEx
throw new CmpValidationException(
INTERFACE_NAME,
PKIFailureInfo.transactionIdInUse,
- "transaction in wrong state (" + persistencyContext.getLastTransactionState() + ") for "
+ "transaction in wrong state " + persistencyContext.getLastTransactionState() + " for "
+ MessageDumper.msgAsShortString(message));
}
}
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/util/MessageDumper.java b/src/main/java/com/siemens/pki/cmpracomponent/util/MessageDumper.java
index 60072958..427e33e0 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/util/MessageDumper.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/util/MessageDumper.java
@@ -19,6 +19,7 @@
import static com.siemens.pki.cmpracomponent.util.NullUtil.ifNotNull;
+import com.siemens.pki.verifieradapter.asn1.AttestationObjectIdentifiers;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -464,7 +465,8 @@ private static synchronized void initNameOidMaps() {
PQCObjectIdentifiers.class,
org.bouncycastle.asn1.x509.Extension.class,
EdECObjectIdentifiers.class,
- CMPObjectIdentifiers.class)) {
+ CMPObjectIdentifiers.class,
+ AttestationObjectIdentifiers.class)) {
for (final Field aktField : aktClass.getFields()) {
if (aktField.getType().equals(ASN1ObjectIdentifier.class)
&& (aktField.getModifiers() & Modifier.STATIC) != 0) {
diff --git a/src/main/java/com/siemens/pki/verifieradapter/asn1/AttestationObjectIdentifiers.java b/src/main/java/com/siemens/pki/verifieradapter/asn1/AttestationObjectIdentifiers.java
new file mode 100644
index 00000000..6b1cd428
--- /dev/null
+++ b/src/main/java/com/siemens/pki/verifieradapter/asn1/AttestationObjectIdentifiers.java
@@ -0,0 +1,59 @@
+/*
+ * 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.verifieradapter.asn1;
+
+/**
+ * OID definition from
+ * https://datatracker.ietf.org/doc/draft-ietf-lamps-attestation-freshness/ and
+ * https://datatracker.ietf.org/doc/draft-ietf-lamps-csr-attestation/
+ */
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
+
+/**
+ * OIDs from from https://datatracker.ietf.org/doc/draft-ietf-lamps-attestation-freshness/ and
+ * https://datatracker.ietf.org/doc/draft-ietf-lamps-csr-attestation/
+ */
+public interface AttestationObjectIdentifiers {
+
+ /**
+ * Branch for attestation statement types
+ */
+ /** RFC 4120: id-it: PKIX.4 = 1.3.6.1.5.5.7.4 */
+ ASN1ObjectIdentifier id_it = X509ObjectIdentifiers.id_pkix.branch("4");
+
+ /**
+ * from https://datatracker.ietf.org/doc/draft-ietf-lamps-attestation-freshness/
+ * TODO update to current state of the draft
+ */
+ String TBD1 = "99";
+
+ String TBD2 = "100";
+ String TBD3 = "101";
+
+ ASN1ObjectIdentifier id_it_NonceRequest = id_it.branch(TBD1);
+ ASN1ObjectIdentifier id_it_NonceResponse = id_it.branch(TBD2);
+
+ /**
+ * from https://datatracker.ietf.org/doc/draft-ietf-lamps-csr-attestation/
+ */
+ ASN1ObjectIdentifier id_aa_evidence = PKCSObjectIdentifiers.id_aa.branch("59");
+
+ ASN1ObjectIdentifier id_aa_ar = PKCSObjectIdentifiers.id_aa.branch(TBD3);
+}
diff --git a/src/main/java/com/siemens/pki/verifieradapter/asn1/AttestationResult.java b/src/main/java/com/siemens/pki/verifieradapter/asn1/AttestationResult.java
new file mode 100644
index 00000000..d6530357
--- /dev/null
+++ b/src/main/java/com/siemens/pki/verifieradapter/asn1/AttestationResult.java
@@ -0,0 +1,79 @@
+package com.siemens.pki.verifieradapter.asn1;
+
+/*
+ * 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
+ */
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * {@code
+ * AttestationResult ::= SEQUENCE {
+ * type ATTESTATION-RESULT.&id({AttestationResultSet}),
+ * stmt ATTESTATION-RESULT.&Type({AttestationResultSet}{@type}),
+ * }
+ * }
+ */
+public class AttestationResult extends ASN1Object {
+ public static AttestationResult getInstance(Object o) {
+ if (o instanceof AttestationResult) {
+ return (AttestationResult) o;
+ }
+
+ if (o != null) {
+ return new AttestationResult(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ private final ASN1ObjectIdentifier type;
+
+ private final ASN1Encodable stmt;
+
+ public AttestationResult(ASN1ObjectIdentifier type, ASN1Encodable stmt) {
+ this.type = type;
+ this.stmt = stmt;
+ }
+
+ private AttestationResult(ASN1Sequence seq) {
+ type = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ stmt = seq.getObjectAt(1);
+ }
+
+ public ASN1Encodable getStmt() {
+ return stmt;
+ }
+
+ public ASN1ObjectIdentifier getType() {
+ return type;
+ }
+
+ @Override
+ public ASN1Primitive toASN1Primitive() {
+ final ASN1EncodableVector v = new ASN1EncodableVector(2);
+ v.add(type);
+ v.add(stmt);
+ return new DERSequence(v);
+ }
+}
diff --git a/src/main/java/com/siemens/pki/verifieradapter/asn1/AttestationResultBundle.java b/src/main/java/com/siemens/pki/verifieradapter/asn1/AttestationResultBundle.java
new file mode 100644
index 00000000..c3ba7746
--- /dev/null
+++ b/src/main/java/com/siemens/pki/verifieradapter/asn1/AttestationResultBundle.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2025 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.verifieradapter.asn1;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.Certificate;
+
+/**
+ * {@code
+ * AttestationResultBundle ::= SEQUENCE {
+ * results SEQUENCE SIZE (1..MAX) OF AttestationResult,
+ * certs SEQUENCE SIZE (1..MAX) OF CertificateChoices OPTIONAL,
+ * -- CertificateChoices MUST only contain certificate or other,
+ * -- see Section 10.2.2 of [RFC5652]
+ * }
+ * }
+ *
+ */
+public class AttestationResultBundle extends ASN1Object {
+
+ private final ASN1EncodableVector results;
+ private final ASN1EncodableVector certs;
+
+ private AttestationResultBundle(ASN1Sequence sequence) {
+ results = new ASN1EncodableVector();
+ results.addAll(ASN1Sequence.getInstance(sequence.getObjectAt(0)).toArray());
+ if (sequence.size() > 1) {
+ certs = new ASN1EncodableVector();
+ certs.addAll(ASN1Sequence.getInstance(sequence.getObjectAt(1)).toArray());
+ } else {
+ certs = null;
+ }
+ }
+
+ public AttestationResultBundle(AttestationResult[] results, Certificate[] certs) {
+ this.results = new ASN1EncodableVector();
+ this.results.addAll(results);
+ if (certs != null) {
+ this.certs = new ASN1EncodableVector();
+ this.certs.addAll(certs);
+ } else {
+ this.certs = null;
+ }
+ }
+
+ @Override
+ public ASN1Primitive toASN1Primitive() {
+ ASN1EncodableVector ret = new ASN1EncodableVector();
+ ret.add(new DERSequence(results));
+ if (certs != null) {
+ ret.add(new DERSequence(certs));
+ }
+ return new DERSequence(ret);
+ }
+
+ public static AttestationResultBundle getInstance(Object o) {
+ if (o instanceof AttestationResultBundle) {
+ return (AttestationResultBundle) o;
+ } else if (o != null) {
+ return new AttestationResultBundle(ASN1Sequence.getInstance(o));
+ }
+ return null;
+ }
+
+ public Certificate[] getCerts() {
+ if (certs == null) {
+ return null;
+ }
+ Certificate[] ret = new Certificate[certs.size()];
+ for (int i = 0; i < certs.size(); i++) {
+ ret[i] = Certificate.getInstance(certs.get(i));
+ }
+ return ret;
+ }
+
+ public AttestationResult[] getResults() {
+ AttestationResult[] ret = new AttestationResult[results.size()];
+ for (int i = 0; i < ret.length; i++) {
+ ret[i] = AttestationResult.getInstance(results.get(i));
+ }
+ return ret;
+ }
+}
diff --git a/src/main/java/com/siemens/pki/verifieradapter/asn1/EvidenceBundle.java b/src/main/java/com/siemens/pki/verifieradapter/asn1/EvidenceBundle.java
new file mode 100644
index 00000000..adb8f29c
--- /dev/null
+++ b/src/main/java/com/siemens/pki/verifieradapter/asn1/EvidenceBundle.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2025 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.verifieradapter.asn1;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.Certificate;
+
+/**
+ *
+ * {@code
+ * EvidenceBundle ::= SEQUENCE {
+ * evidences SEQUENCE SIZE (1..MAX) OF EvidenceStatement,
+ * certs SEQUENCE SIZE (1..MAX) OF LimitedCertChoices OPTIONAL
+ * }
+ * }
+ */
+public class EvidenceBundle extends ASN1Object {
+
+ private final ASN1EncodableVector evidences;
+ private final ASN1EncodableVector certs;
+
+ private EvidenceBundle(ASN1Sequence sequence) {
+ evidences = new ASN1EncodableVector();
+ evidences.addAll(ASN1Sequence.getInstance(sequence.getObjectAt(0)).toArray());
+ if (sequence.size() > 1) {
+ certs = new ASN1EncodableVector();
+ certs.addAll(ASN1Sequence.getInstance(sequence.getObjectAt(1)).toArray());
+ } else {
+ certs = null;
+ }
+ }
+
+ public EvidenceBundle(EvidenceStatement[] evidences, Certificate[] certs) {
+ this.evidences = new ASN1EncodableVector();
+ this.evidences.addAll(evidences);
+ if (certs != null) {
+ this.certs = new ASN1EncodableVector();
+ this.certs.addAll(certs);
+ } else {
+ this.certs = null;
+ }
+ }
+
+ @Override
+ public ASN1Primitive toASN1Primitive() {
+ ASN1EncodableVector ret = new ASN1EncodableVector();
+ ret.add(new DERSequence(evidences));
+ if (certs != null && certs.size() > 0) {
+ ret.add(new DERSequence(certs));
+ }
+ return new DERSequence(ret);
+ }
+
+ public static EvidenceBundle getInstance(Object o) {
+ if (o instanceof EvidenceBundle) {
+ return (EvidenceBundle) o;
+ } else if (o != null) {
+ return new EvidenceBundle(ASN1Sequence.getInstance(o));
+ }
+ return null;
+ }
+
+ public EvidenceStatement[] getEvidences() {
+ EvidenceStatement[] ret = new EvidenceStatement[evidences.size()];
+ for (int i = 0; i < ret.length; i++) {
+ ret[i] = EvidenceStatement.getInstance(evidences.get(i));
+ }
+ return ret;
+ }
+
+ public Certificate[] getCerts() {
+ if (certs == null) {
+ return null;
+ }
+ Certificate[] ret = new Certificate[certs.size()];
+ for (int i = 0; i < certs.size(); i++) {
+ ret[i] = Certificate.getInstance(certs.get(i));
+ }
+ return ret;
+ }
+}
diff --git a/src/main/java/com/siemens/pki/verifieradapter/asn1/EvidenceStatement.java b/src/main/java/com/siemens/pki/verifieradapter/asn1/EvidenceStatement.java
new file mode 100644
index 00000000..414ff9cd
--- /dev/null
+++ b/src/main/java/com/siemens/pki/verifieradapter/asn1/EvidenceStatement.java
@@ -0,0 +1,101 @@
+package com.siemens.pki.verifieradapter.asn1;
+
+/*
+ * 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
+ */
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1IA5String;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * {@code
+ * EvidenceStatement ::= SEQUENCE {
+ * type EVIDENCE-STATEMENT.&id({EvidenceStatementSet}),
+ * stmt EVIDENCE-STATEMENT.&Type({EvidenceStatementSet}{@type}),
+ * hint IA5String OPTIONAL
+ * }
+ * }
+ */
+public class EvidenceStatement extends ASN1Object {
+
+ public static EvidenceStatement getInstance(Object o) {
+ if (o instanceof EvidenceStatement) {
+ return (EvidenceStatement) o;
+ }
+
+ if (o != null) {
+ return new EvidenceStatement(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ private final ASN1ObjectIdentifier type;
+
+ private final ASN1Encodable stmt;
+
+ private final ASN1IA5String hint;
+
+ public EvidenceStatement(ASN1ObjectIdentifier type, ASN1Encodable stmt, ASN1IA5String hint) {
+ if (type == null) {
+ throw new NullPointerException("'type' cannot be null");
+ }
+
+ this.type = type;
+ this.stmt = stmt;
+ this.hint = hint;
+ }
+
+ public ASN1IA5String getHint() {
+ return hint;
+ }
+
+ private EvidenceStatement(ASN1Sequence seq) {
+ type = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ stmt = seq.getObjectAt(1);
+ if (seq.size() > 2) {
+ hint = ASN1IA5String.getInstance(seq.getObjectAt(2));
+ } else {
+ hint = null;
+ }
+ }
+
+ public ASN1Encodable getStmt() {
+ return stmt;
+ }
+
+ public ASN1ObjectIdentifier getType() {
+ return type;
+ }
+
+ @Override
+ public ASN1Primitive toASN1Primitive() {
+ final ASN1EncodableVector v = new ASN1EncodableVector(3);
+ v.add(type);
+ v.add(stmt);
+ if (hint != null) {
+ v.add(hint);
+ }
+ return new DERSequence(v);
+ }
+}
diff --git a/src/main/java/com/siemens/pki/verifieradapter/asn1/NonceRequestValue.java b/src/main/java/com/siemens/pki/verifieradapter/asn1/NonceRequestValue.java
new file mode 100644
index 00000000..8d1a7f4c
--- /dev/null
+++ b/src/main/java/com/siemens/pki/verifieradapter/asn1/NonceRequestValue.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2025 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.verifieradapter.asn1;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1UTF8String;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERUTF8String;
+
+/**
+ * from https://datatracker.ietf.org/doc/draft-ietf-lamps-attestation-freshness/
+ *
+ * {@code
+ * NonceRequestValue ::= SEQUENCE SIZE (1..MAX) OF NonceRequest
+ * NonceRequest ::= SEQUENCE {
+ * len INTEGER OPTIONAL,
+ * -- indicates the required length of the requested nonce
+ * type EVIDENCE-STATEMENT.&id({EvidenceStatementSet}) OPTIONAL,
+ * -- indicates which Evidence type to request a nonce for
+ * hint UTF8String OPTIONAL
+ * -- indicates which Verifier to request a nonce from
+ * vendorextension OCTET STRING OPTIONAL
+ * -- Siemens proprietary extension to carry additional data
+ * }
+ * }
+ */
+public class NonceRequestValue extends ASN1Object {
+
+ ASN1EncodableVector nonceRequests = new ASN1EncodableVector();
+
+ public static class NonceRequest extends ASN1Object {
+ public ASN1Integer getLen() {
+ return len;
+ }
+
+ public ASN1ObjectIdentifier getType() {
+ return type;
+ }
+
+ public ASN1UTF8String getHint() {
+ return hint;
+ }
+
+ public ASN1OctetString getVendorextension() {
+ return vendorextension;
+ }
+
+ private ASN1Integer len = null;
+ private ASN1ObjectIdentifier type = null;
+ private ASN1UTF8String hint = null;
+ private ASN1OctetString vendorextension = null;
+
+ public NonceRequest(
+ ASN1Integer len, ASN1ObjectIdentifier type, ASN1UTF8String hint, ASN1OctetString vendorextension) {
+ this.len = len;
+ this.type = type;
+ this.hint = hint;
+ this.vendorextension = vendorextension;
+ }
+
+ public ASN1Primitive toASN1Primitive() {
+
+ ASN1EncodableVector v = new ASN1EncodableVector(4);
+
+ addOptional(v, len);
+ addOptional(v, type);
+ addOptional(v, hint);
+ addOptional(v, vendorextension);
+
+ return new DERSequence(v);
+ }
+
+ public static NonceRequest getInstance(Object o) {
+ if (o instanceof NonceRequest) {
+ return (NonceRequest) o;
+ } else if (o != null) {
+ return new NonceRequest(ASN1Sequence.getInstance(o));
+ }
+
+ return null;
+ }
+
+ private NonceRequest(ASN1Sequence seq) {
+ Enumeration> en = seq.getObjects();
+ if (!en.hasMoreElements()) {
+ return;
+ }
+ Object next = en.nextElement();
+ if (next instanceof ASN1Integer) {
+ len = ASN1Integer.getInstance(next);
+ if (!en.hasMoreElements()) {
+ return;
+ }
+ next = en.nextElement();
+ }
+ if (next instanceof ASN1ObjectIdentifier) {
+ type = ASN1ObjectIdentifier.getInstance(next);
+ if (!en.hasMoreElements()) {
+ return;
+ }
+ next = en.nextElement();
+ }
+ if (next instanceof ASN1UTF8String) {
+ hint = ASN1UTF8String.getInstance(next);
+ if (!en.hasMoreElements()) {
+ return;
+ }
+ next = en.nextElement();
+ }
+ if (next instanceof ASN1OctetString) {
+ vendorextension = ASN1OctetString.getInstance(next);
+ }
+ }
+
+ public NonceRequest(
+ BigInteger nonceRequestLen,
+ String nonceRequestType,
+ String nonceRequestHint,
+ byte[] nonceRequestVendorextension) {
+ this(
+ nonceRequestLen != null ? new ASN1Integer(nonceRequestLen) : null,
+ nonceRequestType != null ? new ASN1ObjectIdentifier(nonceRequestType) : null,
+ nonceRequestHint != null ? new DERUTF8String(nonceRequestHint) : null,
+ nonceRequestVendorextension != null ? new DEROctetString(nonceRequestVendorextension) : null);
+ }
+
+ private void addOptional(ASN1EncodableVector v, ASN1Encodable obj) {
+ if (obj != null) {
+ v.add(obj);
+ }
+ }
+ }
+
+ public NonceRequestValue(NonceRequest[] requests) {
+ nonceRequests.addAll(requests);
+ }
+
+ public NonceRequest[] getNonceRequests() {
+ NonceRequest[] ret = new NonceRequest[nonceRequests.size()];
+ for (int i = 0; i < ret.length; i++) {
+ ret[i] = NonceRequest.getInstance(nonceRequests.get(i));
+ }
+ return ret;
+ }
+
+ public NonceRequestValue(ASN1Sequence instance) {
+ for (int i = 0; i < instance.size(); i++) {
+ nonceRequests.add(instance.getObjectAt(i));
+ }
+ }
+
+ @Override
+ public ASN1Primitive toASN1Primitive() {
+ return new DERSequence(nonceRequests);
+ }
+
+ public static NonceRequestValue getInstance(Object o) {
+ if (o instanceof NonceRequestValue) {
+ return (NonceRequestValue) o;
+ } else if (o != null) {
+ return new NonceRequestValue(ASN1Sequence.getInstance(o));
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/siemens/pki/verifieradapter/asn1/NonceResponseValue.java b/src/main/java/com/siemens/pki/verifieradapter/asn1/NonceResponseValue.java
new file mode 100644
index 00000000..95adf2ca
--- /dev/null
+++ b/src/main/java/com/siemens/pki/verifieradapter/asn1/NonceResponseValue.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2025 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.verifieradapter.asn1;
+
+import java.util.Enumeration;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1UTF8String;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERUTF8String;
+
+/**
+ * from https://datatracker.ietf.org/doc/draft-ietf-lamps-attestation-freshness/
+ *
+ * {@code
+ * NonceResponseValue ::= SEQUENCE SIZE (1..MAX) OF NonceResponse
+ * NonceResponse ::= SEQUENCE {
+ * nonce OCTET STRING,
+ * -- contains the nonce of length len
+ * -- provided by the Verifier indicated with hint
+ * expiry INTEGER OPTIONAL,
+ * -- indicates how long in seconds the Verifier considers
+ * -- the nonce valid
+ * type EVIDENCE-STATEMENT.&id({EvidenceStatementSet}) OPTIONAL,
+ * -- indicates which Evidence type to request a nonce for
+ * hint UTF8String OPTIONAL
+ * -- indicates which Verifier to request a nonce from
+ * vendorextension OCTET STRING OPTIONAL
+ * -- Siemens proprietary extension to carry additional data
+ * }
+ * }
+ */
+public class NonceResponseValue extends ASN1Object {
+
+ ASN1EncodableVector nonceResponses = new ASN1EncodableVector();
+
+ public static class NonceResponse extends ASN1Object {
+
+ private final ASN1OctetString nonce;
+ private ASN1Integer expiry = null;
+ private ASN1ObjectIdentifier type = null;
+ private ASN1UTF8String hint = null;
+ private ASN1OctetString vendorextension = null;
+
+ public ASN1Primitive toASN1Primitive() {
+
+ ASN1EncodableVector v = new ASN1EncodableVector(4);
+
+ v.add(nonce);
+ addOptional(v, expiry);
+ addOptional(v, type);
+ addOptional(v, hint);
+ addOptional(v, vendorextension);
+
+ return new DERSequence(v);
+ }
+
+ public static NonceResponse getInstance(Object o) {
+ if (o instanceof NonceResponse) {
+ return (NonceResponse) o;
+ } else if (o != null) {
+ return new NonceResponse(ASN1Sequence.getInstance(o));
+ }
+ return null;
+ }
+
+ private NonceResponse(ASN1Sequence seq) {
+ Enumeration> en = seq.getObjects();
+ if (!en.hasMoreElements()) {
+ throw new IllegalArgumentException("NonceResponse missing nonce value");
+ }
+ Object next = en.nextElement();
+ nonce = ASN1OctetString.getInstance(next);
+ if (!en.hasMoreElements()) {
+ return;
+ }
+ next = en.nextElement();
+ if (next instanceof ASN1Integer) {
+ expiry = ASN1Integer.getInstance(next);
+ if (!en.hasMoreElements()) {
+ return;
+ }
+ next = en.nextElement();
+ }
+
+ if (next instanceof ASN1ObjectIdentifier) {
+ type = ASN1ObjectIdentifier.getInstance(next);
+ if (!en.hasMoreElements()) {
+ return;
+ }
+ next = en.nextElement();
+ }
+ if (next instanceof ASN1UTF8String) {
+ hint = ASN1UTF8String.getInstance(next);
+ if (!en.hasMoreElements()) {
+ return;
+ }
+ next = en.nextElement();
+ }
+ if (next instanceof ASN1OctetString) {
+ vendorextension = ASN1OctetString.getInstance(next);
+ }
+ }
+
+ public NonceResponse(
+ ASN1OctetString nonce,
+ ASN1Integer expiry,
+ ASN1ObjectIdentifier type,
+ ASN1UTF8String hint,
+ ASN1OctetString vendorextension) {
+ this.nonce = nonce;
+ this.expiry = expiry;
+ this.type = type;
+ this.hint = hint;
+ this.vendorextension = vendorextension;
+ }
+
+ public NonceResponse(byte[] nonce, Integer expiry, String type, String hint, byte[] vendorextension) {
+ this(
+ nonce != null ? new DEROctetString(nonce) : null,
+ expiry != null ? new ASN1Integer(expiry) : null,
+ type != null ? new ASN1ObjectIdentifier(type) : null,
+ hint != null ? new DERUTF8String(hint) : null,
+ vendorextension != null ? new DEROctetString(vendorextension) : null);
+ }
+
+ public ASN1OctetString getNonce() {
+ return nonce;
+ }
+
+ public ASN1Integer getExpiry() {
+ return expiry;
+ }
+
+ public ASN1ObjectIdentifier getType() {
+ return type;
+ }
+
+ public ASN1UTF8String getHint() {
+ return hint;
+ }
+
+ public ASN1OctetString getVendorextension() {
+ return vendorextension;
+ }
+
+ private void addOptional(ASN1EncodableVector v, ASN1Encodable obj) {
+ if (obj != null) {
+ v.add(obj);
+ }
+ }
+ }
+
+ public NonceResponseValue(ASN1Sequence instance) {
+ for (int i = 0; i < instance.size(); i++) {
+ nonceResponses.add(instance.getObjectAt(i));
+ }
+ }
+
+ public NonceResponseValue(NonceResponse[] responses) {
+ nonceResponses.addAll(responses);
+ }
+
+ public NonceResponse[] getNonceResponse() {
+ NonceResponse[] ret = new NonceResponse[nonceResponses.size()];
+ for (int i = 0; i < ret.length; i++) {
+ ret[i] = NonceResponse.getInstance(nonceResponses.get(i));
+ }
+ return ret;
+ }
+
+ @Override
+ public ASN1Primitive toASN1Primitive() {
+ return new DERSequence(nonceResponses);
+ }
+
+ public static NonceResponseValue getInstance(Object o) {
+ if (o instanceof NonceResponseValue) {
+ return (NonceResponseValue) o;
+ } else if (o != null) {
+ return new NonceResponseValue(ASN1Sequence.getInstance(o));
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/siemens/pki/verifieradapter/asn1/package-info.java b/src/main/java/com/siemens/pki/verifieradapter/asn1/package-info.java
new file mode 100644
index 00000000..4aaca996
--- /dev/null
+++ b/src/main/java/com/siemens/pki/verifieradapter/asn1/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2025 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
+ */
+/**
+ * ASN.1 types used in remote attestation (RAT)
+ */
+package com.siemens.pki.verifieradapter.asn1;
diff --git a/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCrWithRAT.java b/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCrWithRAT.java
new file mode 100644
index 00000000..d49f027c
--- /dev/null
+++ b/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestCrWithRAT.java
@@ -0,0 +1,583 @@
+/*
+ * 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.assertNotNull;
+import static org.junit.Assert.fail;
+
+import com.siemens.pki.cmpclientcomponent.configuration.ClientAttestationContext;
+import com.siemens.pki.cmpclientcomponent.configuration.ClientContext;
+import com.siemens.pki.cmpclientcomponent.configuration.EnrollmentContext;
+import com.siemens.pki.cmpclientcomponent.configuration.RevocationContext;
+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.GetFreshRatNonceHandler;
+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.RatVerifierAdapter;
+import com.siemens.pki.cmpracomponent.configuration.SupportMessageHandlerInterface;
+import com.siemens.pki.cmpracomponent.configuration.VerificationContext;
+import com.siemens.pki.cmpracomponent.cryptoservices.CertUtility;
+import com.siemens.pki.cmpracomponent.persistency.DefaultPersistencyImplementation;
+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 com.siemens.pki.verifieradapter.asn1.AttestationObjectIdentifiers;
+import com.siemens.pki.verifieradapter.asn1.AttestationResult;
+import com.siemens.pki.verifieradapter.asn1.AttestationResultBundle;
+import com.siemens.pki.verifieradapter.asn1.EvidenceStatement;
+import com.siemens.pki.verifieradapter.asn1.NonceResponseValue.NonceResponse;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.List;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DERUTF8String;
+import org.bouncycastle.asn1.cmp.PKIBody;
+import org.bouncycastle.asn1.x509.Certificate;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TestCrWithRAT extends EnrollmentTestcaseBase {
+
+ private static final String UPSTREAM_TRUST_PATH = "credentials/CMP_CA_and_LRA_DOWNSTREAM_Root.pem";
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TestCrWithRAT.class);
+
+ 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 buildSimpleRaConfiguration(
+ downstreamCredentials,
+ ReprotectMode.keep,
+ downstreamTrust,
+ upstreamCredentials,
+ upstreamTrust,
+ enrollmentTrust);
+ }
+
+ private static Configuration buildSimpleRaConfiguration(
+ 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 RatVerifierAdapter getVerifierAdapter(String certProfile, int bodyType) {
+ return new RatVerifierAdapter() {
+
+ @Override
+ public byte[] processRatVerification(byte[] transactionId, byte[] evidence) {
+ LOGGER.debug(
+ "processRatVerification called with certprofile: {}, type: {}",
+ certProfile,
+ MessageDumper.msgTypeAsString(bodyType));
+
+ EvidenceStatement evidenceStatement = EvidenceStatement.getInstance(evidence);
+ assertNotNull(evidenceStatement.getHint());
+ assertNotNull(evidenceStatement.getStmt());
+
+ try {
+ return new AttestationResult(
+ evidenceStatement.getType().branch("99"),
+ new DERUTF8String("attestation result stm"))
+ .getEncoded();
+ } catch (IOException e) {
+ fail(e.getMessage());
+ return null;
+ }
+ }
+
+ @Override
+ public NonceResponseRet generateNonce(
+ byte[] transactionId,
+ BigInteger len,
+ String type,
+ String hint,
+ byte[] vendorextension,
+ byte[] encodedNonceRequest) {
+ LOGGER.debug(
+ "generateNonce called with certprofile: {}, type: {}",
+ certProfile,
+ MessageDumper.msgTypeAsString(bodyType));
+ return new NonceResponseRet() {
+
+ @Override
+ public byte[] getNonce() {
+ return CertUtility.generateRandomBytes(len.intValue());
+ }
+
+ @Override
+ public Integer getExpiry() {
+ return 999;
+ }
+
+ @Override
+ public String getHint() {
+ return "responded hint: " + hint;
+ }
+
+ @Override
+ public byte[] getVendorextension() {
+ return vendorextension;
+ }
+
+ @Override
+ public String getType() {
+ return new ASN1ObjectIdentifier("1.7.8.9").getId();
+ }
+ };
+ }
+ };
+ }
+
+ @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);
+ if (AttestationObjectIdentifiers.id_it_NonceRequest.getId().equalsIgnoreCase(infoTypeOid)) {
+ return new GetFreshRatNonceHandler() {};
+ }
+ 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 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;
+ }
+ };
+ }
+
+ ClientContext getRatClientContext(final int enrollmentType, KeyPair keyPair, boolean requestImplicitConfirm) {
+ return new ClientContext() {
+
+ @Override
+ public ClientAttestationContext getAttestationContext() {
+ return new ClientAttestationContext() {
+
+ @Override
+ public String getNonceRequestHint() {
+ return "nonce hint";
+ }
+
+ @Override
+ public String getNonceRequestType() {
+ return "1.9.8.7.6";
+ }
+
+ @Override
+ public BigInteger getNonceRequestLen() {
+ return new BigInteger("16");
+ }
+
+ @Override
+ public byte[] getNonceRequestVendorextension() {
+ return "vendor extension".getBytes();
+ }
+
+ @Override
+ public Certificate[] getEvidenceBundleCerts() {
+ // just provide some certificates
+ final SignatureValidationCredentials enrollmentTrust =
+ new SignatureValidationCredentials("credentials/ENROLL_Root.pem", null);
+ final Collection trustedCertificates =
+ enrollmentTrust.getTrustedCertificates();
+ Certificate[] ret = new Certificate[trustedCertificates.size()];
+ int i = 0;
+ for (X509Certificate akt : trustedCertificates) {
+ try {
+ ret[i++] = Certificate.getInstance(akt.getEncoded());
+ } catch (CertificateEncodingException e) {
+ fail(e.getLocalizedMessage());
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public byte[] getEvidenceStatement(byte[] attestationNonce) {
+
+ NonceResponse nonceResponse = NonceResponse.getInstance(attestationNonce);
+ assertNotNull(nonceResponse.getExpiry());
+ assertNotNull(nonceResponse.getHint());
+ assertNotNull(nonceResponse.getVendorextension());
+
+ try {
+ return new EvidenceStatement(
+ nonceResponse.getType().branch("88"),
+ new DERUTF8String("evidence statement"),
+ new DERIA5String("evidence hint"))
+ .getEncoded();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+ }
+
+ @Override
+ public EnrollmentContext getEnrollmentContext() {
+ return new EnrollmentContext() {
+
+ @Override
+ public KeyPair getCertificateKeypair() {
+ return keyPair;
+ }
+
+ @Override
+ public VerificationContext getEnrollmentTrust() {
+ return enrollmentCredentials;
+ }
+
+ @Override
+ public int getEnrollmentType() {
+ return enrollmentType;
+ }
+
+ @Override
+ public List getExtensions() {
+ return null;
+ }
+
+ @Override
+ public X509Certificate getOldCert() {
+ return null;
+ }
+
+ @Override
+ public boolean getRequestImplictConfirm() {
+ return requestImplicitConfirm;
+ }
+
+ @Override
+ public String getSubject() {
+ return "CN=Subject";
+ }
+ };
+ }
+
+ @Override
+ public RevocationContext getRevocationContext() {
+ fail("getRevocationContext");
+ return null;
+ }
+ };
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ launchCmpCaAndRa(buildSignatureBasedDownstreamConfiguration());
+ }
+
+ @Test
+ public void testCr() throws Exception {
+ final EnrollmentResult ret = getSignatureBasedCmpClient(
+ "theCertProfileForOnlineEnrollment",
+ getRatClientContext(
+ PKIBody.TYPE_CERT_REQ,
+ ConfigurationFactory.getKeyGenerator().generateKeyPair(),
+ false),
+ UPSTREAM_TRUST_PATH)
+ .invokeEnrollment();
+ final ASN1OctetString extValue = ASN1OctetString.getInstance(
+ ret.getEnrolledCertificate().getExtensionValue(AttestationObjectIdentifiers.id_aa_ar.getId()));
+ final AttestationResultBundle attestationResultBundle =
+ AttestationResultBundle.getInstance(extValue.getOctets());
+ assertNotNull(attestationResultBundle);
+ assertNotNull(attestationResultBundle.getCerts());
+ for (AttestationResult result : attestationResultBundle.getResults()) {
+ assertNotNull(result.getType());
+ assertNotNull(result.getStmt());
+ }
+ }
+
+ @Test
+ public void testCrWithImplicitConfirm() throws Exception {
+ final EnrollmentResult ret = getSignatureBasedCmpClient(
+ "theCertProfileForOnlineEnrollment",
+ getRatClientContext(
+ PKIBody.TYPE_CERT_REQ,
+ ConfigurationFactory.getKeyGenerator().generateKeyPair(),
+ true),
+ UPSTREAM_TRUST_PATH)
+ .invokeEnrollment();
+ final ASN1OctetString extValue = ASN1OctetString.getInstance(
+ ret.getEnrolledCertificate().getExtensionValue(AttestationObjectIdentifiers.id_aa_ar.getId()));
+ final AttestationResultBundle attestationResultBundle =
+ AttestationResultBundle.getInstance(extValue.getOctets());
+ assertNotNull(attestationResultBundle);
+ assertNotNull(attestationResultBundle.getCerts());
+ for (AttestationResult result : attestationResultBundle.getResults()) {
+ assertNotNull(result.getType());
+ assertNotNull(result.getStmt());
+ }
+ }
+}
diff --git a/src/test/java/com/siemens/pki/verifieradapter/asn1/TestRatAsn1.java b/src/test/java/com/siemens/pki/verifieradapter/asn1/TestRatAsn1.java
new file mode 100644
index 00000000..c587b226
--- /dev/null
+++ b/src/test/java/com/siemens/pki/verifieradapter/asn1/TestRatAsn1.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2025 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.verifieradapter.asn1;
+
+import static org.junit.Assert.*;
+
+import com.siemens.pki.verifieradapter.asn1.NonceRequestValue.NonceRequest;
+import com.siemens.pki.verifieradapter.asn1.NonceResponseValue.NonceResponse;
+import java.io.IOException;
+import java.math.BigInteger;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERUTF8String;
+import org.junit.Test;
+
+/**
+ * test some unusual parameter combinations to improve test coverage
+ */
+public class TestRatAsn1 {
+
+ @Test
+ public void testAttestationResultBundle() throws IOException {
+ byte[] encoded = new AttestationResultBundle(
+ new AttestationResult[] {
+ new AttestationResult(new ASN1ObjectIdentifier("1.2.3"), new DERUTF8String("hallo"))
+ },
+ null)
+ .getEncoded();
+ AttestationResultBundle decoded = AttestationResultBundle.getInstance(encoded);
+ assertEquals(1, decoded.getResults().length);
+ final AttestationResult attestationResult = decoded.getResults()[0];
+ assertEquals(new ASN1ObjectIdentifier("1.2.3"), attestationResult.getType());
+ assertEquals(new DERUTF8String("hallo"), attestationResult.getStmt());
+ assertNull(decoded.getCerts());
+ }
+
+ @Test
+ public void testEvidenceBundle() throws IOException {
+ byte[] encoded = new EvidenceBundle(
+ new EvidenceStatement[] {
+ new EvidenceStatement(new ASN1ObjectIdentifier("1.2.3"), new DERUTF8String("hallo"), null)
+ },
+ null)
+ .getEncoded();
+ EvidenceBundle decoded = EvidenceBundle.getInstance(encoded);
+ assertEquals(1, decoded.getEvidences().length);
+ final EvidenceStatement evidenceStatement = decoded.getEvidences()[0];
+ assertEquals(new ASN1ObjectIdentifier("1.2.3"), evidenceStatement.getType());
+ assertEquals(new DERUTF8String("hallo"), evidenceStatement.getStmt());
+ assertNull(evidenceStatement.getHint());
+ assertNull(decoded.getCerts());
+ }
+
+ @Test
+ public void testNonceRequestValue() throws IOException {
+ byte[] encoded = new NonceRequestValue(
+ new NonceRequest[] {new NonceRequest((BigInteger) null, null, null, null)})
+ .getEncoded();
+ NonceRequestValue decoded = NonceRequestValue.getInstance(encoded);
+ assertEquals(1, decoded.getNonceRequests().length);
+ final NonceRequest nonceRequest = decoded.getNonceRequests()[0];
+ assertNull(nonceRequest.getLen());
+ assertNull(nonceRequest.getType());
+ assertNull(nonceRequest.getHint());
+ assertNull(nonceRequest.getVendorextension());
+ }
+
+ @Test
+ public void testNonceResponseValue() throws IOException {
+ byte[] encoded = new NonceResponseValue(
+ new NonceResponse[] {new NonceResponse(new byte[10], null, null, null, null)})
+ .getEncoded();
+ NonceResponseValue decoded = NonceResponseValue.getInstance(encoded);
+ assertEquals(1, decoded.getNonceResponse().length);
+ final NonceResponse nonceRequest = decoded.getNonceResponse()[0];
+ assertNotNull(nonceRequest.getNonce());
+ assertNull(nonceRequest.getExpiry());
+ assertNull(nonceRequest.getType());
+ assertNull(nonceRequest.getHint());
+ assertNull(nonceRequest.getVendorextension());
+ }
+}