Builds the CMS signed certificate message.
- *payloadCertificate and SigningCertificate needs to be set previously.
- * - * @param detached flag whether only the signature should be returned (detached signature) - * @return Bytes of signed CMS message. - */ - public byte[] build(boolean detached) { - Security.addProvider(new BouncyCastleProvider()); - - if (payloadCertificate == null || signingCertificate == null || signingCertificatePrivateKey == null) { - throw new RuntimeException("Message Builder is not ready"); - } - - byte[] messageBytes; - CMSSignedDataGenerator signedDataGenerator = new CMSSignedDataGenerator(); - - try { - DigestCalculatorProvider digestCalculatorProvider = new JcaDigestCalculatorProviderBuilder().build(); - - String signingAlgorithmName = - new DefaultAlgorithmNameFinder().getAlgorithmName(signingCertificate.getSignatureAlgorithm()); - - ContentSigner contentSigner = - new JcaContentSignerBuilder(signingAlgorithmName).build(signingCertificatePrivateKey); - - SignerInfoGenerator signerInfoGenerator = new JcaSignerInfoGeneratorBuilder(digestCalculatorProvider) - .build(contentSigner, signingCertificate); - - signedDataGenerator.addSignerInfoGenerator(signerInfoGenerator); - - signedDataGenerator.addCertificate(signingCertificate); - - CMSSignedData signedData = signedDataGenerator.generate( - new CMSProcessableByteArray(payloadCertificate.getEncoded()), !detached); - - messageBytes = signedData.getEncoded(); - } catch (OperatorCreationException | CMSException | IOException e) { - throw new RuntimeException("Failed to create signed message"); - } - - return messageBytes; - } - - /** - *Builds the CMS signed certificate message.
- *payloadCertificate and SigningCertificate needs to be set previously.
- * - * @return Bytes of signed CMS message. - */ - public byte[] build() { - return build(false); - } - - /** - *Builds the CMS signed certificate message.
- *payloadCertificate and SigningCertificate needs to be set previously.
- * - * @param detached flag whether only the signature should be returned (detached signature) - * @return Base64 encoded String of CMS message. - */ - public String buildAsString(boolean detached) { - return Base64.getEncoder().encodeToString(build(detached)); - } - - /** - *Builds the CMS signed certificate message.
- *payloadCertificate and SigningCertificate needs to be set previously.
- * - * @return Base64 encoded String of signed CMS message. - */ - public String buildAsString() { - return Base64.getEncoder().encodeToString(build(false)); - } } diff --git a/src/main/java/eu/europa/ec/dgc/signing/SignedCertificateMessageParser.java b/src/main/java/eu/europa/ec/dgc/signing/SignedCertificateMessageParser.java index 27ab0fb..b96ac29 100644 --- a/src/main/java/eu/europa/ec/dgc/signing/SignedCertificateMessageParser.java +++ b/src/main/java/eu/europa/ec/dgc/signing/SignedCertificateMessageParser.java @@ -20,74 +20,31 @@ package eu.europa.ec.dgc.signing; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.security.Security; -import java.security.cert.CertificateException; -import java.util.Base64; -import java.util.Collection; -import lombok.Getter; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; -import org.bouncycastle.asn1.cms.CMSObjectIdentifiers; import org.bouncycastle.cert.X509CertificateHolder; -import org.bouncycastle.cms.CMSException; -import org.bouncycastle.cms.CMSProcessableByteArray; -import org.bouncycastle.cms.CMSSignedData; -import org.bouncycastle.cms.CMSSignedDataGenerator; -import org.bouncycastle.cms.SignerInformation; -import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.operator.OperatorCreationException; /** * Utility to parse a CMS signed message containing a DER encoded X509 Certificate. */ @Slf4j -public class SignedCertificateMessageParser { +public class SignedCertificateMessageParser extends SignedMessageParserThe result of parsing the CMS message.
* - *Result of SUCCESS does not mean that the signature of the signed message is valid. - * Pay attention to the value of signatureVerified. Only in conjunction of - * parserState == ParserState.SUCCESS and signatureVerified == true a valid certificate CMS - * message was passed to the parser.
+ * @deprecated use .getPayload() instead. + * @return certificate. */ - @Getter - private ParserState parserState = ParserState.IDLE; - - /** - *Result of the integrity check of the cms message.
- * - *The result just proofs, that the message was signed with the attached certificate. - * The integrity of the signer certificate is not proven
- */ - @Getter - private boolean signatureVerified = false; - - /** - *Base64 encoded signature of the cms message.
- * - *This string contains only the signature which signs the message.
- */ - @Getter - private String signature; + @Deprecated + public X509CertificateHolder getPayloadCertificate() { + return getPayload(); + } /** * Create a new instance of {@link SignedCertificateMessageParser} and starts the parsing process. @@ -96,9 +53,7 @@ public class SignedCertificateMessageParser { * @param cmsMessage base64 encoded CMS message bytes. */ public SignedCertificateMessageParser(@NonNull byte[] cmsMessage) { - raw = cmsMessage; - rawPayload = null; - afterPropertiesSet(); + super(cmsMessage); } /** @@ -109,9 +64,7 @@ public SignedCertificateMessageParser(@NonNull byte[] cmsMessage) { * @param cmsPayload base64 encoded CMS message payload. */ public SignedCertificateMessageParser(@NonNull byte[] cmsSignature, @NonNull byte[] cmsPayload) { - raw = cmsSignature; - rawPayload = cmsPayload; - afterPropertiesSet(); + super(cmsSignature, cmsPayload); } /** @@ -121,9 +74,7 @@ public SignedCertificateMessageParser(@NonNull byte[] cmsSignature, @NonNull byt * @param cmsMessage base64 encoded CMS message string. */ public SignedCertificateMessageParser(@NonNull String cmsMessage) { - raw = cmsMessage.getBytes(StandardCharsets.UTF_8); - rawPayload = null; - afterPropertiesSet(); + super(cmsMessage); } /** @@ -134,9 +85,7 @@ public SignedCertificateMessageParser(@NonNull String cmsMessage) { * @param cmsPayload base64 encoded CMS message payload string. */ public SignedCertificateMessageParser(@NonNull String cmsSignature, @NonNull String cmsPayload) { - raw = cmsSignature.getBytes(StandardCharsets.UTF_8); - rawPayload = cmsPayload.getBytes(StandardCharsets.UTF_8); - afterPropertiesSet(); + super(cmsSignature, cmsPayload); } /** @@ -147,9 +96,7 @@ public SignedCertificateMessageParser(@NonNull String cmsSignature, @NonNull Str * @param cmsPayload base64 encoded CMS message payload string. */ public SignedCertificateMessageParser(@NonNull byte[] cmsSignature, @NonNull String cmsPayload) { - raw = cmsSignature; - rawPayload = cmsPayload.getBytes(StandardCharsets.UTF_8); - afterPropertiesSet(); + super(cmsSignature, cmsPayload); } /** @@ -160,120 +107,6 @@ public SignedCertificateMessageParser(@NonNull byte[] cmsSignature, @NonNull Str * @param cmsPayload base64 encoded CMS message payload bytes. */ public SignedCertificateMessageParser(@NonNull String cmsSignature, @NonNull byte[] cmsPayload) { - raw = cmsSignature.getBytes(StandardCharsets.UTF_8); - rawPayload = cmsPayload; - afterPropertiesSet(); - } - - private void afterPropertiesSet() { - Security.addProvider(new BouncyCastleProvider()); - - // Parse Base64 - byte[] cmsBytes; - byte[] cmsPayloadBytes = null; - try { - cmsBytes = Base64.getDecoder().decode(raw); - - if (rawPayload != null) { - cmsPayloadBytes = Base64.getDecoder().decode(rawPayload); - } - - } catch (IllegalArgumentException e) { - parserState = ParserState.FAILURE_INVALID_BASE64; - return; - } - - // Parse CMS Message; - CMSSignedData cmsSignedData; - try { - if (rawPayload == null) { - cmsSignedData = new CMSSignedData(cmsBytes); - } else { - CMSProcessableByteArray cmsProcessablePayload = new CMSProcessableByteArray(cmsPayloadBytes); - cmsSignedData = new CMSSignedData(cmsProcessablePayload, cmsBytes); - } - } catch (CMSException e) { - parserState = ParserState.FAILURE_INVALID_CMS; - return; - } - - // Check Payload of CMS Message - if (cmsSignedData.getSignedContent().getContentType() != CMSObjectIdentifiers.data) { - parserState = ParserState.FAILURE_INVALID_CMS_BODY; - return; - } - - // Extract Certificate from Payload - try { - payloadCertificate = new X509CertificateHolder( - (byte[]) cmsSignedData.getSignedContent().getContent()); - } catch (IOException e) { - parserState = ParserState.FAILURE_CMS_BODY_NO_CERTIFICATE; - return; - } - - // Get signer certificate - CollectionAbstract class to build Typed CMS Message builder.
+ *
+ * T -> DataType which needs to be signed with CMS.
+ * R -> Implemented Subclass
+ *
Builds the CMS signed message.
+ *payload and SigningCertificate needs to be set previously.
+ * + * @param detached flag whether only the signature should be returned (detached signature) + * @return Bytes of signed CMS message. + */ + public byte[] build(boolean detached) { + Security.addProvider(new BouncyCastleProvider()); + + if (payload == null || signingCertificate == null || signingCertificatePrivateKey == null) { + throw new RuntimeException("Message Builder is not ready"); + } + + byte[] messageBytes; + CMSSignedDataGenerator signedDataGenerator = new CMSSignedDataGenerator(); + + try { + DigestCalculatorProvider digestCalculatorProvider = new JcaDigestCalculatorProviderBuilder().build(); + + String signingAlgorithmName = + new DefaultAlgorithmNameFinder().getAlgorithmName(signingCertificate.getSignatureAlgorithm()); + + ContentSigner contentSigner = + new JcaContentSignerBuilder(signingAlgorithmName).build(signingCertificatePrivateKey); + + SignerInfoGenerator signerInfoGenerator = new JcaSignerInfoGeneratorBuilder(digestCalculatorProvider) + .build(contentSigner, signingCertificate); + + signedDataGenerator.addSignerInfoGenerator(signerInfoGenerator); + + signedDataGenerator.addCertificate(signingCertificate); + + CMSSignedData signedData = signedDataGenerator.generate( + new CMSProcessableByteArray(convertToBytes(payload)), !detached); + + messageBytes = signedData.getEncoded(); + } catch (OperatorCreationException | CMSException | IOException e) { + throw new RuntimeException("Failed to create signed message"); + } + + return messageBytes; + } + + /** + *Builds the CMS signed message.
+ *payload and SigningCertificate needs to be set previously.
+ * + * @return Bytes of signed CMS message. + */ + public byte[] build() { + return build(false); + } + + /** + *Builds the CMS signed message.
+ *payload and SigningCertificate needs to be set previously.
+ * + * @param detached flag whether only the signature should be returned (detached signature) + * @return Base64 encoded String of CMS message. + */ + public String buildAsString(boolean detached) { + return Base64.getEncoder().encodeToString(build(detached)); + } + + /** + *Builds the CMS signed message.
+ *payload and SigningCertificate needs to be set previously.
+ * + * @return Base64 encoded String of signed CMS message. + */ + public String buildAsString() { + return Base64.getEncoder().encodeToString(build(false)); + } +} diff --git a/src/main/java/eu/europa/ec/dgc/signing/SignedMessageParser.java b/src/main/java/eu/europa/ec/dgc/signing/SignedMessageParser.java new file mode 100644 index 0000000..5429b13 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/signing/SignedMessageParser.java @@ -0,0 +1,294 @@ +/*- + * ---license-start + * EU Digital Green Certificate Gateway Service / dgc-lib + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * 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. + * ---license-end + */ + +package eu.europa.ec.dgc.signing; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.Security; +import java.security.cert.CertificateException; +import java.util.Base64; +import java.util.Collection; +import lombok.Getter; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.asn1.cms.CMSObjectIdentifiers; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cms.CMSException; +import org.bouncycastle.cms.CMSProcessableByteArray; +import org.bouncycastle.cms.CMSSignedData; +import org.bouncycastle.cms.CMSSignedDataGenerator; +import org.bouncycastle.cms.SignerInformation; +import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.OperatorCreationException; + +/** + * Abstract class to create typed CMS Message parsers. + */ +@Slf4j +public abstract class SignedMessageParserThe result of parsing the CMS message.
+ * + *Result of SUCCESS does not mean that the signature of the signed message is valid. + * Pay attention to the value of signatureVerified. Only in conjunction of + * parserState == ParserState.SUCCESS and signatureVerified == true a valid CMS + * message was passed to the parser.
+ */ + @Getter + private ParserState parserState = ParserState.IDLE; + + /** + *Result of the integrity check of the cms message.
+ * + *The result just proofs, that the message was signed with the attached certificate.
+ */ + @Getter + private boolean signatureVerified = false; + + /** + *Base64 encoded signature of the cms message.
+ * + *This string contains only the signature which signs the message.
+ */ + @Getter + private String signature; + + /** + * Method to convert the encoded bytes to the actual Class instance. + * + * @param bytes to convert. + * @return instance of T. + */ + abstract T convertFromBytes(byte[] bytes) throws Exception; + + /** + * Create a new instance of {@link SignedMessageParser} and starts the parsing process. + * The result of parsing process will be immediately available. + * + * @param cmsMessage base64 encoded CMS message bytes. + */ + protected SignedMessageParser(@NonNull byte[] cmsMessage) { + raw = cmsMessage; + rawPayload = null; + afterPropertiesSet(); + } + + /** + * Create a new instance of {@link SignedMessageParser} and starts the parsing process. + * The result of parsing process will be immediately available. + * + * @param cmsSignature base64 encoded detached CMS signature bytes. + * @param cmsPayload base64 encoded CMS message payload. + */ + protected SignedMessageParser(@NonNull byte[] cmsSignature, @NonNull byte[] cmsPayload) { + raw = cmsSignature; + rawPayload = cmsPayload; + afterPropertiesSet(); + } + + /** + * Create a new instance of {@link SignedMessageParser} and starts the parsing process. + * The result of parsing process will be immediately available. + * + * @param cmsMessage base64 encoded CMS message string. + */ + protected SignedMessageParser(@NonNull String cmsMessage) { + raw = cmsMessage.getBytes(StandardCharsets.UTF_8); + rawPayload = null; + afterPropertiesSet(); + } + + /** + * Create a new instance of {@link SignedMessageParser} and starts the parsing process. + * The result of parsing process will be immediately available. + * + * @param cmsSignature base64 encoded detached CMS signature string. + * @param cmsPayload base64 encoded CMS message payload string. + */ + protected SignedMessageParser(@NonNull String cmsSignature, @NonNull String cmsPayload) { + raw = cmsSignature.getBytes(StandardCharsets.UTF_8); + rawPayload = cmsPayload.getBytes(StandardCharsets.UTF_8); + afterPropertiesSet(); + } + + /** + * Create a new instance of {@link SignedMessageParser} and starts the parsing process. + * The result of parsing process will be immediately available. + * + * @param cmsSignature base64 encoded detached CMS signature bytes. + * @param cmsPayload base64 encoded CMS message payload string. + */ + protected SignedMessageParser(@NonNull byte[] cmsSignature, @NonNull String cmsPayload) { + raw = cmsSignature; + rawPayload = cmsPayload.getBytes(StandardCharsets.UTF_8); + afterPropertiesSet(); + } + + /** + * Create a new instance of {@link SignedMessageParser} and starts the parsing process. + * The result of parsing process will be immediately available. + * + * @param cmsSignature base64 encoded detached CMS signature string. + * @param cmsPayload base64 encoded CMS message payload bytes. + */ + protected SignedMessageParser(@NonNull String cmsSignature, @NonNull byte[] cmsPayload) { + raw = cmsSignature.getBytes(StandardCharsets.UTF_8); + rawPayload = cmsPayload; + afterPropertiesSet(); + } + + private void afterPropertiesSet() { + Security.addProvider(new BouncyCastleProvider()); + + // Parse Base64 + byte[] cmsBytes; + byte[] cmsPayloadBytes = null; + try { + cmsBytes = Base64.getDecoder().decode(raw); + + if (rawPayload != null) { + cmsPayloadBytes = Base64.getDecoder().decode(rawPayload); + } + + } catch (IllegalArgumentException e) { + parserState = ParserState.FAILURE_INVALID_BASE64; + return; + } + + // Parse CMS Message; + CMSSignedData cmsSignedData; + try { + if (rawPayload == null) { + cmsSignedData = new CMSSignedData(cmsBytes); + } else { + CMSProcessableByteArray cmsProcessablePayload = new CMSProcessableByteArray(cmsPayloadBytes); + cmsSignedData = new CMSSignedData(cmsProcessablePayload, cmsBytes); + } + } catch (CMSException e) { + parserState = ParserState.FAILURE_INVALID_CMS; + return; + } + + // Check Payload of CMS Message + if (cmsSignedData.getSignedContent().getContentType() != CMSObjectIdentifiers.data) { + parserState = ParserState.FAILURE_INVALID_CMS_BODY; + return; + } + + // Extract and convert payload + try { + payload = convertFromBytes((byte[]) cmsSignedData.getSignedContent().getContent()); + } catch (Exception e) { + parserState = ParserState.FAILURE_CMS_BODY_PARSING_FAILED; + return; + } + + // Get signer certificate + Collection