From dee91b5c7aef5e13e607490d770f308cdb53304c Mon Sep 17 00:00:00 2001 From: Andreas Kretschmer Date: Tue, 9 Jul 2024 10:29:16 +0200 Subject: [PATCH] enable PQ certs for enrollment and protection Adapt to NIST 203..205 align to BC 1.79 --- CHANGELOG.md | 2 +- pom.xml | 23 +- .../main/ClientRequestHandler.java | 2 +- .../cmpclientcomponent/main/CmpClient.java | 42 ++- .../SignatureCredentialContext.java | 11 + .../cryptoservices/AlgorithmHelper.java | 40 ++- .../cryptoservices/CertUtility.java | 58 +++- .../cryptoservices/CmsDecryptor.java | 52 ++- .../cryptoservices/CmsEncryptorBase.java | 51 ++- .../cryptoservices/DataSignVerifier.java | 46 ++- .../cryptoservices/DataSigner.java | 19 +- .../cryptoservices/KeyAgreementEncryptor.java | 7 +- .../KeyPairGeneratorFactory.java | 25 ++ .../cryptoservices/KeyTransportEncryptor.java | 2 +- .../cryptoservices/PasswordEncryptor.java | 10 +- .../TrustCredentialAdapter.java | 42 ++- .../msggeneration/MsgOutputProtector.java | 1 + .../msggeneration/PkiMessageGenerator.java | 111 ++++++- .../msgprocessing/RaDownstream.java | 59 ++-- .../msgvalidation/InputValidator.java | 5 +- .../msgvalidation/MessageBodyValidator.java | 55 ++-- .../SignatureProtectionValidator.java | 3 +- .../persistency/PersistencyContext.java | 38 ++- .../PersistencyContextManager.java | 22 +- .../persistency/TransactionStateTracker.java | 28 +- .../protection/SignatureBasedProtection.java | 3 +- .../test/CmpClientTestcaseBase.java | 22 +- .../SignatureBasedCrWithAllKeyTypesBase.java | 296 ++++++++++++++++++ .../test/TestEnrollmentForAllKeyTypes.java | 74 +++++ ...tSignatureBasedCrWithAllKeyTypesMixed.java | 80 +++++ ...SignatureBasedCrWithAllKeyTypesUnique.java | 50 +++ .../cmpracomponent/test/CmpTestcaseBase.java | 3 - .../test/DelayedDeliveryTestcaseBase.java | 3 - .../test/TestSupportMessages.java | 8 +- .../test/framework/CmpCaMock.java | 55 ++-- .../test/framework/ConfigurationFactory.java | 57 +++- .../cmpracomponent/test/framework/TcAlgs.java | 146 +++++++++ .../test/framework/TestCertUtility.java | 54 ++-- .../framework/TestCertificateFactory.java | 11 +- .../framework/TrustChainAndPrivateKey.java | 205 +++++++++++- 40 files changed, 1493 insertions(+), 328 deletions(-) create mode 100644 src/test/java/com/siemens/pki/cmpclientcomponent/test/SignatureBasedCrWithAllKeyTypesBase.java create mode 100644 src/test/java/com/siemens/pki/cmpclientcomponent/test/TestEnrollmentForAllKeyTypes.java create mode 100644 src/test/java/com/siemens/pki/cmpclientcomponent/test/TestSignatureBasedCrWithAllKeyTypesMixed.java create mode 100644 src/test/java/com/siemens/pki/cmpclientcomponent/test/TestSignatureBasedCrWithAllKeyTypesUnique.java create mode 100644 src/test/java/com/siemens/pki/cmpracomponent/test/framework/TcAlgs.java 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..7aa595de 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,11 @@ 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.CMSException; +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 +145,7 @@ public interface EnrollmentResult { /** * ctor + * * @param certProfile certificate profile to be used for enrollment. * null if no certificate profile * should be used. @@ -299,12 +305,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 +502,31 @@ 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()) { + try { + byte[] content = recipient.getContent(jkr); + enrolledCertificate = CMPCertificate.getInstance(content); + break; + } catch (CMSException ex) { + // try next recipient + } + } + } else { + enrolledCertificate = certOrEncCert.getCertificate(); + } + if (enrolledCertificate == null) { + LOGGER.error("could not extract enrolled certificate from response"); + return null; + } 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..397514e9 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,17 @@ 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..3cfb13ff 100644 --- a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/AlgorithmHelper.java +++ b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/AlgorithmHelper.java @@ -22,6 +22,7 @@ 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; @@ -245,6 +246,7 @@ String[] extractAliases(final ASN1ObjectIdentifier cmpId) { /** * convert shared secrets from byte[] to char[] + * * @param sharedSecret sharedSecret as byte[] * @return sharedSecret as char[] */ @@ -261,6 +263,7 @@ public static char[] convertSharedSecretToPassword(final byte[] sharedSecret) { /** * get AlgorithmIdentifier for MessageDigest + * * @param dig digest * @return AlgorithmIdentifier */ @@ -295,6 +298,7 @@ public static AlgorithmIdentifier getAlgOID(final String algorithm) { /** * get OID for name of KEK algorithm + * * @param id name of KEK algorithm * @return KEK OID * @throws NoSuchAlgorithmException if id is unknown @@ -305,6 +309,7 @@ public static ASN1ObjectIdentifier getKekOID(final String id) throws NoSuchAlgor /** * 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 +320,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 +330,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 +340,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 +364,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 +378,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 +405,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 +415,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 +452,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 +479,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..a49b0e24 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,10 @@ 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.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -41,12 +46,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 +182,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 +233,39 @@ 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 + * @throws InvalidKeySpecException if the given key specificationis inappropriate for this key factory to produce a public key. + */ + public static PublicKey parsePublicKey(final SubjectPublicKeyInfo subjectPublicKeyInfo) + throws NoSuchAlgorithmException, IOException, InvalidKeySpecException { + return KeyFactory.getInstance( + subjectPublicKeyInfo.getAlgorithm().getAlgorithm().toString(), + CertUtility.getBouncyCastleProvider()) + .generatePublic(new X509EncodedKeySpec(subjectPublicKeyInfo.getEncoded(ASN1Encoding.DER))); + } + + /** + * 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 { + return p10Request.isSignatureValid(new JcaX509ContentVerifierProviderBuilder() + .setProvider(CertUtility.getBouncyCastleProvider()) + .build(p10Request.getSubjectPublicKeyInfo())); + } + private static class BouncyCastleInitializer { private static synchronized Provider getInstance() { return Arrays.stream(Security.getProviders()) @@ -218,6 +274,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..4463085a 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,24 +83,26 @@ 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 (recipient != null) { - return recipient.getContent(passwordRecipient); - } + RecipientInformation recipient = recipients.get(passRecipientId); + if (recipient != null) { + 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..0a677fcf 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,10 @@ 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.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 +31,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 +40,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 +81,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 +99,7 @@ private static byte[] verifySignature( /** * ctor + * * @param config context used for verification * @param interfaceName CMP interface name for logging */ @@ -107,7 +108,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, NoSuchProviderException, + CertificateException { return validateCertAgainstTrust(CertUtility.asX509Certificate(cert.getEncoded()), allCerts) != null; } @@ -133,11 +135,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 +148,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..e93c5bdb 100644 --- a/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/DataSigner.java +++ b/src/main/java/com/siemens/pki/cmpracomponent/cryptoservices/DataSigner.java @@ -51,15 +51,17 @@ 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() + SignerInfoGenerator signerInfoGenerator; + signerInfoGenerator = new JcaSimpleSignerInfoGeneratorBuilder() .setProvider(CertUtility.getBouncyCastleProvider()) .build( credentialService.getSignatureAlgorithmName(), @@ -79,13 +81,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 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 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..3e60d689 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,10 @@ 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..b793de63 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,18 @@ 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 +69,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 +84,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 +94,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 +178,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 +206,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 +258,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 +279,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 +293,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 +347,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())) { @@ -379,11 +370,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 +419,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..a7931633 --- /dev/null +++ b/src/test/java/com/siemens/pki/cmpclientcomponent/test/SignatureBasedCrWithAllKeyTypesBase.java @@ -0,0 +1,296 @@ +/* + * 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); + } + + private CmpClient getCmpClient( + String certProfile, ClientContext clientContext, CmpMessageInterface upstreamInterface) throws Exception { + return new CmpClient(certProfile, 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 data() throws GeneralSecurityException { + List ret = new ArrayList(); + ret.addAll(TcAlgs.getKemAlgorithms()); + ret.addAll(TcAlgs.getCompositeAlgorithms()); + ret.addAll(TcAlgs.getPqSignatureAlgorithms()); + ret.addAll(TcAlgs.getClassicSignatureAlgorithms()); + return ret; + } + + private KeyPairGenerator kp; + + public TestEnrollmentForAllKeyTypes(String description, KeyPairGenerator kp) { + this.kp = kp; + } + + @Before + public void setUp() throws Exception { + launchCmpCaAndRa(ConfigurationFactory.buildSignatureBasedDownstreamConfiguration()); + } + + @Test + public void testCr() throws Exception { + final EnrollmentResult ret = getSignatureBasedCmpClient( + "theCertProfileForOnlineEnrollment", + getClientContext(PKIBody.TYPE_CERT_REQ, kp.generateKeyPair(), null), + UPSTREAM_TRUST_PATH) + .invokeEnrollment(); + assertNotNull(ret); + } +} diff --git a/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestSignatureBasedCrWithAllKeyTypesMixed.java b/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestSignatureBasedCrWithAllKeyTypesMixed.java new file mode 100644 index 00000000..81a609c9 --- /dev/null +++ b/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestSignatureBasedCrWithAllKeyTypesMixed.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024 Siemens AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package com.siemens.pki.cmpclientcomponent.test; + +import com.siemens.pki.cmpracomponent.cryptoservices.KeyPairGeneratorFactory; +import com.siemens.pki.cmpracomponent.test.framework.TrustChainAndPrivateKey; +import java.security.GeneralSecurityException; +import java.security.KeyPairGenerator; +import java.util.Arrays; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.misc.MiscObjectIdentifiers; +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * use protection chains with mixed keytypes + */ +@RunWith(Parameterized.class) +public class TestSignatureBasedCrWithAllKeyTypesMixed extends SignatureBasedCrWithAllKeyTypesBase { + + @Parameters(name = "{0}") + public static Iterable data() throws GeneralSecurityException { + KeyPairGenerator pqKpg = KeyPairGeneratorFactory.getGenericKeyPairGenerator( + NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128f_with_sha256); + KeyPairGenerator rsaKpg = KeyPairGeneratorFactory.getRsaKeyPairGenerator(1024); + KeyPairGenerator ecKpg = KeyPairGeneratorFactory.getEcKeyPairGenerator("secp256r1"); + KeyPairGenerator edKpg = KeyPairGeneratorFactory.getEdDsaKeyPairGenerator("Ed448"); + KeyPairGenerator compKpg = + KeyPairGeneratorFactory.getGenericKeyPairGenerator(MiscObjectIdentifiers.id_MLDSA44_ECDSA_P256_SHA256); + + return Arrays.asList(new Object[][] { + // + {"EC-COMP-RSA", null, new KeyPairGenerator[] {ecKpg, compKpg, rsaKpg}}, + {"EC-ED-COMP", null, new KeyPairGenerator[] {ecKpg, edKpg, compKpg}}, + {"PQ-RSA-EC", null, new KeyPairGenerator[] {pqKpg, rsaKpg, ecKpg}}, + {"RSA-PQ-EC", null, new KeyPairGenerator[] {rsaKpg, pqKpg, ecKpg}}, + {"ED-PQ-EC", null, new KeyPairGenerator[] {edKpg, pqKpg, ecKpg}}, + { + "PQ-RSA-EC (Alt: SLH-DSA)", + NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128f_with_sha256, + new KeyPairGenerator[] {pqKpg, rsaKpg, ecKpg} + }, + { + "RSA-PQ-EC (Alt: ML-DSA)", + NISTObjectIdentifiers.id_ml_dsa_87, + new KeyPairGenerator[] {rsaKpg, pqKpg, ecKpg} + }, + { + "ED-PQ-EC (Alt: COMP)", + MiscObjectIdentifiers.id_MLDSA44_ECDSA_P256_SHA256, + new KeyPairGenerator[] {edKpg, pqKpg, ecKpg} + }, + }); + } + + public TestSignatureBasedCrWithAllKeyTypesMixed( + String description, ASN1ObjectIdentifier altSigningAlg, KeyPairGenerator... kps) throws Exception { + super( + new TrustChainAndPrivateKey("CLIENT", false, altSigningAlg, kps), + new TrustChainAndPrivateKey("RA_DOWN", false, altSigningAlg, kps), + new TrustChainAndPrivateKey("ENROLL_", true, altSigningAlg, kps)); + } +} diff --git a/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestSignatureBasedCrWithAllKeyTypesUnique.java b/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestSignatureBasedCrWithAllKeyTypesUnique.java new file mode 100644 index 00000000..98503f81 --- /dev/null +++ b/src/test/java/com/siemens/pki/cmpclientcomponent/test/TestSignatureBasedCrWithAllKeyTypesUnique.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024 Siemens AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package com.siemens.pki.cmpclientcomponent.test; + +import com.siemens.pki.cmpracomponent.test.framework.TcAlgs; +import com.siemens.pki.cmpracomponent.test.framework.TrustChainAndPrivateKey; +import java.security.GeneralSecurityException; +import java.security.KeyPairGenerator; +import java.util.ArrayList; +import java.util.List; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * use protection chains with unique keytype + */ +@RunWith(Parameterized.class) +public class TestSignatureBasedCrWithAllKeyTypesUnique extends SignatureBasedCrWithAllKeyTypesBase { + public TestSignatureBasedCrWithAllKeyTypesUnique(String description, KeyPairGenerator kp) throws Exception { + super( + new TrustChainAndPrivateKey("CLIENT", false, null, kp, kp, kp), + new TrustChainAndPrivateKey("RA_DOWN", false, null, kp, kp, kp), + new TrustChainAndPrivateKey("ENROLL", true, null, kp, kp, kp)); + } + + @Parameters(name = "{0}") + public static Iterable data() throws GeneralSecurityException { + List ret = new ArrayList(); + ret.addAll(TcAlgs.getCompositeAlgorithms()); + ret.addAll(TcAlgs.getPqSignatureAlgorithms()); + ret.addAll(TcAlgs.getClassicSignatureAlgorithms()); + return ret; + } +} diff --git a/src/test/java/com/siemens/pki/cmpracomponent/test/CmpTestcaseBase.java b/src/test/java/com/siemens/pki/cmpracomponent/test/CmpTestcaseBase.java index 2a368ba7..07d25e16 100644 --- a/src/test/java/com/siemens/pki/cmpracomponent/test/CmpTestcaseBase.java +++ b/src/test/java/com/siemens/pki/cmpracomponent/test/CmpTestcaseBase.java @@ -20,7 +20,6 @@ import static org.junit.Assert.fail; import com.siemens.pki.cmpracomponent.configuration.Configuration; -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; @@ -28,7 +27,6 @@ import com.siemens.pki.cmpracomponent.test.framework.ConfigFileLoader; import java.io.File; import java.io.IOException; -import java.security.Security; import java.util.function.BiFunction; import java.util.function.Function; import org.bouncycastle.asn1.cmp.PKIMessage; @@ -41,7 +39,6 @@ public class CmpTestcaseBase { @BeforeClass public static void setUpBeforeClass() throws Exception { - Security.addProvider(CertUtility.getBouncyCastleProvider()); ConfigFileLoader.setConfigFileBase(CONFIG_DIRECTORY); } diff --git a/src/test/java/com/siemens/pki/cmpracomponent/test/DelayedDeliveryTestcaseBase.java b/src/test/java/com/siemens/pki/cmpracomponent/test/DelayedDeliveryTestcaseBase.java index e6a5ba60..6463b370 100644 --- a/src/test/java/com/siemens/pki/cmpracomponent/test/DelayedDeliveryTestcaseBase.java +++ b/src/test/java/com/siemens/pki/cmpracomponent/test/DelayedDeliveryTestcaseBase.java @@ -21,7 +21,6 @@ import static org.junit.Assert.fail; import com.siemens.pki.cmpracomponent.configuration.Configuration; -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; @@ -32,7 +31,6 @@ import com.siemens.pki.cmpracomponent.test.framework.HeaderProviderForTest; import com.siemens.pki.cmpracomponent.util.MessageDumper; import java.io.File; -import java.security.Security; import java.util.Timer; import java.util.TimerTask; import java.util.function.Function; @@ -52,7 +50,6 @@ public class DelayedDeliveryTestcaseBase { @BeforeClass public static void setUpBeforeClass() throws Exception { - Security.addProvider(CertUtility.getBouncyCastleProvider()); ConfigFileLoader.setConfigFileBase(CONFIG_DIRECTORY); } diff --git a/src/test/java/com/siemens/pki/cmpracomponent/test/TestSupportMessages.java b/src/test/java/com/siemens/pki/cmpracomponent/test/TestSupportMessages.java index f3a3987c..3b4daf58 100644 --- a/src/test/java/com/siemens/pki/cmpracomponent/test/TestSupportMessages.java +++ b/src/test/java/com/siemens/pki/cmpracomponent/test/TestSupportMessages.java @@ -19,14 +19,13 @@ import static org.junit.Assert.*; +import com.siemens.pki.cmpracomponent.cryptoservices.CertUtility; import com.siemens.pki.cmpracomponent.msggeneration.PkiMessageGenerator; import com.siemens.pki.cmpracomponent.test.framework.ConfigurationFactory; import com.siemens.pki.cmpracomponent.test.framework.HeaderProviderForTest; import com.siemens.pki.cmpracomponent.test.framework.TestCertUtility; import com.siemens.pki.cmpracomponent.util.MessageDumper; -import java.io.ByteArrayInputStream; import java.security.cert.CRL; -import java.security.cert.CertificateFactory; import java.util.Date; import java.util.function.Function; import org.bouncycastle.asn1.ASN1Integer; @@ -90,9 +89,8 @@ null, new GeneralNames(new GeneralName(new X500Name("CN=distributionPoint")))), assertEquals("crlsOid", crlsOid, itav[0].getInfoType()); final ASN1Sequence sequenceOfCrl = (ASN1Sequence) itav[0].getInfoValue().toASN1Primitive(); - final CRL crl = CertificateFactory.getInstance("X.509") - .generateCRL(new ByteArrayInputStream( - sequenceOfCrl.getObjectAt(0).toASN1Primitive().getEncoded())); + final CRL crl = CertUtility.parseCrl( + sequenceOfCrl.getObjectAt(0).toASN1Primitive().getEncoded()); assertNotNull("CRL", crl); } diff --git a/src/test/java/com/siemens/pki/cmpracomponent/test/framework/CmpCaMock.java b/src/test/java/com/siemens/pki/cmpracomponent/test/framework/CmpCaMock.java index d487165e..ca1520bf 100644 --- a/src/test/java/com/siemens/pki/cmpracomponent/test/framework/CmpCaMock.java +++ b/src/test/java/com/siemens/pki/cmpracomponent/test/framework/CmpCaMock.java @@ -29,9 +29,8 @@ import com.siemens.pki.cmpracomponent.protection.SignatureBasedProtection; import com.siemens.pki.cmpracomponent.util.MessageDumper; import java.math.BigInteger; -import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; import java.security.PublicKey; -import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; @@ -58,12 +57,10 @@ import org.bouncycastle.cert.CertIOException; import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.cert.jcajce.JcaX509ContentVerifierProviderBuilder; import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; -import org.bouncycastle.openssl.PEMException; import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; -import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.pkcs.PKCS10CertificationRequest; import org.slf4j.Logger; @@ -74,11 +71,7 @@ */ public class CmpCaMock implements CmpRaComponent.UpstreamExchange { - private static final String INTERFACE_NAME = "CA Mock"; private static final Logger LOGGER = LoggerFactory.getLogger(CmpCaMock.class); - private static final JcaX509ContentVerifierProviderBuilder X509_CVPB = - new JcaX509ContentVerifierProviderBuilder().setProvider(CertUtility.getBouncyCastleProvider()); - private static final JcaPEMKeyConverter JCA_KEY_CONVERTER = new JcaPEMKeyConverter(); private static final int MAX_LAST_RECEIVED = 10; private final LinkedList lastReceivedMessages = new LinkedList<>(); @@ -91,7 +84,14 @@ public CmpCaMock(final String enrollmentCredentials, final String protectionCred this.enrollmentCredentials = new TrustChainAndPrivateKey(enrollmentCredentials, TestUtils.PASSWORD_AS_CHAR_ARRAY); caProtectionProvider = new SignatureBasedProtection( - new TrustChainAndPrivateKey(protectionCredentials, TestUtils.PASSWORD_AS_CHAR_ARRAY), INTERFACE_NAME); + new TrustChainAndPrivateKey(protectionCredentials, TestUtils.PASSWORD_AS_CHAR_ARRAY), "CMP TEST CA"); + } + + public CmpCaMock(TrustChainAndPrivateKey enrollmentCredentials, final String protectionCredentials) + throws Exception { + this.enrollmentCredentials = enrollmentCredentials; + caProtectionProvider = new SignatureBasedProtection( + new TrustChainAndPrivateKey(protectionCredentials, TestUtils.PASSWORD_AS_CHAR_ARRAY), "CMP TEST CA"); } private CMPCertificate createCertificate( @@ -99,10 +99,11 @@ private CMPCertificate createCertificate( final SubjectPublicKeyInfo publicKey, final X509Certificate issuingCert, Extensions extensionsFromTemplate) - throws PEMException, NoSuchAlgorithmException, CertIOException, CertificateException, - OperatorCreationException { + throws Exception { final long now = System.currentTimeMillis(); - final PublicKey pubKey = JCA_KEY_CONVERTER.getPublicKey(publicKey); + final PublicKey pubKey = new JcaPEMKeyConverter() + .setProvider(CertUtility.getBouncyCastleProvider()) + .getPublicKey(publicKey); final X509v3CertificateBuilder v3CertBldr = new JcaX509v3CertificateBuilder( issuingCert.getSubjectX500Principal(), BigInteger.valueOf(now), @@ -126,13 +127,29 @@ private CMPCertificate createCertificate( Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(issuingCert)); v3CertBldr.addExtension(Extension.basicConstraints, true, new BasicConstraints(false)); - final JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder( - AlgorithmHelper.getSigningAlgNameFromKey(enrollmentCredentials.getPrivateKey())) - .setProvider(TestCertUtility.BOUNCY_CASTLE_PROVIDER); + final PrivateKey signingPrivKey = enrollmentCredentials.getPrivateKey(); + + final PrivateKey altPrivKey = enrollmentCredentials.getAlternativePrivateKey(); + + final ContentSigner certSigner = new JcaContentSignerBuilder( + AlgorithmHelper.getSigningAlgNameFromKey(signingPrivKey)) + .setProvider(CertUtility.getBouncyCastleProvider()) + .build(signingPrivKey); + + if (altPrivKey != null) { + final ContentSigner altSigner = new JcaContentSignerBuilder( + AlgorithmHelper.getSigningAlgNameFromKey(altPrivKey)) + .setProvider(CertUtility.getBouncyCastleProvider()) + .build(altPrivKey); + + return TestCertUtility.cmpCertificateFromCertificate(new JcaX509CertificateConverter() + .setProvider(CertUtility.getBouncyCastleProvider()) + .getCertificate(v3CertBldr.build(certSigner, false, altSigner))); + } return TestCertUtility.cmpCertificateFromCertificate(new JcaX509CertificateConverter() - .setProvider(TestCertUtility.BOUNCY_CASTLE_PROVIDER) - .getCertificate(v3CertBldr.build(signerBuilder.build(enrollmentCredentials.getPrivateKey())))); + .setProvider(CertUtility.getBouncyCastleProvider()) + .getCertificate(v3CertBldr.build(certSigner))); } private PKIMessage generateError(final PKIMessage receivedMessage, final String errorDetails) throws Exception { @@ -237,7 +254,7 @@ private PKIMessage handleRevocationRequest(final PKIMessage receivedMessage) thr public byte[] processP10CerticateRequest(final byte[] csr, final String certProfile) { try { final PKCS10CertificationRequest p10Request = new PKCS10CertificationRequest(csr); - if (!p10Request.isSignatureValid(X509_CVPB.build(p10Request.getSubjectPublicKeyInfo()))) { + if (!CertUtility.validateP10Request(p10Request)) { LOGGER.error("invalid P10 Request"); return null; } diff --git a/src/test/java/com/siemens/pki/cmpracomponent/test/framework/ConfigurationFactory.java b/src/test/java/com/siemens/pki/cmpracomponent/test/framework/ConfigurationFactory.java index 469680e6..376fe91e 100644 --- a/src/test/java/com/siemens/pki/cmpracomponent/test/framework/ConfigurationFactory.java +++ b/src/test/java/com/siemens/pki/cmpracomponent/test/framework/ConfigurationFactory.java @@ -34,8 +34,10 @@ import com.siemens.pki.cmpracomponent.configuration.InventoryInterface; import com.siemens.pki.cmpracomponent.configuration.NestedEndpointContext; import com.siemens.pki.cmpracomponent.configuration.PersistencyInterface; +import com.siemens.pki.cmpracomponent.configuration.SignatureCredentialContext; import com.siemens.pki.cmpracomponent.configuration.SupportMessageHandlerInterface; import com.siemens.pki.cmpracomponent.configuration.VerificationContext; +import com.siemens.pki.cmpracomponent.cryptoservices.CertUtility; import com.siemens.pki.cmpracomponent.cryptoservices.KeyPairGeneratorFactory; import com.siemens.pki.cmpracomponent.persistency.DefaultPersistencyImplementation; import com.siemens.pki.cmpracomponent.protection.ProtectionProvider; @@ -47,10 +49,7 @@ import java.security.InvalidKeyException; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; -import java.security.cert.CRLException; import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509CRL; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.util.Collections; @@ -70,7 +69,7 @@ */ public class ConfigurationFactory { - private static final String INTERFACE_NAME = "testclient"; + private static final String INTERFACE_NAME = "TEST CMP CLient"; private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationFactory.class); public static ProtectionProvider eeSignaturebasedProtectionProvider; public static ProtectionProvider eePbmac1ProtectionProvider; @@ -142,6 +141,47 @@ public static Configuration buildMixedDownstreamConfiguration() throws Exception enrollmentTrust); } + public static CmpMessageInterface createSignatureBasedCmpMessageInterface( + SignatureCredentialContext outgoingProtection, VerificationContext incomingProtection) { + 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.reprotect; + } + + @Override + public CredentialContext getOutputCredentials() { + return outgoingProtection; + } + + @Override + public NestedEndpointContext getNestedEndpointContext() { + return null; + } + + @Override + public VerificationContext getInputVerification() { + return incomingProtection; + } + }; + } + public static Configuration buildSignatureBasedDownstreamConfiguration() throws Exception { final TrustChainAndPrivateKey downstreamCredentials = new TrustChainAndPrivateKey("credentials/CMP_LRA_DOWNSTREAM_Keystore.p12", "Password".toCharArray()); @@ -411,10 +451,11 @@ public X509Certificate getOldWithNew() { dpnNameRelativeToCRLIssuer, issuer, thisUpdate); - return Collections.singletonList((X509CRL) CertificateFactory.getInstance("X.509") - .generateCRL( - ConfigFileLoader.getConfigFileAsStream("credentials/CRL.der"))); - } catch (CRLException | CertificateException | IOException e) { + return Collections.singletonList(CertUtility.parseCrl( + ConfigFileLoader.getConfigFileAsStream("credentials/CRL.der") + .readAllBytes())); + + } catch (GeneralSecurityException | IOException e) { throw new RuntimeException(e); } }; diff --git a/src/test/java/com/siemens/pki/cmpracomponent/test/framework/TcAlgs.java b/src/test/java/com/siemens/pki/cmpracomponent/test/framework/TcAlgs.java new file mode 100644 index 00000000..acc5cdf1 --- /dev/null +++ b/src/test/java/com/siemens/pki/cmpracomponent/test/framework/TcAlgs.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2024 Siemens AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package com.siemens.pki.cmpracomponent.test.framework; + +import com.siemens.pki.cmpracomponent.cryptoservices.KeyPairGeneratorFactory; +import java.security.GeneralSecurityException; +import java.util.Arrays; +import java.util.List; +import org.bouncycastle.asn1.misc.MiscObjectIdentifiers; +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; + +/** + * provide lists of algoritrhms to test + */ +public class TcAlgs { + // utility class + private TcAlgs() {} + + public static List getPqSignatureAlgorithms() throws GeneralSecurityException { + return Arrays.asList(new Object[][] { + {"id_ml_dsa_44", KeyPairGeneratorFactory.getGenericKeyPairGenerator(NISTObjectIdentifiers.id_ml_dsa_44)}, + {"id_ml_dsa_65", KeyPairGeneratorFactory.getGenericKeyPairGenerator(NISTObjectIdentifiers.id_ml_dsa_65)}, + {"id_ml_dsa_87", KeyPairGeneratorFactory.getGenericKeyPairGenerator(NISTObjectIdentifiers.id_ml_dsa_87)}, + { + "id_slh_dsa_sha2_128s", + KeyPairGeneratorFactory.getGenericKeyPairGenerator(NISTObjectIdentifiers.id_slh_dsa_sha2_128s) + }, + // + }); + } + + public static List getCompositeAlgorithms() throws GeneralSecurityException { + return Arrays.asList(new Object[][] { + { + "MLDSA44-RSA2048-PSS-SHA256", + KeyPairGeneratorFactory.getGenericKeyPairGenerator(MiscObjectIdentifiers.id_MLDSA44_RSA2048_PSS_SHA256) + }, + { + "MLDSA44-RSA2048-PKCS15-SHA256", + KeyPairGeneratorFactory.getGenericKeyPairGenerator( + MiscObjectIdentifiers.id_MLDSA44_RSA2048_PKCS15_SHA256) + }, + { + "MLDSA44-ECDSA-P256-SHA256", + KeyPairGeneratorFactory.getGenericKeyPairGenerator(MiscObjectIdentifiers.id_MLDSA44_ECDSA_P256_SHA256) + }, + { + "MLDSA44-ECDSA-BRAINPOOLP256R1-SHA256", + KeyPairGeneratorFactory.getGenericKeyPairGenerator( + MiscObjectIdentifiers.id_MLDSA44_ECDSA_brainpoolP256r1_SHA256) + }, + { + "MLDSA44-ED25519-SHA512", + KeyPairGeneratorFactory.getGenericKeyPairGenerator(MiscObjectIdentifiers.id_MLDSA44_Ed25519_SHA512) + }, + { + "MLDSA65-RSA3072-PSS-SHA512", + KeyPairGeneratorFactory.getGenericKeyPairGenerator(MiscObjectIdentifiers.id_MLDSA65_RSA3072_PSS_SHA512) + }, + { + "MLDSA65-RSA3072-PKCS15-SHA512", + KeyPairGeneratorFactory.getGenericKeyPairGenerator( + MiscObjectIdentifiers.id_MLDSA65_RSA3072_PKCS15_SHA512) + }, + { + "MLDSA65-ECDSA-BRAINPOOLP256R1-SHA512", + KeyPairGeneratorFactory.getGenericKeyPairGenerator( + MiscObjectIdentifiers.id_MLDSA65_ECDSA_brainpoolP256r1_SHA512) + }, + { + "MLDSA65-ECDSA-P256-SHA512", + KeyPairGeneratorFactory.getGenericKeyPairGenerator(MiscObjectIdentifiers.id_MLDSA65_ECDSA_P256_SHA512) + }, + { + "MLDSA65-ED25519-SHA512", + KeyPairGeneratorFactory.getGenericKeyPairGenerator(MiscObjectIdentifiers.id_MLDSA65_Ed25519_SHA512) + }, + { + "MLDSA87-ECDSA-P384-SHA512", + KeyPairGeneratorFactory.getGenericKeyPairGenerator(MiscObjectIdentifiers.id_MLDSA87_ECDSA_P384_SHA512) + }, + { + "MLDSA87-ECDSA-BRAINPOOLP384R1-SHA512", + KeyPairGeneratorFactory.getGenericKeyPairGenerator( + MiscObjectIdentifiers.id_MLDSA87_ECDSA_brainpoolP384r1_SHA512) + }, + { + "MLDSA87-ED448-SHA512", + KeyPairGeneratorFactory.getGenericKeyPairGenerator(MiscObjectIdentifiers.id_MLDSA87_Ed448_SHA512) + }, + { + "FALCON512-ECDSA-P256-SHA256", + KeyPairGeneratorFactory.getGenericKeyPairGenerator(MiscObjectIdentifiers.id_Falcon512_ECDSA_P256_SHA256) + }, + { + "FALCON512-ECDSA-BRAINPOOLP256R1-SHA256", + KeyPairGeneratorFactory.getGenericKeyPairGenerator( + MiscObjectIdentifiers.id_Falcon512_ECDSA_brainpoolP256r1_SHA256) + }, + { + "FALCON512-ED25519-SHA512", + KeyPairGeneratorFactory.getGenericKeyPairGenerator(MiscObjectIdentifiers.id_Falcon512_Ed25519_SHA512) + }, + // + }); + } + + public static List getKemAlgorithms() throws GeneralSecurityException { + return Arrays.asList(new Object[][] { + { + "id_alg_ml_kem_512", + KeyPairGeneratorFactory.getGenericKeyPairGenerator(NISTObjectIdentifiers.id_alg_ml_kem_512) + }, + { + "id_alg_ml_kem_1024", + KeyPairGeneratorFactory.getGenericKeyPairGenerator(NISTObjectIdentifiers.id_alg_ml_kem_1024) + }, + }); + } + + public static List getClassicSignatureAlgorithms() throws GeneralSecurityException { + return Arrays.asList(new Object[][] { + {"RSA1024", KeyPairGeneratorFactory.getRsaKeyPairGenerator(1024)}, + {"RSA2048", KeyPairGeneratorFactory.getRsaKeyPairGenerator(2048)}, + {"Ed448", KeyPairGeneratorFactory.getEdDsaKeyPairGenerator("Ed448")}, + {"Ed25519", KeyPairGeneratorFactory.getEdDsaKeyPairGenerator("Ed25519")}, + {"secp256r1", KeyPairGeneratorFactory.getEcKeyPairGenerator("secp256r1")}, + // + }); + } +} diff --git a/src/test/java/com/siemens/pki/cmpracomponent/test/framework/TestCertUtility.java b/src/test/java/com/siemens/pki/cmpracomponent/test/framework/TestCertUtility.java index 1c29e146..b9054255 100644 --- a/src/test/java/com/siemens/pki/cmpracomponent/test/framework/TestCertUtility.java +++ b/src/test/java/com/siemens/pki/cmpracomponent/test/framework/TestCertUtility.java @@ -18,15 +18,26 @@ package com.siemens.pki.cmpracomponent.test.framework; import com.siemens.pki.cmpracomponent.cryptoservices.CertUtility; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.security.*; +import java.security.InvalidKeyException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.SignatureException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.DEROctetString; @@ -39,13 +50,11 @@ */ public class TestCertUtility { - public static final Provider BOUNCY_CASTLE_PROVIDER = CertUtility.getBouncyCastleProvider(); private static final Logger LOGGER = LoggerFactory.getLogger(TestCertUtility.class); private static final char[] TRUSTSTORE_SECRET = "Unimportant password".toCharArray(); private static final SecureRandom RANDOM = new SecureRandom(); - private static CertificateFactory certificateFactory; /** * conversion function from CMPCertificate to X509 certificate @@ -57,24 +66,12 @@ public class TestCertUtility { */ public static X509Certificate certificateFromCmpCertificate(final CMPCertificate cert) throws Exception { try { - return certificateFromEncoded(cert.getEncoded(ASN1Encoding.DER)); + return CertUtility.asX509Certificate(cert.getEncoded(ASN1Encoding.DER)); } catch (final IOException excpt) { throw new CertificateException(excpt); } } - /** - * conversion function from byte to X509 certificate - * - * @param encoded byte string to encode - * @return converted certificate - * @throws CertificateException if certificate could not be converted from - * encoded - */ - public static X509Certificate certificateFromEncoded(final byte[] encoded) throws Exception { - return (X509Certificate) getCertificateFactory().generateCertificate(new ByteArrayInputStream(encoded)); - } - /** * conversion function from CMPCertificates to X509 certificates * @@ -87,7 +84,7 @@ public static List certificatesFromCmpCertificates(final CMPCer try { final ArrayList ret = new ArrayList<>(certs.length); for (final CMPCertificate aktCert : certs) { - ret.add(certificateFromEncoded(aktCert.getEncoded(ASN1Encoding.DER))); + ret.add(CertUtility.asX509Certificate(aktCert.getEncoded(ASN1Encoding.DER))); } return ret; } catch (final IOException excpt) { @@ -155,21 +152,6 @@ public static byte[] generateRandomBytes(final int length) { return ret; } - /** - * Function to retrieve the static certificate factory object - * - * @return static certificate factory object - * @throws CertificateException thrown if the certificate factory could not be - * instantiated - * @throws Exception in case of an error - */ - public static synchronized CertificateFactory getCertificateFactory() throws Exception { - if (certificateFactory == null) { - certificateFactory = CertificateFactory.getInstance("X.509", BOUNCY_CASTLE_PROVIDER); - } - return certificateFactory; - } - /** * Checks whether given X.509 certificate is self-signed. * @@ -205,7 +187,7 @@ public static boolean isSelfSigned(final X509Certificate cert) public static synchronized List loadCertificatesFromFile(final String filename) throws Exception { try (InputStream is = ConfigFileLoader.getConfigFileAsStream(filename)) { final List ret = new ArrayList<>(); - final CertificateFactory cf = getCertificateFactory(); + final CertificateFactory cf = CertificateFactory.getInstance("X509"); for (final Certificate aktCert : cf.generateCertificates(is)) { ret.add((X509Certificate) aktCert); } @@ -294,7 +276,7 @@ public static KeyStore loadTruststoreFromFile(final String filename, final char[ try (InputStream is = ConfigFileLoader.getConfigFileAsStream(filename)) { final KeyStore truststore = KeyStore.getInstance(KeyStore.getDefaultType()); truststore.load(null, TRUSTSTORE_SECRET); - final CertificateFactory cf = getCertificateFactory(); + final CertificateFactory cf = CertificateFactory.getInstance("X509"); int i = 1; for (final Certificate aktCert : cf.generateCertificates(is)) { truststore.setCertificateEntry("cert_" + i++, aktCert); diff --git a/src/test/java/com/siemens/pki/cmpracomponent/test/framework/TestCertificateFactory.java b/src/test/java/com/siemens/pki/cmpracomponent/test/framework/TestCertificateFactory.java index fb451f47..6c6d9693 100644 --- a/src/test/java/com/siemens/pki/cmpracomponent/test/framework/TestCertificateFactory.java +++ b/src/test/java/com/siemens/pki/cmpracomponent/test/framework/TestCertificateFactory.java @@ -18,6 +18,7 @@ package com.siemens.pki.cmpracomponent.test.framework; import com.siemens.pki.cmpracomponent.cryptoservices.AlgorithmHelper; +import com.siemens.pki.cmpracomponent.cryptoservices.CertUtility; import java.io.IOException; import java.math.BigInteger; import java.net.URL; @@ -53,6 +54,7 @@ import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.slf4j.Logger; @@ -133,11 +135,12 @@ private static X509Certificate buildCertifcate(CertParams params) } } } - final JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder(params.getSignatureAlgorithm()) - .setProvider(TestCertUtility.BOUNCY_CASTLE_PROVIDER); + ContentSigner signer = new JcaContentSignerBuilder(params.getSignatureAlgorithm()) + .setProvider(CertUtility.getBouncyCastleProvider()) + .build(params.getSigningPrivateKey()); return new JcaX509CertificateConverter() - .setProvider(TestCertUtility.BOUNCY_CASTLE_PROVIDER) - .getCertificate(v3CertBldr.build(signerBuilder.build(params.getSigningPrivateKey()))); + .setProvider(CertUtility.getBouncyCastleProvider()) + .getCertificate(v3CertBldr.build(signer)); } private static Extension createAiaOcspExtension(final String url) throws IOException { diff --git a/src/test/java/com/siemens/pki/cmpracomponent/test/framework/TrustChainAndPrivateKey.java b/src/test/java/com/siemens/pki/cmpracomponent/test/framework/TrustChainAndPrivateKey.java index 7ba37257..71cac015 100644 --- a/src/test/java/com/siemens/pki/cmpracomponent/test/framework/TrustChainAndPrivateKey.java +++ b/src/test/java/com/siemens/pki/cmpracomponent/test/framework/TrustChainAndPrivateKey.java @@ -18,41 +18,222 @@ package com.siemens.pki.cmpracomponent.test.framework; import com.siemens.pki.cmpracomponent.configuration.SignatureCredentialContext; +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.KeyPairGeneratorFactory; import com.siemens.pki.cmpracomponent.protection.ProtectionProvider; +import com.siemens.pki.cmpracomponent.util.NullUtil; import java.io.IOException; +import java.math.BigInteger; import java.security.GeneralSecurityException; import java.security.Key; +import java.security.KeyPair; +import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.PrivateKey; +import java.security.PublicKey; import java.security.Signature; import java.security.cert.Certificate; +import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.LinkedList; import java.util.List; +import java.util.Set; +import javax.security.auth.x500.X500Principal; import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.DERBitString; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.cmp.CMPCertificate; import org.bouncycastle.asn1.cmp.ProtectedPart; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.cert.CertIOException; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; -public class TrustChainAndPrivateKey implements SignatureCredentialContext { +public class TrustChainAndPrivateKey implements SignatureCredentialContext, VerificationContext { // chain starting with end certificate ending with root certificate - private final ArrayList trustChain = new ArrayList<>(); + private final LinkedList trustChain = new LinkedList<>(); + + private final Set trustAnchors = new HashSet<>(); private PrivateKey privateKeyOfEndCertififcate = null; + private PrivateKey altPrivateKeyOfEndCertififcate = null; - public TrustChainAndPrivateKey(final String keyStoreFileName, final char[] password) throws Exception { + /** + * create a new cert chain + * + * @param subjectPrefix prefix fur all common names + * @param withoutEndCert true if end certificate should't be generated + * @param altSigningAlg optionale alternative signature algorithm + * @param kpg generator to use for key generation + * @param chainLength length from root to end to generate + * param withoutEndCert if true, generate unfinished chain without end certificate + * @throws Exception in case of error + */ + public TrustChainAndPrivateKey( + String subjectPrefix, boolean withoutEndCert, ASN1ObjectIdentifier altSigningAlg, KeyPairGenerator... kpg) + throws Exception { + final int finalChainLength = withoutEndCert ? kpg.length + 1 : kpg.length; + final long now = System.currentTimeMillis(); + final JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils(); + + KeyPairGenerator altKpg = + NullUtil.ifNotNull(altSigningAlg, s -> KeyPairGeneratorFactory.getGenericKeyPairGenerator(s)); + + // generate trusted root + KeyPair rootKeypair = kpg[0].generateKeyPair(); + privateKeyOfEndCertififcate = rootKeypair.getPrivate(); + KeyPair altKeypair = NullUtil.ifNotNull(altKpg, k -> k.generateKeyPair()); + altPrivateKeyOfEndCertififcate = NullUtil.ifNotNull(altKeypair, k -> k.getPrivate()); + X500Principal lastIssuer = new X500Principal("CN=" + subjectPrefix + "_ROOT"); + X509Certificate lastCert = generateCert( + lastIssuer, + null, + lastIssuer, + rootKeypair.getPublic(), + NullUtil.ifNotNull(altKeypair, k -> k.getPublic()), + now, + extUtils, + finalChainLength); + trustAnchors.add(lastCert); + trustChain.addLast(lastCert); + if (finalChainLength <= 1) { + return; + } + if (finalChainLength == 2 && withoutEndCert) { + return; + } + int certsStillToGenerate = finalChainLength - 1; + for (int i = 1; ; i++) { + KeyPair nextKeyPair = kpg[i].generateKeyPair(); + altKeypair = NullUtil.ifNotNull(altKpg, k -> k.generateKeyPair()); + X500Principal nextIssuer = certsStillToGenerate > 1 + ? new X500Principal("CN=" + subjectPrefix + "_INTERMEDIATE_" + certsStillToGenerate) + : new X500Principal("CN=" + subjectPrefix + "_END"); + lastCert = generateCert( + lastIssuer, + lastCert, + nextIssuer, + nextKeyPair.getPublic(), + NullUtil.ifNotNull(altKeypair, k -> k.getPublic()), + now, + extUtils, + certsStillToGenerate); + trustChain.addFirst(lastCert); + privateKeyOfEndCertififcate = nextKeyPair.getPrivate(); + altPrivateKeyOfEndCertififcate = NullUtil.ifNotNull(altKeypair, k -> k.getPrivate()); + certsStillToGenerate--; + if (certsStillToGenerate <= 0) { + return; + } + if (withoutEndCert && certsStillToGenerate <= 1) { + return; + } + lastIssuer = nextIssuer; + } + } + + private X509Certificate generateCert( + X500Principal lastIssuer, + X509Certificate lastCert, + X500Principal nextIssuer, + final PublicKey nextPublic, + PublicKey nextAltPublic, + final long now, + final JcaX509ExtensionUtils extUtils, + int certsStillToGenerate) + throws CertIOException, OperatorCreationException, CertificateException { + JcaX509v3CertificateBuilder nextV3CertBldr = new JcaX509v3CertificateBuilder( + lastIssuer, + BigInteger.valueOf(now), + new Date(now - 60 * 60 * 1000L), + new Date(now + 100 * 60 * 60 * 1000L), + nextIssuer, + nextPublic); + nextV3CertBldr.addExtension( + Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(nextPublic)); + if (nextAltPublic != null) { + nextV3CertBldr.addExtension( + Extension.subjectAltPublicKeyInfo, + false, + SubjectPublicKeyInfo.getInstance(nextAltPublic.getEncoded())); + } + if (lastCert != null) { + nextV3CertBldr.addExtension( + Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(lastCert)); + } else { + nextV3CertBldr.addExtension( + Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(nextPublic)); + } + nextV3CertBldr.addExtension( + Extension.basicConstraints, + true, + certsStillToGenerate > 1 + ? new BasicConstraints(certsStillToGenerate - 1) + : new BasicConstraints(false)); + ContentSigner certSigner = new JcaContentSignerBuilder( + AlgorithmHelper.getSigningAlgNameFromKey(privateKeyOfEndCertififcate)) + .setProvider(CertUtility.getBouncyCastleProvider()) + .build(privateKeyOfEndCertififcate); + if (altPrivateKeyOfEndCertififcate != null) { + ContentSigner altCertSigner = new JcaContentSignerBuilder( + AlgorithmHelper.getSigningAlgNameFromKey(altPrivateKeyOfEndCertififcate)) + .setProvider(CertUtility.getBouncyCastleProvider()) + .build(altPrivateKeyOfEndCertififcate); + return new JcaX509CertificateConverter() + .setProvider(CertUtility.getBouncyCastleProvider()) + .getCertificate(nextV3CertBldr.build(certSigner, false, altCertSigner)); + } + return new JcaX509CertificateConverter() + .setProvider(CertUtility.getBouncyCastleProvider()) + .getCertificate(nextV3CertBldr.build(certSigner)); + } + + /** + * load keystore from file + * + * @param keyStoreFileName name of the key store file + * @param password keystore password + * @throws Exception in case of error + */ + public TrustChainAndPrivateKey(final String keyStoreFileName, final char[] password) throws Exception { this(TestCertUtility.loadKeystoreFromFile(keyStoreFileName, password), password); } + /** + * initilize from keystore + * + * @param keyStore the keystore to load from + * @param password keystore password + * @throws Exception in case of error + */ TrustChainAndPrivateKey(final KeyStore keyStore, final char[] password) throws Exception { + for (final String aktAlias : Collections.list(keyStore.aliases())) { + if (keyStore.isCertificateEntry(aktAlias)) { + Certificate aktTrusted = keyStore.getCertificate(aktAlias); + if (aktTrusted instanceof X509Certificate) { + trustAnchors.add((X509Certificate) aktTrusted); + } + } + } for (final String aktAlias : Collections.list(keyStore.aliases())) { final Key privKey = keyStore.getKey(aktAlias, password); if (!(privKey instanceof PrivateKey)) { @@ -82,6 +263,11 @@ public PrivateKey getPrivateKey() { return privateKeyOfEndCertififcate; } + @Override + public PrivateKey getAlternativePrivateKey() { + return altPrivateKeyOfEndCertififcate; + } + public ProtectionProvider setEndEntityToProtect(final CMPCertificate certificate, final PrivateKey privateKey) throws Exception { final AlgorithmIdentifier protectionAlg = AlgorithmHelper.getSigningAlgIdFromKey(privateKey); @@ -114,7 +300,8 @@ public AlgorithmIdentifier getProtectionAlg() { @Override public DERBitString getProtectionFor(final ProtectedPart protectedPart) throws GeneralSecurityException, IOException { - final Signature sig = Signature.getInstance(AlgorithmHelper.getSigningAlgNameFromKey(privateKey)); + final Signature sig = + AlgorithmHelper.getSignature(AlgorithmHelper.getSigningAlgNameFromKey(privateKey)); sig.initSign(privateKey); sig.update(protectedPart.getEncoded(ASN1Encoding.DER)); return new DERBitString(sig.sign()); @@ -131,4 +318,14 @@ public DEROctetString getSenderKID() { } }; } + + @Override + public Collection getTrustedCertificates() { + return trustAnchors; + } + + @Override + public Collection getAdditionalCerts() { + return trustChain; + } }