diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7820dfe5..4a93ee78 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -128,7 +128,7 @@ feat: implement configurable recipient
fix: extension processing in CMP client
-### 4.1.0 (Dec 14 2023)
+### 4.1.0 (Dec 14 2024)
feat: revocation checking via inventory interface
diff --git a/pom.xml b/pom.xml
index 2480f038..1bd771da 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,7 +8,7 @@
com.siemens.pki
CmpRaComponent
jar
- 4.2.0
+ 4.3.0
UTF-8
.
@@ -93,7 +93,7 @@
org.apache.maven.plugins
maven-javadoc-plugin
- 3.10.0
+ 3.10.1
javadoc-jar
@@ -118,7 +118,7 @@
src/test/java/**/*.java
- 2.38.0
+ 2.39.0
@@ -177,7 +177,7 @@
org.apache.maven.plugins
maven-gpg-plugin
- 3.2.5
+ 3.2.7
sign-artifacts
@@ -204,7 +204,7 @@
org.cyclonedx
cyclonedx-maven-plugin
- 2.8.1
+ 2.8.2
package
@@ -225,12 +225,17 @@
org.bouncycastle
bcprov-jdk18on
- 1.78.1
+ 1.79
org.bouncycastle
bcpkix-jdk18on
- 1.78.1
+ 1.79
+
+
+ org.bouncycastle
+ bcutil-jdk18on
+ 1.79
org.slf4j
@@ -240,12 +245,12 @@
com.fasterxml.jackson.core
jackson-databind
- 2.17.2
+ 2.18.0
com.fasterxml.jackson.dataformat
jackson-dataformat-yaml
- 2.17.2
+ 2.18.0
org.jacoco
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 603f26c2..3bc3ab93 100644
--- a/src/main/java/com/siemens/pki/cmpclientcomponent/main/ClientRequestHandler.java
+++ b/src/main/java/com/siemens/pki/cmpclientcomponent/main/ClientRequestHandler.java
@@ -76,7 +76,7 @@ class ValidatorAndProtector {
private final MessageHeaderValidator headerValidator;
- private final ValidatorIF bodyValidator;
+ private final ValidatorIF bodyValidator;
private final VerificationContext inputVerification;
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 87ea3e6f..e38aad80 100644
--- a/src/main/java/com/siemens/pki/cmpclientcomponent/main/CmpClient.java
+++ b/src/main/java/com/siemens/pki/cmpclientcomponent/main/CmpClient.java
@@ -40,12 +40,10 @@
import com.siemens.pki.cmpracomponent.protection.ProtectionProvider;
import com.siemens.pki.cmpracomponent.protection.SignatureBasedProtection;
import com.siemens.pki.cmpracomponent.util.MessageDumper;
-import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
@@ -63,6 +61,7 @@
import org.bouncycastle.asn1.cmp.CMPObjectIdentifiers;
import org.bouncycastle.asn1.cmp.CRLSource;
import org.bouncycastle.asn1.cmp.CRLStatus;
+import org.bouncycastle.asn1.cmp.CertOrEncCert;
import org.bouncycastle.asn1.cmp.CertRepMessage;
import org.bouncycastle.asn1.cmp.CertReqTemplateContent;
import org.bouncycastle.asn1.cmp.CertResponse;
@@ -76,6 +75,7 @@
import org.bouncycastle.asn1.cmp.PKIStatus;
import org.bouncycastle.asn1.cmp.RevRepContent;
import org.bouncycastle.asn1.cmp.RootCaKeyUpdateContent;
+import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.cms.EnvelopedData;
import org.bouncycastle.asn1.crmf.AttributeTypeAndValue;
import org.bouncycastle.asn1.crmf.CertId;
@@ -89,6 +89,10 @@
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.Time;
+import org.bouncycastle.cms.CMSEnvelopedData;
+import org.bouncycastle.cms.RecipientInformation;
+import org.bouncycastle.cms.RecipientInformationStore;
+import org.bouncycastle.cms.jcajce.JceKEMEnvelopedRecipient;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -140,6 +144,7 @@ public interface EnrollmentResult {
/**
* ctor
+ *
* @param certProfile certificate profile to be used for enrollment.
* null
if no certificate profile
* should be used.
@@ -299,12 +304,11 @@ public List getCrls(
if (infoValue == null) {
return null;
}
- final CertificateFactory certificateFactory = CertUtility.getCertificateFactory();
final ASN1Sequence crls = ASN1Sequence.getInstance(infoValue);
final List ret = new ArrayList<>(crls.size());
for (final ASN1Encodable aktCrl : crls) {
- ret.add((X509CRL) certificateFactory.generateCRL(new ByteArrayInputStream(
- aktCrl.toASN1Primitive().getEncoded())));
+ ret.add(CertUtility.parseCrl(
+ aktCrl.toASN1Primitive().getEncoded()));
}
return ret;
}
@@ -497,8 +501,23 @@ public EnrollmentResult invokeEnrollment() {
return null;
}
final CertifiedKeyPair certifiedKeyPair = certResponse.getCertifiedKeyPair();
- final CMPCertificate enrolledCertificate =
- certifiedKeyPair.getCertOrEncCert().getCertificate();
+ CertOrEncCert certOrEncCert = certifiedKeyPair.getCertOrEncCert();
+ CMPCertificate enrolledCertificate = null;
+ if (certOrEncCert.hasEncryptedCertificate()) {
+ JceKEMEnvelopedRecipient jkr = new JceKEMEnvelopedRecipient(certificateKeypair.getPrivate());
+ EnvelopedData envelopedData =
+ (EnvelopedData) certOrEncCert.getEncryptedCert().getValue();
+ final CMSEnvelopedData cmsEnvelopedData = new CMSEnvelopedData(
+ new ContentInfo(envelopedData.getEncryptedContentInfo().getContentType(), envelopedData));
+ final RecipientInformationStore recipients = cmsEnvelopedData.getRecipientInfos();
+ for (RecipientInformation recipient : recipients.getRecipients()) {
+ byte[] content = recipient.getContent(jkr);
+ enrolledCertificate = CMPCertificate.getInstance(content);
+ break;
+ }
+ } else {
+ enrolledCertificate = certOrEncCert.getCertificate();
+ }
if (enrollmentType != PKIBody.TYPE_P10_CERT_REQ && enrolledPrivateKey == null) {
// central key generation in place, decrypt private key
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/configuration/SignatureCredentialContext.java b/src/main/java/com/siemens/pki/cmpracomponent/configuration/SignatureCredentialContext.java
index e256873f..d04038a9 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/configuration/SignatureCredentialContext.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/configuration/SignatureCredentialContext.java
@@ -47,6 +47,18 @@ public interface SignatureCredentialContext extends CredentialContext {
*/
PrivateKey getPrivateKey();
+ /**
+ * provide the alternative private key for the end certificate, see X.509 (2019)
+ * section 9.8
+ *
+ * @return private key for first certificate returned by
+ * {@link #getCertificateChain()}
+ */
+ default PrivateKey getAlternativePrivateKey() {
+ return null;
+ }
+ ;
+
/**
* provide name or OID of signature algorithm, see Signature
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/AlgorithmHelper.java b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/AlgorithmHelper.java
index f2d2af23..0e3c3170 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/AlgorithmHelper.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/AlgorithmHelper.java
@@ -17,15 +17,16 @@
*/
package com.siemens.pki.cmpracomponent.cryptoservices;
-import static com.siemens.pki.cmpracomponent.util.NullUtil.ifNotNull;
-
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
import java.util.HashMap;
import java.util.Map;
+
import javax.crypto.Mac;
import javax.crypto.SecretKeyFactory;
+
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
@@ -43,6 +44,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static com.siemens.pki.cmpracomponent.util.NullUtil.ifNotNull;
+
/**
* utility class to translate between Java JCE Strings, OIDs, {@link Mac},
* {@link Digest} a.s.o.
@@ -241,10 +244,12 @@ String[] extractAliases(final ASN1ObjectIdentifier cmpId) {
KEY_ENCRYPTION_OIDS.addAll(CMSAlgorithm.AES128_CBC, "AES128_CBC", "AES128");
KEY_ENCRYPTION_OIDS.addAll(CMSAlgorithm.AES192_CBC, "AES192_CBC", "AES192");
KEY_ENCRYPTION_OIDS.addAll(CMSAlgorithm.AES256_CBC, "AES256_CBC", "AES256");
+
}
/**
* convert shared secrets from byte[] to char[]
+ *
* @param sharedSecret sharedSecret as byte[]
* @return sharedSecret as char[]
*/
@@ -261,6 +266,7 @@ public static char[] convertSharedSecretToPassword(final byte[] sharedSecret) {
/**
* get AlgorithmIdentifier for MessageDigest
+ *
* @param dig digest
* @return AlgorithmIdentifier
*/
@@ -293,8 +299,11 @@ public static AlgorithmIdentifier getAlgOID(final String algorithm) {
return null;
}
+
+
/**
* get OID for name of KEK algorithm
+ *
* @param id name of KEK algorithm
* @return KEK OID
* @throws NoSuchAlgorithmException if id is unknown
@@ -303,8 +312,10 @@ public static ASN1ObjectIdentifier getKekOID(final String id) throws NoSuchAlgor
return KEK_OIDS.getOid(id);
}
+
/**
* get OID for key agreement algorithm name
+ *
* @param id name of key agreement algorithm
* @return key agreement OID
* @throws NoSuchAlgorithmException if id is unknown
@@ -315,6 +326,7 @@ public static final ASN1ObjectIdentifier getKeyAgreementOID(final String id) thr
/**
* get OID for key encryption algorithm name
+ *
* @param id name of key encryption algorithm
* @return key encryption OID
* @throws NoSuchAlgorithmException if id is unknown
@@ -324,7 +336,8 @@ public static ASN1ObjectIdentifier getKeyEncryptionOID(final String id) throws N
}
/**
- * get OID for MAC name
+ * get MAC for name or OID
+ *
* @param macId name of MAC
* @return mac
* @throws NoSuchAlgorithmException if macId is unknown
@@ -333,8 +346,20 @@ public static Mac getMac(final String macId) throws NoSuchAlgorithmException {
return Mac.getInstance(macId, CertUtility.getBouncyCastleProvider());
}
+ /**
+ * get Signature for name or OID
+ *
+ * @param signatureId name of Signature
+ * @return signature
+ * @throws NoSuchAlgorithmException if signatureId is unknown
+ */
+ public static Signature getSignature(String signatureId) throws NoSuchAlgorithmException {
+ return Signature.getInstance(signatureId, CertUtility.getBouncyCastleProvider());
+ }
+
/**
* get MessageDigest for MessageDigest name
+ *
* @param id MessageDigest name
* @return MessageDigest instance
* @throws NoSuchAlgorithmException if nothing found
@@ -345,6 +370,7 @@ public static MessageDigest getMessageDigest(final String id) throws NoSuchAlgor
/**
* get OID for mac algorithm name
+ *
* @param macAlg mac algorithm
* @return OID for mac
*/
@@ -358,6 +384,7 @@ public static ASN1ObjectIdentifier getOidForMac(final String macAlg) {
/**
* get PRF for id of PRF
+ *
* @param id id of PRF
* @return PRF
* @throws NoSuchAlgorithmException if id is unknown
@@ -384,6 +411,7 @@ public static SecretKeyFactory getSecretKeyFactory(final String id) throws NoSuc
/**
* get AlgorithmIdentifier of preferred signature algorithm for a key
+ *
* @param key the key
* @return AlgorithmIdentifier of preferred signature algorithm
*/
@@ -393,14 +421,17 @@ public static AlgorithmIdentifier getSigningAlgIdFromKey(final Key key) {
/**
* get AlgorithmIdentifier for keyAlgorithm name
+ *
* @param keyAlgorithm name
* @return AlgorithmIdentifier
*/
public static AlgorithmIdentifier getSigningAlgIdFromKeyAlg(final String keyAlgorithm) {
return DEFAULT_SIGNATURE_ALGORITHM_IDENTIFIER_FINDER.find(getSigningAlgNameFromKeyAlg(keyAlgorithm));
}
+
/**
* get AlgorithmIdentifier for signatureAlgorithmName
+ *
* @param signatureAlgorithmName the name
* @return AlgorithmIdentifier
*/
@@ -427,15 +458,15 @@ public static String getSigningAlgNameFromKey(final Key key) {
* key uses algorithms beside RSA, EC or EdDSA
*/
public static String getSigningAlgNameFromKeyAlg(final String keyAlgorithm) {
- if (keyAlgorithm.startsWith("Ed")) {
- // EdDSA key
- return keyAlgorithm;
- }
- if ("EC".equals(keyAlgorithm)) {
+ if (keyAlgorithm.startsWith("EC")) {
// EC key
return "SHA256withECDSA";
}
- return "SHA256with" + keyAlgorithm;
+ if (keyAlgorithm.startsWith("RSA")) {
+ return "SHA256with" + keyAlgorithm;
+ }
+
+ return keyAlgorithm;
}
/**
@@ -454,6 +485,7 @@ public enum PasswordBasedMacAlg {
/**
* get an Password-based message authentication code (MAC) algorithms
+ *
* @param passwordBasedMacAlgorithm id of Password-Based MAC used for protection
* @return related {@link PasswordBasedMacAlg}
* @throws NoSuchAlgorithmException if id is unknown
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/CertUtility.java b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/CertUtility.java
index 817789ae..154566da 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/CertUtility.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/CertUtility.java
@@ -21,7 +21,9 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
+import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
@@ -32,7 +34,9 @@
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
+import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
+import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -41,12 +45,18 @@
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.cmp.CMPCertificate;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.jcajce.JcaX509ContentVerifierProviderBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+import org.bouncycastle.pkcs.PKCSException;
/**
* A utility class for certificate handling
*/
public class CertUtility {
+
private static final SecureRandom RANDOM = new SecureRandom();
private static Provider BOUNCY_CASTLE_PROVIDER;
private static CertificateFactory certificateFactory;
@@ -171,6 +181,18 @@ public static Provider getBouncyCastleProvider() {
return BOUNCY_CASTLE_PROVIDER;
}
+ /**
+ * create an {@link X509CRL} from a byte string
+ *
+ * @param encodedCrl DER encoded CRL
+ * @return parsed CRL
+ * @throws GeneralSecurityException in case of parsing error
+ */
+ public static X509CRL parseCrl(byte[] encodedCrl) throws GeneralSecurityException {
+ return (X509CRL) CertificateFactory.getInstance("X.509", CertUtility.getBouncyCastleProvider())
+ .generateCRL(new ByteArrayInputStream(encodedCrl));
+ }
+
/**
* Function to retrieve the static certificate factory object
*
@@ -210,6 +232,50 @@ public static boolean isIntermediateCertificate(final X509Certificate cert) {
}
}
+ // utility class
+ private CertUtility() {}
+
+ /**
+ * extract PublicKey from SubjectPublicKeyInfo
+ * @param subjectPublicKeyInfo the subjectPublicKeyInfo
+ * @return extracted PublicKey
+ * @throws NoSuchAlgorithmException if PublicKey algorithm is not known
+ * @throws IOException if subjectPublicKeyInfo could not parsed
+ */
+ public static PublicKey parsePublicKey(final SubjectPublicKeyInfo subjectPublicKeyInfo)
+ throws NoSuchAlgorithmException, IOException {
+ try {
+ return KeyFactory.getInstance(
+ subjectPublicKeyInfo.getAlgorithm().getAlgorithm().toString(),
+ CertUtility.getBouncyCastleProvider())
+ .generatePublic(new X509EncodedKeySpec(subjectPublicKeyInfo.getEncoded(ASN1Encoding.DER)));
+ } catch (NoSuchAlgorithmException | IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * check integrity of an PKCS#10 request
+ * @param p10Request request to check
+ * @return true
if request is valid
+ * @throws PKCSException if request could not be parsed
+ * @throws OperatorCreationException if the signature cannot be processed or is inappropriate
+ */
+ public static boolean validateP10Request(final PKCS10CertificationRequest p10Request)
+ throws PKCSException, OperatorCreationException {
+ try {
+ return p10Request.isSignatureValid(new JcaX509ContentVerifierProviderBuilder()
+ .setProvider(CertUtility.getBouncyCastleProvider())
+ .build(p10Request.getSubjectPublicKeyInfo()));
+ } catch (PKCSException | OperatorCreationException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
private static class BouncyCastleInitializer {
private static synchronized Provider getInstance() {
return Arrays.stream(Security.getProviders())
@@ -218,6 +284,4 @@ private static synchronized Provider getInstance() {
.orElseGet(BouncyCastleProvider::new);
}
}
- // utility class
- private CertUtility() {}
}
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/CmsDecryptor.java b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/CmsDecryptor.java
index a512e6df..0761415e 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/CmsDecryptor.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/CmsDecryptor.java
@@ -17,8 +17,6 @@
*/
package com.siemens.pki.cmpracomponent.cryptoservices;
-import static com.siemens.pki.cmpracomponent.util.NullUtil.ifNotNull;
-
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import org.bouncycastle.asn1.cms.ContentInfo;
@@ -31,13 +29,10 @@
import org.bouncycastle.cms.RecipientInformation;
import org.bouncycastle.cms.RecipientInformationStore;
import org.bouncycastle.cms.jcajce.JceKeyAgreeEnvelopedRecipient;
-import org.bouncycastle.cms.jcajce.JceKeyAgreeRecipient;
import org.bouncycastle.cms.jcajce.JceKeyAgreeRecipientId;
import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
-import org.bouncycastle.cms.jcajce.JceKeyTransRecipient;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientId;
import org.bouncycastle.cms.jcajce.JcePasswordEnvelopedRecipient;
-import org.bouncycastle.cms.jcajce.JcePasswordRecipient;
/**
* CMS data decryption
@@ -46,37 +41,32 @@ public class CmsDecryptor {
private static final RecipientId passRecipientId = new PasswordRecipientId();
- private final JceKeyTransRecipient transRecipient;
-
private final JceKeyTransRecipientId transRecipientId;
private final RecipientId agreeRecipientId;
- private final JceKeyAgreeRecipient agreeRecipient;
- private final JcePasswordRecipient passwordRecipient;
+ private PrivateKey recipientKey;
+
+ private char[] passwd;
+
/**
* ctor
+ *
* @param recipientCert cert used to decrypt
- * @param recipientKey private key used to decrypt
- * @param passwd password used to decrypt
+ * @param recipientKey private key used to decrypt
+ * @param passwd password used to decrypt
*/
public CmsDecryptor(final X509Certificate recipientCert, final PrivateKey recipientKey, final char[] passwd) {
+ this.recipientKey = recipientKey;
+ this.passwd = passwd;
+
if (recipientCert != null && recipientKey != null) {
transRecipientId = new JceKeyTransRecipientId(recipientCert);
- transRecipient =
- new JceKeyTransEnvelopedRecipient(recipientKey).setProvider(CertUtility.getBouncyCastleProvider());
agreeRecipientId = new JceKeyAgreeRecipientId(recipientCert);
- agreeRecipient =
- new JceKeyAgreeEnvelopedRecipient(recipientKey).setProvider(CertUtility.getBouncyCastleProvider());
} else {
transRecipientId = null;
- transRecipient = null;
agreeRecipientId = null;
- agreeRecipient = null;
}
- passwordRecipient = ifNotNull(passwd, x -> new JcePasswordEnvelopedRecipient(x)
- .setProvider("BC")
- .setPasswordConversionScheme(PasswordRecipient.PKCS5_SCHEME2_UTF8));
}
/**
@@ -93,23 +83,27 @@ public byte[] decrypt(final EnvelopedData envelopedData) throws CMSException {
new ContentInfo(envelopedData.getEncryptedContentInfo().getContentType(), envelopedData));
final RecipientInformationStore recipients = cmsEnvelopedData.getRecipientInfos();
- RecipientInformation recipient;
+
if (agreeRecipientId != null) {
- recipient = recipients.get(agreeRecipientId);
+ RecipientInformation recipient = recipients.get(agreeRecipientId);
if (recipient != null) {
- return recipient.getContent(agreeRecipient);
+ return recipient.getContent(new JceKeyAgreeEnvelopedRecipient(recipientKey)
+ .setProvider(CertUtility.getBouncyCastleProvider()));
}
}
if (transRecipientId != null) {
- recipient = recipients.get(transRecipientId);
+ RecipientInformation recipient = recipients.get(transRecipientId);
if (recipient != null) {
- return recipient.getContent(transRecipient);
+ return recipient.getContent(new JceKeyTransEnvelopedRecipient(recipientKey)
+ .setProvider(CertUtility.getBouncyCastleProvider()));
}
}
- if (passwordRecipient != null) {
- recipient = recipients.get(passRecipientId);
+ if (passRecipientId != null) {
+ RecipientInformation recipient = recipients.get(passRecipientId);
if (recipient != null) {
- return recipient.getContent(passwordRecipient);
+ return recipient.getContent(new JcePasswordEnvelopedRecipient(passwd)
+ .setProvider(CertUtility.getBouncyCastleProvider())
+ .setPasswordConversionScheme(PasswordRecipient.PKCS5_SCHEME2_UTF8));
}
}
throw new IllegalArgumentException("recipient for certificate not found");
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/CmsEncryptorBase.java b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/CmsEncryptorBase.java
index e009f6e4..c960d6a6 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/CmsEncryptorBase.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/CmsEncryptorBase.java
@@ -18,9 +18,9 @@
package com.siemens.pki.cmpracomponent.cryptoservices;
import com.siemens.pki.cmpracomponent.configuration.CkgContext;
-import com.siemens.pki.cmpracomponent.util.ConfigLogger;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.cms.EnvelopedData;
import org.bouncycastle.cms.CMSEnvelopedData;
@@ -35,8 +35,14 @@
*/
public class CmsEncryptorBase {
+ protected static Provider getProvider() {
+ return CertUtility.getBouncyCastleProvider();
+ }
+
private final CMSEnvelopedDataGenerator envGen = new CMSEnvelopedDataGenerator();
private final CkgContext config;
+
+ @SuppressWarnings("unused")
private final String interfaceName;
protected CmsEncryptorBase(final CkgContext config, String interfaceName) {
@@ -44,8 +50,23 @@ protected CmsEncryptorBase(final CkgContext config, String interfaceName) {
this.interfaceName = interfaceName;
}
- protected void addRecipientInfoGenerator(final RecipientInfoGenerator recipientGenerator) {
- envGen.addRecipientInfoGenerator(recipientGenerator);
+ /**
+ * encrypt the data
+ *
+ * @param msg data to encrypt
+ * @return encrypted data
+ * @throws CMSException in case of an CMS processing error
+ * @throws NoSuchAlgorithmException if getContentEncryptionAlg in config is
+ * unknown
+ */
+ public EnvelopedData encrypt(final byte[] msg) throws CMSException, NoSuchAlgorithmException {
+ try {
+ return encrypt(msg, CertUtility.getBouncyCastleProvider());
+ } catch (CMSException | NoSuchAlgorithmException e) {
+ throw e;
+ } catch (Exception e) {
+ return null;
+ }
}
/**
@@ -57,14 +78,12 @@ protected void addRecipientInfoGenerator(final RecipientInfoGenerator recipientG
* @throws NoSuchAlgorithmException if getContentEncryptionAlg in config is
* unknown
*/
- public EnvelopedData encrypt(final byte[] msg) throws CMSException, NoSuchAlgorithmException {
+ private EnvelopedData encrypt(final byte[] msg, Provider provider) throws CMSException, NoSuchAlgorithmException {
+ CMSProcessableByteArray content = new CMSProcessableByteArray(msg);
final CMSEnvelopedData cmsEnvData = envGen.generate(
- new CMSProcessableByteArray(msg),
- new JceCMSContentEncryptorBuilder(AlgorithmHelper.getKeyEncryptionOID(ConfigLogger.log(
- interfaceName,
- "CkgContext.getContentEncryptionAlg()",
- config::getContentEncryptionAlg)))
- .setProvider(CertUtility.getBouncyCastleProvider())
+ content,
+ new JceCMSContentEncryptorBuilder(AlgorithmHelper.getKeyEncryptionOID(config.getContentEncryptionAlg()))
+ .setProvider(provider)
.build());
return EnvelopedData.getInstance(cmsEnvData.toASN1Structure().getContent());
}
@@ -72,15 +91,19 @@ public EnvelopedData encrypt(final byte[] msg) throws CMSException, NoSuchAlgori
/**
* encrypt the data
*
- * @param asn1Object ASN.1 object to encrypt
+ * @param asn1Object ASN1.1 object to encrypt
* @return encrypted data
- * @throws CMSException in case of an CMS processing error
- * @throws IOException in case of ASN.1 encoding error
- * @throws NoSuchAlgorithmException if getContentEncryptionAlg in config is
+ * @throws CMSException in case of an CMS processing error
+ * @throws IOException in case of ASN.1 encoding error
+ * @throws NoSuchAlgorithmException f getContentEncryptionAlg in config is
* unknown
*/
public EnvelopedData encrypt(final ASN1Object asn1Object)
throws CMSException, IOException, NoSuchAlgorithmException {
return encrypt(asn1Object.getEncoded());
}
+
+ protected void addRecipientInfoGenerator(final RecipientInfoGenerator recipientGenerator) {
+ envGen.addRecipientInfoGenerator(recipientGenerator);
+ }
}
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/DataSignVerifier.java b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/DataSignVerifier.java
index 05a7e9cd..cf340a38 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/DataSignVerifier.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/DataSignVerifier.java
@@ -20,12 +20,11 @@
import com.siemens.pki.cmpracomponent.configuration.VerificationContext;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.security.KeyFactory;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
-import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -33,6 +32,8 @@
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.cms.SignedData;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.cert.CertException;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSSignedData;
@@ -40,26 +41,23 @@
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.util.Store;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* a verifier for CMS signed data
*/
public class DataSignVerifier extends TrustCredentialAdapter {
- private static final Logger LOGGER = LoggerFactory.getLogger(DataSignVerifier.class);
- private static final JcaSimpleSignerInfoVerifierBuilder builder =
- new JcaSimpleSignerInfoVerifierBuilder().setProvider(CertUtility.getBouncyCastleProvider());
-
/**
* verify and strip off a signature
+ *
* @param encodedSignedData date to verify
* @return date without signature
* @throws CertificateException in case of error
- * @throws CMSException in case of error
- * @throws IOException in case of error
+ * @throws CMSException in case of error
+ * @throws IOException in case of error
*/
public static byte[] verifySignature(final byte[] encodedSignedData)
throws CertificateException, CMSException, IOException {
@@ -84,7 +82,10 @@ private static byte[] verifySignature(
final Collection certCollection = certs.getMatches(signerInfo.getSID());
final X509CertificateHolder cert = certCollection.iterator().next();
try {
- if (signerInfo.verify(builder.build(cert)) && trustValidator.test(cert, allCerts)) {
+ boolean verified = signerInfo.verify(new JcaSimpleSignerInfoVerifierBuilder()
+ .setProvider(CertUtility.getBouncyCastleProvider())
+ .build(cert));
+ if (verified && trustValidator.test(cert, allCerts)) {
final CMSTypedData cmsData = signedData.getSignedContent();
final ByteArrayOutputStream bOut = new ByteArrayOutputStream();
cmsData.write(bOut);
@@ -99,6 +100,7 @@ private static byte[] verifySignature(
/**
* ctor
+ *
* @param config context used for verification
* @param interfaceName CMP interface name for logging
*/
@@ -107,7 +109,8 @@ public DataSignVerifier(final VerificationContext config, String interfaceName)
}
private boolean validate(final X509CertificateHolder cert, final List allCerts)
- throws CertificateException, IOException, NoSuchProviderException {
+ throws IOException, OperatorCreationException, CertException, CertificateEncodingException,
+ NoSuchProviderException, CertificateException {
return validateCertAgainstTrust(CertUtility.asX509Certificate(cert.getEncoded()), allCerts) != null;
}
@@ -133,11 +136,12 @@ public byte[] verifySignatureAndTrust(final byte[] encodedSignedData)
/**
* strip off a private key from signed date
+ *
* @param encodedSignedData the BER encoding of the SignedData
* @return found private key
* @throws CertificateException in case of error
- * @throws IOException in case of error
- * @throws CMSException in case of error
+ * @throws IOException in case of error
+ * @throws CMSException in case of error
*/
public PrivateKey verifySignedKey(final byte[] encodedSignedData)
throws CertificateException, IOException, CMSException {
@@ -145,16 +149,9 @@ public PrivateKey verifySignedKey(final byte[] encodedSignedData)
if (verifiedContent == null) {
return null;
}
- final PKCS8EncodedKeySpec pkcs8EncKeySpec = new PKCS8EncodedKeySpec(verifiedContent);
- for (final String keyType : new String[] {"RSA", "EC", "Ed448", "Ed25519"}) {
- try {
- final KeyFactory factory = KeyFactory.getInstance(keyType, CertUtility.getBouncyCastleProvider());
- return factory.generatePrivate(pkcs8EncKeySpec);
- } catch (final Exception e) {
- // try next key type
- }
- }
- LOGGER.error("could not load private key");
- return null;
+ PrivateKeyInfo pki = PrivateKeyInfo.getInstance(verifiedContent);
+ return new JcaPEMKeyConverter()
+ .setProvider(CertUtility.getBouncyCastleProvider())
+ .getPrivateKey(pki);
}
}
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/DataSigner.java b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/DataSigner.java
index 0ada2a5b..e8d8bc59 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/DataSigner.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/DataSigner.java
@@ -51,20 +51,28 @@ public class DataSigner {
/**
* ctor
+ *
* @param credentialService credentials used for signing
- * @throws OperatorCreationException in case of error
+ * @throws OperatorCreationException in case of error
* @throws CertificateEncodingException in case of error
- * @throws IOException in case of error
- * @throws CMSException in case of error
+ * @throws IOException in case of error
+ * @throws CMSException in case of error
*/
public DataSigner(final BaseCredentialService credentialService)
throws OperatorCreationException, CertificateEncodingException, IOException, CMSException {
- final SignerInfoGenerator signerInfoGenerator = new JcaSimpleSignerInfoGeneratorBuilder()
- .setProvider(CertUtility.getBouncyCastleProvider())
- .build(
- credentialService.getSignatureAlgorithmName(),
- credentialService.getPrivateKey(),
- credentialService.getEndCertificate());
+ SignerInfoGenerator signerInfoGenerator;
+ try {
+ signerInfoGenerator = new JcaSimpleSignerInfoGeneratorBuilder()
+ .setProvider(CertUtility.getBouncyCastleProvider())
+ .build(
+ credentialService.getSignatureAlgorithmName(),
+ credentialService.getPrivateKey(),
+ credentialService.getEndCertificate());
+ } catch (OperatorCreationException | CertificateEncodingException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
gen.addSignerInfoGenerator(signerInfoGenerator);
final List certChain = new ArrayList<>();
@@ -79,13 +87,14 @@ public DataSigner(final BaseCredentialService credentialService)
/**
* ctor
- * @param privateKey private key used for signing
+ *
+ * @param privateKey private key used for signing
* @param endCertificate certificate used for signing
* @param interfaceName CMP interface name for logging
* @throws CertificateEncodingException in case of error
- * @throws OperatorCreationException in case of error
- * @throws IOException in case of error
- * @throws CMSException in case of error
+ * @throws OperatorCreationException in case of error
+ * @throws IOException in case of error
+ * @throws CMSException in case of error
*/
public DataSigner(final PrivateKey privateKey, final X509Certificate endCertificate, String interfaceName)
throws CertificateEncodingException, OperatorCreationException, IOException, CMSException {
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/KeyAgreementEncryptor.java b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/KeyAgreementEncryptor.java
index c492565c..cd03f4c8 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/KeyAgreementEncryptor.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/KeyAgreementEncryptor.java
@@ -78,10 +78,7 @@ public KeyAgreementEncryptor(
"CkgKeyAgreementContext.getKeyEncryptionAlg()",
keyAgreementContext::getKeyEncryptionAlg)));
- infGen.addRecipient(ConfigLogger.log(
- interfaceName,
- "CkgKeyAgreementContext.getRecipient(X509Certificate)",
- () -> keyAgreementContext.getRecipient(protectingCert)));
- addRecipientInfoGenerator(infGen.setProvider(CertUtility.getBouncyCastleProvider()));
+ infGen.addRecipient(keyAgreementContext.getRecipient(protectingCert));
+ addRecipientInfoGenerator(infGen.setProvider(getProvider()));
}
}
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/KeyPairGeneratorFactory.java b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/KeyPairGeneratorFactory.java
index 0a72305e..4c8abd1a 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/KeyPairGeneratorFactory.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/KeyPairGeneratorFactory.java
@@ -22,6 +22,7 @@
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.ECGenParameterSpec;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.ec.CustomNamedCurves;
import org.bouncycastle.jce.spec.ECParameterSpec;
@@ -42,6 +43,7 @@ private KeyPairGeneratorFactory() {}
* @throws GeneralSecurityException if key pair generator generation failed
*/
public static KeyPairGenerator getEcKeyPairGenerator(final String curve) throws GeneralSecurityException {
+
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", CertUtility.getBouncyCastleProvider());
try {
final ECGenParameterSpec ecSpec = new ECGenParameterSpec(curve);
@@ -67,6 +69,29 @@ public static KeyPairGenerator getEdDsaKeyPairGenerator(final String keyType) th
return KeyPairGenerator.getInstance(keyType, CertUtility.getBouncyCastleProvider());
}
+ /**
+ * Generate key pair generator, let BC/BCPQ find the Algorithm.
+ *
+ * @param algOid Algorithm OID
+ * @return the generated key pair generator
+ * @throws GeneralSecurityException if key pair generator generation failed
+ */
+ public static KeyPairGenerator getGenericKeyPairGenerator(final ASN1ObjectIdentifier algOid)
+ throws GeneralSecurityException {
+ return KeyPairGenerator.getInstance(algOid.getId(), CertUtility.getBouncyCastleProvider());
+ }
+
+ /**
+ * Generate key pair generator, let BC/BCPQ find the Algorithm.
+ *
+ * @param keyType type of key
+ * @return the generated key pair generator
+ * @throws GeneralSecurityException if key pair generator generation failed
+ */
+ public static KeyPairGenerator getGenericKeyPairGenerator(final String keyType) throws GeneralSecurityException {
+ return KeyPairGenerator.getInstance(keyType, CertUtility.getBouncyCastleProvider());
+ }
+
/**
* generate RSA key pair generator
*
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/KeyTransportEncryptor.java b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/KeyTransportEncryptor.java
index e54a4f89..4d2f799e 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/KeyTransportEncryptor.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/KeyTransportEncryptor.java
@@ -71,6 +71,6 @@ public KeyTransportEncryptor(
.createSubjectKeyIdentifier(publicKey)
.getKeyIdentifier(),
publicKey)
- .setProvider(CertUtility.getBouncyCastleProvider()));
+ .setProvider(getProvider()));
}
}
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/PasswordEncryptor.java b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/PasswordEncryptor.java
index d5f954d4..db44632e 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/PasswordEncryptor.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/PasswordEncryptor.java
@@ -58,13 +58,9 @@ public PasswordEncryptor(final CkgContext config, final int initialRequestType,
"CkgPasswordContext.getEncryptionCredentials()",
passwordContext::getEncryptionCredentials);
addRecipientInfoGenerator(new JcePasswordRecipientInfoGenerator(
- AlgorithmHelper.getKeyEncryptionOID(ConfigLogger.log(
- interfaceName, "CkgPasswordContext.getKekAlg()", passwordContext::getKekAlg)),
- AlgorithmHelper.convertSharedSecretToPassword(ConfigLogger.log(
- interfaceName,
- "SharedSecretCredentialContext.getSharedSecret()",
- encryptionCredentials::getSharedSecret)))
- .setProvider(CertUtility.getBouncyCastleProvider())
+ AlgorithmHelper.getKeyEncryptionOID(passwordContext.getKekAlg()),
+ AlgorithmHelper.convertSharedSecretToPassword(encryptionCredentials.getSharedSecret()))
+ .setProvider(getProvider())
.setPasswordConversionScheme(PasswordRecipient.PKCS5_SCHEME2_UTF8)
.setPRF(AlgorithmHelper.getPrf(ConfigLogger.log(
interfaceName, "SharedSecretCredentialContext.getPrf()", encryptionCredentials::getPrf)))
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/TrustCredentialAdapter.java b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/TrustCredentialAdapter.java
index 61f4393a..71906db5 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/TrustCredentialAdapter.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/TrustCredentialAdapter.java
@@ -19,6 +19,7 @@
import com.siemens.pki.cmpracomponent.configuration.VerificationContext;
import com.siemens.pki.cmpracomponent.util.ConfigLogger;
+import java.io.IOException;
import java.net.URI;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
@@ -27,6 +28,7 @@
import java.security.cert.CertPathBuilderException;
import java.security.cert.CertStore;
import java.security.cert.CertStoreParameters;
+import java.security.cert.CertificateEncodingException;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXCertPathBuilderResult;
@@ -36,12 +38,19 @@
import java.security.cert.X509CRL;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
+import org.bouncycastle.asn1.x509.SubjectAltPublicKeyInfo;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.CertException;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -52,7 +61,6 @@
*/
public class TrustCredentialAdapter {
- // private static final BouncyCastleProvider PROVIDER = CertUtility.getBouncyCastleProvider();
private static final String PROVIDER = "SUN";
private static final String FALSE_STRING = "false";
@@ -67,7 +75,8 @@ public class TrustCredentialAdapter {
/**
* ctor
- * @param config specific configuration
+ *
+ * @param config specific configuration
* @param interfaceName CMP interface name for logging
*/
public TrustCredentialAdapter(final VerificationContext config, String interfaceName) {
@@ -100,12 +109,17 @@ public boolean isIntermediateCertAcceptable(X509Certificate cert) {
* validation
* @return the validated chain without trust anchor but with cert or
* null
if the validation failed
- * @throws NoSuchProviderException if SUN provider is not available
+ * @throws NoSuchProviderException if provider is not available
+ * @throws IOException in case of encoding error
+ * @throws CertificateEncodingException in case of encoding error
+ * @throws CertException in case of encoding error
+ * @throws OperatorCreationException if certificate validation fails
*/
@SuppressWarnings("unchecked")
public synchronized List extends X509Certificate> validateCertAgainstTrust(
final X509Certificate cert, final List additionalIntermediateCerts)
- throws NoSuchProviderException {
+ throws NoSuchProviderException, CertificateEncodingException, IOException, CertException,
+ OperatorCreationException {
final Collection trustedCertificates = ConfigLogger.logOptional(
interfaceName, "VerificationContext.getTrustedCertificates()", config::getTrustedCertificates);
if (trustedCertificates == null) {
@@ -233,6 +247,26 @@ public synchronized List extends X509Certificate> validateCertAgainstTrust(
return null;
}
}
+ // check alternative signature
+ final JcaContentVerifierProviderBuilder verifier = new JcaContentVerifierProviderBuilder();
+ List altChain = new ArrayList<>(resultChain.size() + 1);
+ final X509CertificateHolder rootHolder = new X509CertificateHolder(
+ result.getTrustAnchor().getTrustedCert().getEncoded());
+ altChain.add(rootHolder);
+ for (X509Certificate aktCert : resultChain) {
+ altChain.add(1, new X509CertificateHolder(aktCert.getEncoded()));
+ }
+
+ SubjectAltPublicKeyInfo altLastKey = SubjectAltPublicKeyInfo.fromExtensions(rootHolder.getExtensions());
+ for (X509CertificateHolder aktHolder : altChain) {
+ if (altLastKey != null) {
+ SubjectPublicKeyInfo lastKey = SubjectPublicKeyInfo.getInstance(altLastKey.getEncoded());
+ if (!aktHolder.isAlternativeSignatureValid(verifier.build(lastKey))) {
+ return null;
+ }
+ }
+ altLastKey = SubjectAltPublicKeyInfo.fromExtensions(aktHolder.getExtensions());
+ }
return resultChain;
} catch (final CertPathBuilderException certExcpt) {
//
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 01fd36ba..440a20f1 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/MsgOutputProtector.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/MsgOutputProtector.java
@@ -112,6 +112,7 @@ public MsgOutputProtector(
* ctor
* @param config specific configuration
* @param interfaceName name of interface used in logging messages
+ * @param credentialContext protection credentials to use
* @throws CmpProcessingException in case of inconsistent configuration
* @throws GeneralSecurityException in case of broken configuration
*/
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 74b3c2ea..b587ed28 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/PkiMessageGenerator.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/msggeneration/PkiMessageGenerator.java
@@ -30,8 +30,11 @@
import java.io.IOException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@@ -68,6 +71,7 @@
import org.bouncycastle.asn1.cmp.ProtectedPart;
import org.bouncycastle.asn1.cmp.RevDetails;
import org.bouncycastle.asn1.cmp.RevReqContent;
+import org.bouncycastle.asn1.cms.EnvelopedData;
import org.bouncycastle.asn1.crmf.CertReqMessages;
import org.bouncycastle.asn1.crmf.CertReqMsg;
import org.bouncycastle.asn1.crmf.CertRequest;
@@ -75,8 +79,11 @@
import org.bouncycastle.asn1.crmf.CertTemplateBuilder;
import org.bouncycastle.asn1.crmf.Controls;
import org.bouncycastle.asn1.crmf.EncryptedKey;
+import org.bouncycastle.asn1.crmf.POPOPrivKey;
import org.bouncycastle.asn1.crmf.POPOSigningKey;
import org.bouncycastle.asn1.crmf.ProofOfPossession;
+import org.bouncycastle.asn1.crmf.SubsequentMessage;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
@@ -84,8 +91,13 @@
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.ExtensionsGenerator;
import org.bouncycastle.asn1.x509.GeneralName;
-import org.bouncycastle.cert.cmp.CMPException;
+import org.bouncycastle.cms.CMSAlgorithm;
+import org.bouncycastle.cms.CMSEnvelopedData;
+import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
+import org.bouncycastle.cms.jcajce.JceKEMRecipientInfoGenerator;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DigestCalculator;
@@ -252,8 +264,8 @@ public ASN1OctetString getTransactionID() {
*
* @param headerProvider PKI header
* @param protectionProvider PKI protection
- * @param newRecipient outgoing recipient or null
if recipient
- * from headerProvider should be used
+ * @param newRecipient outgoing recipient or null
if
+ * recipient from headerProvider should be used
* @param body message body
* @param issuingChain chain of enrolled certificate to append at the
* extraCerts
@@ -317,16 +329,20 @@ public static PKIMessage generateAndProtectMessage(
* @throws Exception in case of error
*/
public static PKIBody generateCertConfBody(final CMPCertificate certificate) throws Exception {
- final AlgorithmIdentifier digAlg =
- DIG_ALG_FINDER.find(certificate.getX509v3PKCert().getSignatureAlgorithm());
- if (digAlg == null) {
- throw new CMPException("cannot find algorithm for digest from signature");
- }
-
- final DigestCalculator digester = BC_DIGEST_CALCULATOR_PROVIDER.get(digAlg);
+ final AlgorithmIdentifier signatureAlgorithm =
+ certificate.getX509v3PKCert().getSignatureAlgorithm();
+ final AlgorithmIdentifier digAlgFromCert =
+ ifNotNull(signatureAlgorithm, x -> DIG_ALG_FINDER.find(signatureAlgorithm));
+ final AlgorithmIdentifier digAlgForHash =
+ computeDefaultIfNull(digAlgFromCert, () -> new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));
+ final DigestCalculator digester = BC_DIGEST_CALCULATOR_PROVIDER.get(digAlgForHash);
digester.getOutputStream().write(certificate.getEncoded(ASN1Encoding.DER));
final ASN1Sequence content = new DERSequence(new CertStatus[] {
- new CertStatus(digester.getDigest(), BigInteger.ZERO, new PKIStatusInfo(PKIStatus.granted))
+ new CertStatus(
+ digester.getDigest(),
+ BigInteger.ZERO,
+ new PKIStatusInfo(PKIStatus.granted),
+ digAlgFromCert != null ? null : digAlgForHash)
});
return new PKIBody(PKIBody.TYPE_CERT_CONFIRM, CertConfirmContent.getInstance(content));
}
@@ -398,6 +414,59 @@ public static PKIBody generateIpCpKupBody(
return new PKIBody(bodyType, new CertRepMessage(null, response));
}
+ /**
+ * generate a IP, CP or KUP body for returning an KEM encrypted cerificate
+ *
+ * @param bodyType bodyType PKIBody.TYPE_INIT_REP,
+ * PKIBody.TYPE_CERT_REP or
+ * PKIBody.TYPE_KEY_UPDATE_REP
+ * @param certificateToEncrypt the certificate to encrypt and return
+ * @return a IP, CP or KUP body
+ * @throws CertificateEncodingException in case of general error
+ * @throws CMSException in case of error in CMS processing
+ */
+ public static PKIBody generateEncryptedIpCpKupBody(final int bodyType, X509Certificate certificateToEncrypt)
+ throws CertificateEncodingException, CMSException {
+ // encrypt certificate
+ // KDF2
+ // AlgorithmIdentifier kdfAlgorithm = new AlgorithmIdentifier(
+ // X9ObjectIdentifiers.id_kdf_kdf2,
+ // new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, DERNull.INSTANCE));
+ // KDF3
+ // AlgorithmIdentifier kdfAlgorithm = new AlgorithmIdentifier(
+ // X9ObjectIdentifiers.id_kdf_kdf3,
+ // new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, DERNull.INSTANCE));
+ // SHAKE256
+ AlgorithmIdentifier kdfAlgorithm = new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake256);
+
+ CMSEnvelopedDataGenerator envGen = new CMSEnvelopedDataGenerator();
+ // Issuer + serialnumber
+ // JceKEMRecipientInfoGenerator recipientInfoGenerator = new
+ // JceKEMRecipientInfoGenerator(certificateToEncrypt, CMSAlgorithm.AES256_WRAP);
+ // Subject Pulic Key
+ JceKEMRecipientInfoGenerator recipientInfoGenerator = new JceKEMRecipientInfoGenerator(
+ certificateToEncrypt.getPublicKey().getEncoded(),
+ certificateToEncrypt.getPublicKey(),
+ CMSAlgorithm.AES256_WRAP);
+ envGen.addRecipientInfoGenerator(recipientInfoGenerator.setKDF(kdfAlgorithm));
+ CMSProcessableByteArray content = new CMSProcessableByteArray(certificateToEncrypt.getEncoded());
+ final CMSEnvelopedData cmsEnvData = envGen.generate(
+ content,
+ new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES256_CBC)
+ .setProvider(CertUtility.getBouncyCastleProvider())
+ .build());
+ EnvelopedData encryptedCertAsEnvelope =
+ EnvelopedData.getInstance(cmsEnvData.toASN1Structure().getContent());
+ final CertResponse[] response = {
+ new CertResponse(
+ PkiMessageGenerator.CERT_REQ_ID_0,
+ new PKIStatusInfo(PKIStatus.granted),
+ new CertifiedKeyPair(new CertOrEncCert(new EncryptedKey(encryptedCertAsEnvelope))),
+ null)
+ };
+ return new PKIBody(bodyType, new CertRepMessage(null, response));
+ }
+
/**
* generate a IP, CP or KUP body containing an error
*
@@ -434,12 +503,19 @@ public static PKIBody generateIrCrKurBody(
if (privateKey == null) {
return new PKIBody(bodyType, new CertReqMessages(new CertReqMsg(certReq, new ProofOfPossession(), null)));
}
- final Signature sig = Signature.getInstance(AlgorithmHelper.getSigningAlgNameFromKey(privateKey));
- sig.initSign(privateKey);
- sig.update(certReq.getEncoded(ASN1Encoding.DER));
- final ProofOfPossession popo = new ProofOfPossession(new POPOSigningKey(
- null, AlgorithmHelper.getSigningAlgIdFromKey(privateKey), new DERBitString(sig.sign())));
- return new PKIBody(bodyType, new CertReqMessages(new CertReqMsg(certReq, popo, null)));
+ try {
+ final Signature sig = AlgorithmHelper.getSignature(AlgorithmHelper.getSigningAlgNameFromKey(privateKey));
+ sig.initSign(privateKey);
+ sig.update(certReq.getEncoded(ASN1Encoding.DER));
+ final ProofOfPossession popo = new ProofOfPossession(new POPOSigningKey(
+ null, AlgorithmHelper.getSigningAlgIdFromKey(privateKey), new DERBitString(sig.sign())));
+ return new PKIBody(bodyType, new CertReqMessages(new CertReqMsg(certReq, popo, null)));
+ } catch (NoSuchAlgorithmException ex) {
+ // POP signing not supported, try KEM
+ final ProofOfPossession popo = new ProofOfPossession(
+ ProofOfPossession.TYPE_KEY_ENCIPHERMENT, new POPOPrivKey(SubsequentMessage.encrCert));
+ return new PKIBody(bodyType, new CertReqMessages(new CertReqMsg(certReq, popo, null)));
+ }
}
/**
@@ -464,6 +540,7 @@ public static PKIBody generatePollRep(final int checkAfterTime) {
/**
* generate a PollReq body
+ *
* @return a PollReq body
*/
public static PKIBody generatePollReq() {
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 e33cf454..51d61653 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/RaDownstream.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/msgprocessing/RaDownstream.java
@@ -27,6 +27,7 @@
import com.siemens.pki.cmpracomponent.configuration.InventoryInterface;
import com.siemens.pki.cmpracomponent.configuration.NestedEndpointContext;
import com.siemens.pki.cmpracomponent.configuration.SignatureCredentialContext;
+import com.siemens.pki.cmpracomponent.cryptoservices.AlgorithmHelper;
import com.siemens.pki.cmpracomponent.cryptoservices.CertUtility;
import com.siemens.pki.cmpracomponent.cryptoservices.CmsEncryptorBase;
import com.siemens.pki.cmpracomponent.cryptoservices.DataSigner;
@@ -53,14 +54,12 @@
import com.siemens.pki.cmpracomponent.util.NullUtil.ExFunction;
import java.io.IOException;
import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.X509Certificate;
-import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
@@ -98,7 +97,6 @@
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
-import org.bouncycastle.cert.jcajce.JcaX509ContentVerifierProviderBuilder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.PKCSException;
@@ -126,9 +124,6 @@ private static Function wrap(ExFunction supportedMessageTypes;
private final Configuration config;
@@ -189,7 +184,14 @@ protected CmsEncryptorBase buildEncryptor(
return new KeyTransportEncryptor(ckgConfiguration, recipientCert, initialRequestType, interfaceName);
}
- // special handling for CR, IR, KUR
+ /**
+ * special handling for CR, IR, KUR
+ *
+ * @param incomingCertificateRequest
+ * @param outputProtector
+ * @return handled message
+ * @throws Exception in case of error
+ */
private PKIMessage handleCrmfCertificateRequest(
final PKIMessage incomingCertificateRequest, final PersistencyContext persistencyContext)
throws BaseCmpException, GeneralSecurityException, IOException {
@@ -341,7 +343,8 @@ private PKIMessage handleCrmfCertificateRequest(
persistencyContext.getCertProfile(),
requestBodyType)
|| popo == null
- || popo.getType() == ProofOfPossession.TYPE_RA_VERIFIED) {
+ || popo.getType() == ProofOfPossession.TYPE_RA_VERIFIED
+ || popo.getType() == ProofOfPossession.TYPE_KEY_ENCIPHERMENT) {
// popo invalid or raVerified, regenerate body
return PkiMessageGenerator.generateUnprotectMessage(
PkiMessageGenerator.buildForwardingHeaderProvider(incomingCertificateRequest),
@@ -350,12 +353,9 @@ private PKIMessage handleCrmfCertificateRequest(
// initial POPO still there and maybe usable again
final POPOSigningKey popoSigningKey = (POPOSigningKey) popo.getObject();
- final PublicKey publicKey = KeyFactory.getInstance(
- subjectPublicKeyInfo.getAlgorithm().getAlgorithm().toString(),
- CertUtility.getBouncyCastleProvider())
- .generatePublic(new X509EncodedKeySpec(subjectPublicKeyInfo.getEncoded(ASN1Encoding.DER)));
- final Signature sig = Signature.getInstance(
- popoSigningKey.getAlgorithmIdentifier().getAlgorithm().getId(), CertUtility.getBouncyCastleProvider());
+ final PublicKey publicKey = CertUtility.parsePublicKey(subjectPublicKeyInfo);
+ final Signature sig = AlgorithmHelper.getSignature(
+ popoSigningKey.getAlgorithmIdentifier().getAlgorithm().getId());
sig.initVerify(publicKey);
sig.update(certRequest.getEncoded(ASN1Encoding.DER));
if (sig.verify(popoSigningKey.getSignature().getBytes())) {
@@ -454,6 +454,10 @@ PKIMessage handleInputMessage(final PKIMessage in) {
responseFromUpstream.getProtection(),
responseFromUpstream.getExtraCerts()),
issuingChain);
+ if (responseBodyType == PKIBody.TYPE_NESTED) {
+ // never nest a nested message
+ return protectedResponse;
+ }
final NestedEndpointContext nestedEndpointContext = ConfigLogger.logOptional(
INTERFACE_NAME,
@@ -573,7 +577,7 @@ private PKIMessage handleP10CertificateRequest(
persistencyContext.setRequestType(body.getType());
final PKCS10CertificationRequest p10Request =
new PKCS10CertificationRequest((CertificationRequest) body.getContent());
- if (!p10Request.isSignatureValid(X509_CVPB.build(p10Request.getSubjectPublicKeyInfo()))) {
+ if (!CertUtility.validateP10Request(p10Request)) {
throw new CmpValidationException(
INTERFACE_NAME, PKIFailureInfo.badMessageCheck, "signature of PKCS#10 Request broken");
}
@@ -626,12 +630,7 @@ private PKIMessage handleRevocationRequest(PKIMessage incomingRequest, Persisten
final PKIBody body = incomingRequest.getBody();
final int requestType = body.getType();
persistencyContext.setRequestType(requestType);
- final InventoryInterface inventory = ConfigLogger.logOptional(
- INTERFACE_NAME,
- "Configuration.getInventory",
- config::getInventory,
- persistencyContext.getCertProfile(),
- requestType);
+ final InventoryInterface inventory = config.getInventory(persistencyContext.getCertProfile(), requestType);
if (inventory != null) {
final CertTemplate revTemplate =
((RevReqContent) body.getContent()).toRevDetailsArray()[0].getCertDetails();
@@ -657,7 +656,8 @@ private PKIMessage handleRevocationRequest(PKIMessage incomingRequest, Persisten
}
private PKIMessage handleValidatedRequest(final PKIMessage incomingRequest, final MessageContext messageContext)
- throws BaseCmpException, IOException {
+ throws BaseCmpException, IOException, OperatorCreationException {
+
// request pre processing
// by default there is no pre processing
PKIMessage preprocessedRequest = incomingRequest;
@@ -795,12 +795,7 @@ private PKIMessage processCertResponse(
persistencyContext.setIssuingChain(issuingChain);
// update inventory
- final InventoryInterface inventory = ConfigLogger.logOptional(
- INTERFACE_NAME,
- "Configuration.getInventory",
- config::getInventory,
- persistencyContext.getCertProfile(),
- responseType);
+ final InventoryInterface inventory = config.getInventory(persistencyContext.getCertProfile(), responseType);
if (inventory != null) {
final byte[] encodedEnrolledCertificate = enrolledCertificate.getEncoded();
if (!ConfigLogger.log(
@@ -828,7 +823,13 @@ private PKIMessage processCertResponse(
final PrivateKey newGeneratedPrivateKey = persistencyContext.getNewGeneratedPrivateKey();
if (newGeneratedPrivateKey == null) {
// no central key generation
- return responseFromUpstream;
+ if (!persistencyContext.isRespondedCertMustBeEncrypted()) {
+ return responseFromUpstream;
+ }
+ return PkiMessageGenerator.generateUnprotectMessage(
+ PkiMessageGenerator.buildForwardingHeaderProvider(PKIHeader.CMP_2021, responseFromUpstream),
+ PkiMessageGenerator.generateEncryptedIpCpKupBody(
+ responseFromUpstream.getBody().getType(), enrolledCertificateAsX509));
}
// central key generation, respond the private key too
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/msgvalidation/InputValidator.java b/src/main/java/com/siemens/pki/cmpracomponent/msgvalidation/InputValidator.java
index 305d6d4f..4218679b 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/msgvalidation/InputValidator.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/msgvalidation/InputValidator.java
@@ -91,7 +91,11 @@ public MessageContext validate(final PKIMessage in) throws BaseCmpException {
certProfile,
in.getBody().getType());
config.apply(certProfile, in.getBody().getType());
- new MessageBodyValidator(interfaceName, isRaVerifiedAcceptable, cmpInterface, certProfile).validate(in);
+ if (new MessageBodyValidator(interfaceName, isRaVerifiedAcceptable, cmpInterface, certProfile)
+ .validate(in)) {
+ persistencyContext.setRespondedCertMustBeEncrypted();
+ }
+ ;
final ProtectionValidator protectionValidator = new ProtectionValidator(
interfaceName,
ConfigLogger.logOptional(
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 99f884ec..cb2f7044 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/msgvalidation/MessageBodyValidator.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/msgvalidation/MessageBodyValidator.java
@@ -18,19 +18,17 @@
package com.siemens.pki.cmpracomponent.msgvalidation;
import com.siemens.pki.cmpracomponent.configuration.CmpMessageInterface;
+import com.siemens.pki.cmpracomponent.cryptoservices.AlgorithmHelper;
import com.siemens.pki.cmpracomponent.cryptoservices.CertUtility;
import com.siemens.pki.cmpracomponent.util.ConfigLogger;
import com.siemens.pki.cmpracomponent.util.MessageDumper;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
-import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.X509EncodedKeySpec;
import java.util.Date;
import java.util.Objects;
import java.util.function.BiPredicate;
@@ -70,15 +68,12 @@
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.cert.jcajce.JcaX509ContentVerifierProviderBuilder;
-import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
-import org.bouncycastle.pkcs.PKCSException;
/**
* A CMP message validator to ensure CMP messages conform to RFC 4210.
*/
-public class MessageBodyValidator implements ValidatorIF {
+public class MessageBodyValidator implements ValidatorIF {
private static final String CERT_REQ_ID_MUST_BE_0 = "CertReqId must be 0";
/**
@@ -88,9 +83,6 @@ public class MessageBodyValidator implements ValidatorIF {
private static final BigInteger MINUS_ONE = BigInteger.ONE.negate();
- private static final JcaX509ContentVerifierProviderBuilder jcaX509ContentVerifierProviderBuilder =
- new JcaX509ContentVerifierProviderBuilder().setProvider(CertUtility.getBouncyCastleProvider());
-
private final String interfaceName;
private final BiPredicate isRaVerifiedAcceptable;
@@ -101,6 +93,7 @@ public class MessageBodyValidator implements ValidatorIF {
/**
* ctor
+ *
* @param interfaceName name used in error messages and logging
* @param isRaVerifiedAcceptable should RaVerified accepted in POPO?
* @param cmpInterfaceConfig specific interface (downstream/upstream)
@@ -184,10 +177,12 @@ private void assertValueNotNull(final Object value, final int failInfo, final St
* CMP profile.
*
* @param message the CMP message to validate
+ *
+ * @return true
if certificate in CP must be encrypted for POP
* @throws BaseCmpException if validation failed
*/
@Override
- public String validate(final PKIMessage message) throws BaseCmpException {
+ public Boolean validate(final PKIMessage message) throws BaseCmpException {
try {
if (cmpInterfaceConfig != null) {
final ASN1GeneralizedTime messageTime = message.getHeader().getMessageTime();
@@ -210,8 +205,7 @@ public String validate(final PKIMessage message) throws BaseCmpException {
case PKIBody.TYPE_INIT_REQ:
case PKIBody.TYPE_CERT_REQ:
case PKIBody.TYPE_KEY_UPDATE_REQ:
- validateCrmfCertReq(bodyType, (CertReqMessages) content, certProfile, bodyType);
- break;
+ return validateCrmfCertReq(bodyType, (CertReqMessages) content, certProfile, bodyType);
case PKIBody.TYPE_P10_CERT_REQ:
validateP10CertReq((CertificationRequest) content);
break;
@@ -263,7 +257,7 @@ public String validate(final PKIMessage message) throws BaseCmpException {
PKIFailureInfo.systemFailure,
"internal error in message validation: " + thr.getLocalizedMessage());
}
- return certProfile;
+ return false;
}
private void validateCertConfirm(final CertConfirmContent content) throws BaseCmpException {
@@ -284,7 +278,11 @@ private void validateCertRep(final CertRepMessage content) throws BaseCmpExcepti
validatePositivePkiStatusInfo(response.getStatus());
final CertOrEncCert certOrEncCert = certifiedKeyPair.getCertOrEncCert();
assertValueNotNull(certOrEncCert, PKIFailureInfo.badDataFormat, "CertOrEncCert");
- assertValueNotNull(certOrEncCert.getCertificate(), PKIFailureInfo.badDataFormat, "Certificate");
+ if (certOrEncCert.hasEncryptedCertificate()) {
+ assertValueNotNull(certOrEncCert.getEncryptedCert(), PKIFailureInfo.badDataFormat, "EncryptedCert");
+ } else {
+ assertValueNotNull(certOrEncCert.getCertificate(), PKIFailureInfo.badDataFormat, "Certificate");
+ }
} else {
validateNegativePkiStatusInfo(response.getStatus());
}
@@ -294,7 +292,7 @@ private void validateConfirm(final PKIConfirmContent content) {
// always ASN1Null
}
- private void validateCrmfCertReq(
+ private boolean validateCrmfCertReq(
final int enrollmentType, final CertReqMessages content, final String certProfile, final int bodyType)
throws CmpValidationException {
final CertReqMsg[] certReqMsgs = content.toCertReqMsgArray();
@@ -348,19 +346,11 @@ private void validateCrmfCertReq(
popoSigningKey.getPoposkInput(),
PKIFailureInfo.badPOP,
"PoposkInput must be absent");
- final PublicKey publicKey = KeyFactory.getInstance(
- publicKeyInfo
- .getAlgorithm()
- .getAlgorithm()
- .toString(),
- CertUtility.getBouncyCastleProvider())
- .generatePublic(new X509EncodedKeySpec(publicKeyInfo.getEncoded(ASN1Encoding.DER)));
- final Signature sig = Signature.getInstance(
- popoSigningKey
- .getAlgorithmIdentifier()
- .getAlgorithm()
- .getId(),
- CertUtility.getBouncyCastleProvider());
+ final PublicKey publicKey = CertUtility.parsePublicKey(publicKeyInfo);
+ final Signature sig = AlgorithmHelper.getSignature(popoSigningKey
+ .getAlgorithmIdentifier()
+ .getAlgorithm()
+ .getId());
sig.initVerify(publicKey);
sig.update(certReq.getEncoded(ASN1Encoding.DER));
if (!sig.verify(popoSigningKey.getSignature().getBytes())) {
@@ -370,7 +360,6 @@ private void validateCrmfCertReq(
} catch (final IOException
| NoSuchAlgorithmException
| InvalidKeyException
- | InvalidKeySpecException
| SignatureException ex) {
throw new CmpEnrollmentException(
enrollmentType,
@@ -379,11 +368,14 @@ private void validateCrmfCertReq(
"exception while calculating POPO: " + ex.getLocalizedMessage());
}
break;
+ case ProofOfPossession.TYPE_KEY_ENCIPHERMENT:
+ return true;
default:
throw new CmpEnrollmentException(
enrollmentType, interfaceName, PKIFailureInfo.badPOP, "unsupported POPO type");
}
}
+ return false;
}
private void validateErrorMsg(final ErrorMsgContent content) throws CmpValidationException {
@@ -425,12 +417,11 @@ private void validateP10CertReq(final CertificationRequest content) throws BaseC
final PKCS10CertificationRequest p10Request = new PKCS10CertificationRequest(content);
assertValueNotNull(p10Request.getSubject(), PKIFailureInfo.badCertTemplate, "Subject");
try {
- if (!p10Request.isSignatureValid(
- jcaX509ContentVerifierProviderBuilder.build(p10Request.getSubjectPublicKeyInfo()))) {
+ if (!CertUtility.validateP10Request(p10Request)) {
throw new CmpValidationException(
interfaceName, PKIFailureInfo.badPOP, "PKCS#10 signature validation failed");
}
- } catch (OperatorCreationException | PKCSException e) {
+ } catch (Exception e) {
throw new CmpValidationException(
interfaceName,
PKIFailureInfo.badPOP,
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/msgvalidation/SignatureProtectionValidator.java b/src/main/java/com/siemens/pki/cmpracomponent/msgvalidation/SignatureProtectionValidator.java
index cf899140..32b9af69 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/msgvalidation/SignatureProtectionValidator.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/msgvalidation/SignatureProtectionValidator.java
@@ -18,6 +18,7 @@
package com.siemens.pki.cmpracomponent.msgvalidation;
import com.siemens.pki.cmpracomponent.configuration.VerificationContext;
+import com.siemens.pki.cmpracomponent.cryptoservices.AlgorithmHelper;
import com.siemens.pki.cmpracomponent.cryptoservices.CertUtility;
import com.siemens.pki.cmpracomponent.cryptoservices.TrustCredentialAdapter;
import com.siemens.pki.cmpracomponent.util.MessageDumper;
@@ -67,7 +68,7 @@ private void checkProtectingSignature(
final PKIHeader header = message.getHeader();
final byte[] protectedBytes = new ProtectedPart(header, message.getBody()).getEncoded(ASN1Encoding.DER);
final byte[] protectionBytes = message.getProtection().getBytes();
- final Signature sig = Signature.getInstance(algorithm.getId(), CertUtility.getBouncyCastleProvider());
+ final Signature sig = AlgorithmHelper.getSignature(algorithm.getId());
sig.initVerify(protectingCert.getPublicKey());
sig.update(protectedBytes);
if (!sig.verify(protectionBytes, 0, protectionBytes.length)) {
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 20d192d3..833358b1 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/persistency/PersistencyContext.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/persistency/PersistencyContext.java
@@ -30,6 +30,7 @@
import org.bouncycastle.asn1.cmp.CMPCertificate;
import org.bouncycastle.asn1.cmp.PKIFailureInfo;
import org.bouncycastle.asn1.cmp.PKIMessage;
+import org.bouncycastle.operator.OperatorCreationException;
/**
* holder for all persistent data
@@ -48,7 +49,7 @@ public class PersistencyContext {
private PKIMessage pendingDelayedResponse;
private LastTransactionState lastTransactionState;
private ASN1OctetString lastSenderNonce;
- private byte[] digestToConfirm;
+ private CMPCertificate enrolledCertificate;
private boolean implicitConfirmGranted;
private byte[] requestedPublicKey;
@@ -60,6 +61,8 @@ public class PersistencyContext {
private int certificateRequestType;
+ private boolean respondedCertMustBeEncrypted;
+
/**
* ctor used by jackson
*/
@@ -75,6 +78,7 @@ public PersistencyContext() {}
this.contextManager = contextManager;
lastTransactionState = LastTransactionState.INITIAL_STATE;
this.certificateRequestType = -1;
+ this.respondedCertMustBeEncrypted = false;
}
/**
@@ -118,11 +122,11 @@ public boolean isDelayedDeliveryInProgress() {
}
/**
- * get certificate digest to confirm
+ * get certificate to confirm
* @return certificate digest or null
*/
- public byte[] getDigestToConfirm() {
- return digestToConfirm;
+ public CMPCertificate getEnrolledCertificate() {
+ return enrolledCertificate;
}
/**
@@ -240,11 +244,11 @@ public void setContextManager(final PersistencyContextManager contextManager) {
}
/**
- * set digestToConfirm
- * @param digestToConfirm the digestToConfirm
+ * set enrolledCertificate
+ * @param enrolledCertificate the enrolledCertificate
*/
- public void setDigestToConfirm(final byte[] digestToConfirm) {
- this.digestToConfirm = digestToConfirm;
+ public void setEnrolledCertficate(final CMPCertificate enrolledCertificate) {
+ this.enrolledCertificate = enrolledCertificate;
}
/**
@@ -341,8 +345,9 @@ public void setRequestType(final int certificateRequestType) {
* @param msg message to process
* @throws BaseCmpException in case of CMP relate error
* @throws IOException in case of general error
+ * @throws OperatorCreationException in case of certificate validation error
*/
- public void trackMessage(final PKIMessage msg) throws BaseCmpException, IOException {
+ public void trackMessage(final PKIMessage msg) throws BaseCmpException, IOException, OperatorCreationException {
transactionStateTracker.trackMessage(msg);
}
@@ -354,4 +359,19 @@ public void updateTransactionExpirationTime(final Date expirationTime) {
// only downstream can expire
this.expirationTime = expirationTime;
}
+
+ /**
+ * in case of indirect KEM POP the cert responded by RA must be encryptet. Enable it.
+ *
+ */
+ public void setRespondedCertMustBeEncrypted() {
+ respondedCertMustBeEncrypted = true;
+ }
+ /**
+ * in case of indirect KEM POP the cert responded by RA must be encryptet
+ * @return true
if indirect KEM POP is used for enrollment
+ */
+ public boolean isRespondedCertMustBeEncrypted() {
+ return respondedCertMustBeEncrypted;
+ }
}
diff --git a/src/main/java/com/siemens/pki/cmpracomponent/persistency/PersistencyContextManager.java b/src/main/java/com/siemens/pki/cmpracomponent/persistency/PersistencyContextManager.java
index 2edf2b4d..ec127b2f 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/persistency/PersistencyContextManager.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/persistency/PersistencyContextManager.java
@@ -27,15 +27,12 @@
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.siemens.pki.cmpracomponent.configuration.PersistencyInterface;
+import com.siemens.pki.cmpracomponent.cryptoservices.CertUtility;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
+import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.asn1.ASN1Object;
@@ -138,14 +135,10 @@ public void serialize(
}
try {
// private key obfuscation
- final Cipher c = Cipher.getInstance(KEY_WRAP_CIPHER);
+ final Cipher c = Cipher.getInstance(KEY_WRAP_CIPHER, CertUtility.getBouncyCastleProvider());
c.init(Cipher.WRAP_MODE, secretKey, iv);
jsonGenerator.writeBinary(c.wrap(key));
- } catch (NoSuchAlgorithmException
- | NoSuchPaddingException
- | InvalidKeyException
- | IllegalBlockSizeException
- | InvalidAlgorithmParameterException e) {
+ } catch (GeneralSecurityException e) {
throw new IOException(e);
}
}
@@ -168,7 +161,7 @@ public PrivateKey deserialize(final JsonParser p, final DeserializationContext c
return null;
}
try {
- final Cipher c = Cipher.getInstance(KEY_WRAP_CIPHER);
+ final Cipher c = Cipher.getInstance(KEY_WRAP_CIPHER, CertUtility.getBouncyCastleProvider());
c.init(Cipher.UNWRAP_MODE, secretKey, iv);
for (final String keyType : new String[] {"RSA", "EC", "Ed448", "Ed25519"}) {
try {
@@ -179,10 +172,7 @@ public PrivateKey deserialize(final JsonParser p, final DeserializationContext c
}
LOGGER.error("cold not load private key");
return null;
- } catch (final InvalidKeyException
- | NoSuchAlgorithmException
- | NoSuchPaddingException
- | InvalidAlgorithmParameterException e1) {
+ } catch (final GeneralSecurityException e1) {
throw new IOException(e1);
}
}
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 cdbbe603..1ffa7b0e 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/persistency/TransactionStateTracker.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/persistency/TransactionStateTracker.java
@@ -25,6 +25,7 @@
import java.util.Arrays;
import java.util.Objects;
import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.cmp.CMPCertificate;
import org.bouncycastle.asn1.cmp.CMPObjectIdentifiers;
import org.bouncycastle.asn1.cmp.CertConfirmContent;
import org.bouncycastle.asn1.cmp.CertRepMessage;
@@ -100,10 +101,7 @@ private void handleCertResponse(final PKIMessage msg) throws CmpValidationExcept
.getCertOrEncCert()
.getCertificate()
.getX509v3PKCert();
- final DigestCalculator dc =
- digestProvider.get(digestFinder.find(enrolledCertificate.getSignatureAlgorithm()));
- dc.getOutputStream().write(enrolledCertificate.getEncoded(ASN1Encoding.DER));
- persistencyContext.setDigestToConfirm(dc.getDigest());
+ persistencyContext.setEnrolledCertficate(new CMPCertificate(enrolledCertificate));
final SubjectPublicKeyInfo enrolledPublicKey = enrolledCertificate.getSubjectPublicKeyInfo();
if (!Arrays.equals(
persistencyContext.getRequestedPublicKey(), enrolledPublicKey.getEncoded(ASN1Encoding.DER))) {
@@ -296,8 +294,9 @@ private boolean isWaitingIndication(final PKIMessage msg) {
* @param message message to process
* @throws BaseCmpException in case of failed CMP processing
* @throws IOException in case of broken ASN.1
+ * @throws OperatorCreationException
*/
- public void trackMessage(final PKIMessage message) throws BaseCmpException, IOException {
+ public void trackMessage(final PKIMessage message) throws BaseCmpException, IOException, OperatorCreationException {
if (isResponse(message)) {
persistencyContext.setLastSenderNonce(message.getHeader().getSenderNonce());
}
@@ -407,12 +406,19 @@ public void trackMessage(final PKIMessage message) throws BaseCmpException, IOEx
"response was not answered with confirmation for "
+ MessageDumper.msgAsShortString(message));
}
- if (!Arrays.equals(
- persistencyContext.getDigestToConfirm(),
- ((CertConfirmContent) message.getBody().getContent())
- .toCertStatusArray()[0]
- .getCertHash()
- .getOctets())) {
+ final CertStatus certStatus =
+ ((CertConfirmContent) message.getBody().getContent()).toCertStatusArray()[0];
+ final CMPCertificate enrolledCertificate = persistencyContext.getEnrolledCertificate();
+ final DigestCalculator dc;
+ if (certStatus.getHashAlg() != null) {
+ dc = digestProvider.get(certStatus.getHashAlg());
+ } else {
+ dc = digestProvider.get(digestFinder.find(
+ enrolledCertificate.getX509v3PKCert().getSignatureAlgorithm()));
+ }
+ dc.getOutputStream().write(enrolledCertificate.getEncoded(ASN1Encoding.DER));
+
+ if (!Arrays.equals(dc.getDigest(), certStatus.getCertHash().getOctets())) {
persistencyContext.setLastTransactionState(LastTransactionState.IN_ERROR_STATE);
throw new CmpValidationException(
INTERFACE_NAME,
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 e2d2bef9..78d7b0ff 100644
--- a/src/main/java/com/siemens/pki/cmpracomponent/protection/SignatureBasedProtection.java
+++ b/src/main/java/com/siemens/pki/cmpracomponent/protection/SignatureBasedProtection.java
@@ -18,6 +18,7 @@
package com.siemens.pki.cmpracomponent.protection;
import com.siemens.pki.cmpracomponent.configuration.SignatureCredentialContext;
+import com.siemens.pki.cmpracomponent.cryptoservices.AlgorithmHelper;
import com.siemens.pki.cmpracomponent.cryptoservices.BaseCredentialService;
import com.siemens.pki.cmpracomponent.cryptoservices.CertUtility;
import java.io.IOException;
@@ -80,7 +81,7 @@ public AlgorithmIdentifier getProtectionAlg() {
@Override
public DERBitString getProtectionFor(final ProtectedPart protectedPart)
throws GeneralSecurityException, IOException {
- final Signature sig = Signature.getInstance(getSignatureAlgorithmName());
+ final Signature sig = AlgorithmHelper.getSignature(getSignatureAlgorithmName());
sig.initSign(getPrivateKey());
sig.update(protectedPart.getEncoded(ASN1Encoding.DER));
return new DERBitString(sig.sign());
diff --git a/src/test/java/com/siemens/pki/cmpclientcomponent/test/CmpClientTestcaseBase.java b/src/test/java/com/siemens/pki/cmpclientcomponent/test/CmpClientTestcaseBase.java
index 69bc3320..69087179 100644
--- a/src/test/java/com/siemens/pki/cmpclientcomponent/test/CmpClientTestcaseBase.java
+++ b/src/test/java/com/siemens/pki/cmpclientcomponent/test/CmpClientTestcaseBase.java
@@ -28,7 +28,6 @@
import com.siemens.pki.cmpracomponent.configuration.NestedEndpointContext;
import com.siemens.pki.cmpracomponent.configuration.SharedSecretCredentialContext;
import com.siemens.pki.cmpracomponent.configuration.VerificationContext;
-import com.siemens.pki.cmpracomponent.cryptoservices.CertUtility;
import com.siemens.pki.cmpracomponent.main.CmpRaComponent;
import com.siemens.pki.cmpracomponent.main.CmpRaComponent.CmpRaInterface;
import com.siemens.pki.cmpracomponent.main.CmpRaComponent.UpstreamExchange;
@@ -37,6 +36,7 @@
import com.siemens.pki.cmpracomponent.test.framework.ConfigurationFactory;
import com.siemens.pki.cmpracomponent.test.framework.PasswordValidationCredentials;
import com.siemens.pki.cmpracomponent.test.framework.SignatureValidationCredentials;
+import com.siemens.pki.cmpracomponent.test.framework.TrustChainAndPrivateKey;
import java.io.File;
import java.io.IOException;
import java.security.Security;
@@ -44,7 +44,8 @@
import java.util.Collection;
import java.util.function.BiFunction;
import java.util.function.Function;
-import org.junit.BeforeClass;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
public class CmpClientTestcaseBase {
@@ -52,6 +53,8 @@ public class CmpClientTestcaseBase {
static {
ConfigFileLoader.setConfigFileBase(CONFIG_DIRECTORY);
+ Security.addProvider(new BouncyCastleProvider());
+ Security.addProvider(new BouncyCastlePQCProvider());
}
protected static CmpMessageInterface getPasswordBasedUpstreamconfiguration(
@@ -161,11 +164,6 @@ public boolean isMessageTimeDeviationAllowed(final long deviation) {
};
}
- @BeforeClass
- public static void setUpBeforeClass() throws Exception {
- Security.addProvider(CertUtility.getBouncyCastleProvider());
- }
-
protected UpstreamExchange upstreamExchange;
protected CmpClient getPasswordBasedCmpClient(
@@ -194,13 +192,19 @@ protected UpstreamExchange getUpstreamExchange() {
return upstreamExchange;
}
- protected UpstreamExchange launchCmpCaAndRa(final Configuration config) throws Exception {
+ protected UpstreamExchange launchCmpCaAndRa(final Configuration raConfig) throws Exception {
return launchCmpRa(
- config,
+ raConfig,
new CmpCaMock("credentials/ENROLL_Keystore.p12", "credentials/CMP_CA_Keystore.p12")
::sendReceiveMessage);
}
+ protected UpstreamExchange launchCmpCaAndRa(
+ TrustChainAndPrivateKey enrollmentCredential, final Configuration raConfig) throws Exception {
+ return launchCmpRa(
+ raConfig, new CmpCaMock(enrollmentCredential, "credentials/CMP_CA_Keystore.p12")::sendReceiveMessage);
+ }
+
protected UpstreamExchange launchCmpCaAndRaAndLra(final Configuration raConfig, final Configuration lraConfig)
throws Exception {
final UpstreamExchange ra = launchCmpCaAndRa(raConfig);
diff --git a/src/test/java/com/siemens/pki/cmpclientcomponent/test/SignatureBasedCrWithAllKeyTypesBase.java b/src/test/java/com/siemens/pki/cmpclientcomponent/test/SignatureBasedCrWithAllKeyTypesBase.java
new file mode 100644
index 00000000..bfac91c0
--- /dev/null
+++ b/src/test/java/com/siemens/pki/cmpclientcomponent/test/SignatureBasedCrWithAllKeyTypesBase.java
@@ -0,0 +1,305 @@
+/*
+ * 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.ClientContext;
+import com.siemens.pki.cmpclientcomponent.configuration.EnrollmentContext;
+import com.siemens.pki.cmpclientcomponent.configuration.RevocationContext;
+import com.siemens.pki.cmpclientcomponent.main.CmpClient;
+import com.siemens.pki.cmpclientcomponent.main.CmpClient.EnrollmentResult;
+import com.siemens.pki.cmpracomponent.configuration.CkgContext;
+import com.siemens.pki.cmpracomponent.configuration.CmpMessageInterface;
+import com.siemens.pki.cmpracomponent.configuration.Configuration;
+import com.siemens.pki.cmpracomponent.configuration.CredentialContext;
+import com.siemens.pki.cmpracomponent.configuration.InventoryInterface;
+import com.siemens.pki.cmpracomponent.configuration.NestedEndpointContext;
+import com.siemens.pki.cmpracomponent.configuration.SupportMessageHandlerInterface;
+import com.siemens.pki.cmpracomponent.configuration.VerificationContext;
+import com.siemens.pki.cmpracomponent.cryptoservices.KeyPairGeneratorFactory;
+import com.siemens.pki.cmpracomponent.test.framework.ConfigurationFactory;
+import com.siemens.pki.cmpracomponent.test.framework.TrustChainAndPrivateKey;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.cert.X509Certificate;
+import java.util.List;
+import org.bouncycastle.asn1.cmp.PKIBody;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * use protection chains with different keytypes
+ */
+class SignatureBasedCrWithAllKeyTypesBase extends EnrollmentTestcaseBase {
+
+ protected SignatureBasedCrWithAllKeyTypesBase(
+ TrustChainAndPrivateKey fromClientToRa,
+ TrustChainAndPrivateKey fromRaToClient,
+ TrustChainAndPrivateKey enrollmentCredentials) {
+ this.fromClientToRa = fromClientToRa;
+ this.fromRaToClient = fromRaToClient;
+ this.enrollmentCredentials = enrollmentCredentials;
+ }
+
+ private final TrustChainAndPrivateKey fromClientToRa;
+ private final TrustChainAndPrivateKey fromRaToClient;
+ private final TrustChainAndPrivateKey enrollmentCredentials;
+
+ @Before
+ public void setUp() throws Exception {
+ launchCmpCaAndRa(enrollmentCredentials, new Configuration() {
+
+ @Override
+ public boolean isRaVerifiedAcceptable(String certProfile, int bodyType) {
+ return false;
+ }
+
+ @Override
+ public CmpMessageInterface getUpstreamConfiguration(String certProfile, int bodyType) {
+ return new CmpMessageInterface() {
+
+ @Override
+ public boolean isMessageTimeDeviationAllowed(long deviation) {
+ return Math.abs(deviation) < 100;
+ }
+
+ @Override
+ public boolean isCacheExtraCerts() {
+ return false;
+ }
+
+ @Override
+ public boolean getSuppressRedundantExtraCerts() {
+ return false;
+ }
+
+ @Override
+ public ReprotectMode getReprotectMode() {
+ return ReprotectMode.strip;
+ }
+
+ @Override
+ public CredentialContext getOutputCredentials() {
+ return null;
+ }
+
+ @Override
+ public NestedEndpointContext getNestedEndpointContext() {
+ return null;
+ }
+
+ @Override
+ public VerificationContext getInputVerification() {
+ return null;
+ }
+ };
+ }
+
+ @Override
+ public SupportMessageHandlerInterface getSupportMessageHandler(String certProfile, String infoTypeOid) {
+ return null;
+ }
+
+ @Override
+ public int getRetryAfterTimeInSeconds(String certProfile, int bodyType) {
+ return 0;
+ }
+
+ @Override
+ public InventoryInterface getInventory(String certProfile, int bodyType) {
+ return null;
+ }
+
+ @Override
+ public boolean getForceRaVerifyOnUpstream(String certProfile, int bodyType) {
+ return false;
+ }
+
+ @Override
+ public VerificationContext getEnrollmentTrust(String certProfile, int bodyType) {
+ return enrollmentCredentials;
+ }
+
+ @Override
+ public int getDownstreamTimeout(String certProfile, int bodyType) {
+ return 0;
+ }
+
+ @Override
+ public CmpMessageInterface getDownstreamConfiguration(String certProfile, int bodyType) {
+ return ConfigurationFactory.createSignatureBasedCmpMessageInterface(fromRaToClient, fromClientToRa);
+ }
+
+ @Override
+ public CkgContext getCkgConfiguration(String certProfile, int bodyType) {
+ return null;
+ }
+ });
+ }
+
+ @Test
+ public void testCr() throws Exception {
+ final EnrollmentResult ret = getCmpClient(
+ "theCertProfileForOnlineEnrollment",
+ new ClientContext() {
+
+ @Override
+ public EnrollmentContext getEnrollmentContext() {
+ return new EnrollmentContext() {
+
+ @Override
+ public KeyPair getCertificateKeypair() {
+ return ConfigurationFactory.getKeyGenerator()
+ .generateKeyPair();
+ }
+
+ @Override
+ public byte[] getCertificationRequest() {
+ return null;
+ }
+
+ @Override
+ public VerificationContext getEnrollmentTrust() {
+ return enrollmentCredentials;
+ }
+
+ @Override
+ public int getEnrollmentType() {
+ return PKIBody.TYPE_CERT_REQ;
+ }
+
+ @Override
+ public List getExtensions() {
+ return null;
+ }
+
+ @Override
+ public X509Certificate getOldCert() {
+ return null;
+ }
+
+ @Override
+ public boolean getRequestImplictConfirm() {
+ return false;
+ }
+
+ @Override
+ public String getSubject() {
+ return "CN=Subject";
+ }
+ };
+ }
+
+ @Override
+ public RevocationContext getRevocationContext() {
+ fail("getRevocationContext");
+ return null;
+ }
+ },
+ ConfigurationFactory.createSignatureBasedCmpMessageInterface(fromClientToRa, fromRaToClient))
+ .invokeEnrollment();
+ assertNotNull(ret);
+ }
+
+ @Test
+ public void testKemCr() throws Exception {
+ final EnrollmentResult ret = getCmpClient(
+ "theCertProfileForOnlineEnrollment",
+ new ClientContext() {
+
+ @Override
+ public EnrollmentContext getEnrollmentContext() {
+ return new EnrollmentContext() {
+
+ @Override
+ public KeyPair getCertificateKeypair() {
+ try {
+ return KeyPairGeneratorFactory.getGenericKeyPairGenerator(
+ NISTObjectIdentifiers.id_alg_ml_kem_512)
+ .generateKeyPair();
+ } catch (GeneralSecurityException e) {
+ fail(e.getLocalizedMessage());
+ return null;
+ }
+ }
+
+ @Override
+ public byte[] getCertificationRequest() {
+ return null;
+ }
+
+ @Override
+ public VerificationContext getEnrollmentTrust() {
+ return enrollmentCredentials;
+ }
+
+ @Override
+ public int getEnrollmentType() {
+ return PKIBody.TYPE_CERT_REQ;
+ }
+
+ @Override
+ public List getExtensions() {
+ return null;
+ }
+
+ @Override
+ public X509Certificate getOldCert() {
+ return null;
+ }
+
+ @Override
+ public boolean getRequestImplictConfirm() {
+ return false;
+ }
+
+ @Override
+ public String getSubject() {
+ return "CN=Subject";
+ }
+ };
+ }
+
+ @Override
+ public RevocationContext getRevocationContext() {
+ fail("getRevocationContext");
+ return null;
+ }
+ },
+ ConfigurationFactory.createSignatureBasedCmpMessageInterface(fromClientToRa, fromRaToClient))
+ .invokeEnrollment();
+ assertNotNull(ret);
+ }
+
+ protected CmpClient getSignatureBasedCmpClient(
+ String certProfile, final ClientContext clientContext, final String upstreamTrustPath) throws Exception {
+ return new CmpClient(
+ certProfile,
+ getUpstreamExchange(),
+ getSignatureBasedUpstreamconfiguration(upstreamTrustPath),
+ clientContext);
+ }
+
+ private CmpClient getCmpClient(String string, ClientContext clientContext, CmpMessageInterface upstreamInterface)
+ throws Exception {
+ return new CmpClient(null, getUpstreamExchange(), upstreamInterface, clientContext);
+ }
+}
diff --git a/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestEnrollmentForAllKeyTypes.java b/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestEnrollmentForAllKeyTypes.java
new file mode 100644
index 00000000..8b9692e8
--- /dev/null
+++ b/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestEnrollmentForAllKeyTypes.java
@@ -0,0 +1,74 @@
+/*
+ * 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 com.siemens.pki.cmpclientcomponent.main.CmpClient.EnrollmentResult;
+import com.siemens.pki.cmpracomponent.test.framework.ConfigurationFactory;
+import com.siemens.pki.cmpracomponent.test.framework.TcAlgs;
+import java.security.GeneralSecurityException;
+import java.security.KeyPairGenerator;
+import java.util.ArrayList;
+import java.util.List;
+import org.bouncycastle.asn1.cmp.PKIBody;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * enroll certificates of different keytypes
+ */
+@RunWith(Parameterized.class)
+public class TestEnrollmentForAllKeyTypes extends EnrollmentTestcaseBase {
+
+ private static final String UPSTREAM_TRUST_PATH = "credentials/CMP_CA_and_LRA_DOWNSTREAM_Root.pem";
+
+ @Parameters(name = "{0}")
+ public static Iterable