From c59d1b5805b768042ec3902c47de7d2af2993826 Mon Sep 17 00:00:00 2001 From: Felix Dittrich Date: Tue, 15 Mar 2022 17:01:50 +0100 Subject: [PATCH] Add Parser and Builder for SignedByteArrayMessages --- .../SignedByteArrayMessageBuilder.java | 43 +++ .../signing/SignedByteArrayMessageParser.java | 100 +++++++ .../SignedByteArrayMessageBuilderTest.java | 144 ++++++++++ .../SignedByteArrayMessageParserTest.java | 260 ++++++++++++++++++ 4 files changed, 547 insertions(+) create mode 100644 src/main/java/eu/europa/ec/dgc/signing/SignedByteArrayMessageBuilder.java create mode 100644 src/main/java/eu/europa/ec/dgc/signing/SignedByteArrayMessageParser.java create mode 100644 src/test/java/eu/europa/ec/dgc/signing/SignedByteArrayMessageBuilderTest.java create mode 100644 src/test/java/eu/europa/ec/dgc/signing/SignedByteArrayMessageParserTest.java diff --git a/src/main/java/eu/europa/ec/dgc/signing/SignedByteArrayMessageBuilder.java b/src/main/java/eu/europa/ec/dgc/signing/SignedByteArrayMessageBuilder.java new file mode 100644 index 0000000..1315f76 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/signing/SignedByteArrayMessageBuilder.java @@ -0,0 +1,43 @@ +/*- + * ---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 lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * Utility to build a CMS signed message containing a Byte Array (e.g. a File). + */ +@Slf4j +@NoArgsConstructor +public class SignedByteArrayMessageBuilder extends SignedMessageBuilder { + + @Override + byte[] convertToBytes(byte[] payload) { + return payload; + } + + @Override + SignedByteArrayMessageBuilder getThis() { + return this; + } + +} diff --git a/src/main/java/eu/europa/ec/dgc/signing/SignedByteArrayMessageParser.java b/src/main/java/eu/europa/ec/dgc/signing/SignedByteArrayMessageParser.java new file mode 100644 index 0000000..e432f7b --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/signing/SignedByteArrayMessageParser.java @@ -0,0 +1,100 @@ +/*- + * ---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 lombok.NonNull; +import lombok.extern.slf4j.Slf4j; + +/** + * Utility to parse a CMS signed message containing a Byte Array (e.g. a File). + */ +@Slf4j +public class SignedByteArrayMessageParser extends SignedMessageParser { + + @Override + byte[] convertFromBytes(byte[] bytes) { + return bytes; + } + + /** + * Create a new instance of {@link SignedByteArrayMessageParser} and starts the parsing process. + * The result of parsing process will be immediately available. + * + * @param cmsMessage base64 encoded CMS message bytes. + */ + public SignedByteArrayMessageParser(@NonNull byte[] cmsMessage) { + super(cmsMessage); + } + + /** + * Create a new instance of {@link SignedByteArrayMessageParser} 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. + */ + public SignedByteArrayMessageParser(@NonNull byte[] cmsSignature, @NonNull byte[] cmsPayload) { + super(cmsSignature, cmsPayload); + } + + /** + * Create a new instance of {@link SignedByteArrayMessageParser} and starts the parsing process. + * The result of parsing process will be immediately available. + * + * @param cmsMessage base64 encoded CMS message string. + */ + public SignedByteArrayMessageParser(@NonNull String cmsMessage) { + super(cmsMessage); + } + + /** + * Create a new instance of {@link SignedByteArrayMessageParser} 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. + */ + public SignedByteArrayMessageParser(@NonNull String cmsSignature, @NonNull String cmsPayload) { + super(cmsSignature, cmsPayload); + } + + /** + * Create a new instance of {@link SignedByteArrayMessageParser} 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. + */ + public SignedByteArrayMessageParser(@NonNull byte[] cmsSignature, @NonNull String cmsPayload) { + super(cmsSignature, cmsPayload); + } + + /** + * Create a new instance of {@link SignedByteArrayMessageParser} 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. + */ + public SignedByteArrayMessageParser(@NonNull String cmsSignature, @NonNull byte[] cmsPayload) { + super(cmsSignature, cmsPayload); + } +} diff --git a/src/test/java/eu/europa/ec/dgc/signing/SignedByteArrayMessageBuilderTest.java b/src/test/java/eu/europa/ec/dgc/signing/SignedByteArrayMessageBuilderTest.java new file mode 100644 index 0000000..4f762cf --- /dev/null +++ b/src/test/java/eu/europa/ec/dgc/signing/SignedByteArrayMessageBuilderTest.java @@ -0,0 +1,144 @@ +/*- + * ---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 eu.europa.ec.dgc.testdata.CertificateTestUtils; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.cert.X509Certificate; +import java.util.Base64; +import java.util.Collection; +import java.util.Random; +import org.bouncycastle.asn1.cms.CMSObjectIdentifiers; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cms.CMSProcessableByteArray; +import org.bouncycastle.cms.CMSSignedData; +import org.bouncycastle.cms.SignerInformation; +import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class SignedByteArrayMessageBuilderTest { + + KeyPair signingKeyPair; + X509Certificate signingCertificate; + byte[] payload; + + SignedByteArrayMessageBuilder builder; + + @BeforeEach + void setupTestData() throws Exception { + payload = new byte[100]; + new Random().nextBytes(payload); + + signingKeyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + signingCertificate = CertificateTestUtils.generateCertificate(signingKeyPair, "DE", "SigningCertificate"); + + builder = new SignedByteArrayMessageBuilder() + .withPayload(payload) + .withSigningCertificate(new X509CertificateHolder(signingCertificate.getEncoded()), signingKeyPair.getPrivate()); + } + + @Test + void testUnreadyBuilder() { + builder = new SignedByteArrayMessageBuilder(); + Assertions.assertThrows(RuntimeException.class, builder::buildAsString); + } + + @Test + void testSignedMessage() throws Exception { + CMSSignedData cmsSignedData = new CMSSignedData(builder.build()); + + Assertions.assertEquals(CMSObjectIdentifiers.data, cmsSignedData.getSignedContent().getContentType()); + Assertions.assertArrayEquals(payload, (byte[]) cmsSignedData.getSignedContent().getContent()); + + Collection certificateHolderCollection = cmsSignedData.getCertificates().getMatches(null); + Assertions.assertEquals(1, certificateHolderCollection.size()); + X509CertificateHolder readSigningCertificate = certificateHolderCollection.iterator().next(); + Assertions.assertNotNull(readSigningCertificate); + + Assertions.assertEquals(1, cmsSignedData.getSignerInfos().size()); + SignerInformation signerInformation = cmsSignedData.getSignerInfos().iterator().next(); + + Assertions.assertTrue(signerInformation.verify(new JcaSimpleSignerInfoVerifierBuilder().build(signingCertificate))); + Assertions.assertTrue(signerInformation.verify(new JcaSimpleSignerInfoVerifierBuilder().build(readSigningCertificate))); + } + + @Test + void testSignedMessageDetached() throws Exception { + CMSSignedData cmsSignedData = new CMSSignedData(builder.build(true)); + + Assertions.assertNull(cmsSignedData.getSignedContent()); + + Collection certificateHolderCollection = cmsSignedData.getCertificates().getMatches(null); + Assertions.assertEquals(1, certificateHolderCollection.size()); + X509CertificateHolder readSigningCertificate = certificateHolderCollection.iterator().next(); + Assertions.assertNotNull(readSigningCertificate); + + cmsSignedData = new CMSSignedData(new CMSProcessableByteArray(payload), builder.build(true)); + + Assertions.assertEquals(1, cmsSignedData.getSignerInfos().size()); + SignerInformation signerInformation = cmsSignedData.getSignerInfos().iterator().next(); + + Assertions.assertTrue(signerInformation.verify(new JcaSimpleSignerInfoVerifierBuilder().build(signingCertificate))); + Assertions.assertTrue(signerInformation.verify(new JcaSimpleSignerInfoVerifierBuilder().build(readSigningCertificate))); + } + + @Test + void testSignedMessageBase64() throws Exception { + CMSSignedData cmsSignedData = new CMSSignedData(Base64.getDecoder().decode(builder.buildAsString())); + + Assertions.assertEquals(CMSObjectIdentifiers.data, cmsSignedData.getSignedContent().getContentType()); + Assertions.assertArrayEquals(payload, (byte[]) cmsSignedData.getSignedContent().getContent()); + + Collection certificateHolderCollection = cmsSignedData.getCertificates().getMatches(null); + Assertions.assertEquals(1, certificateHolderCollection.size()); + X509CertificateHolder readSigningCertificate = certificateHolderCollection.iterator().next(); + Assertions.assertNotNull(readSigningCertificate); + + Assertions.assertEquals(1, cmsSignedData.getSignerInfos().size()); + SignerInformation signerInformation = cmsSignedData.getSignerInfos().iterator().next(); + + Assertions.assertTrue(signerInformation.verify(new JcaSimpleSignerInfoVerifierBuilder().build(signingCertificate))); + Assertions.assertTrue(signerInformation.verify(new JcaSimpleSignerInfoVerifierBuilder().build(readSigningCertificate))); + } + + @Test + void testSignedMessageDetachedBase64() throws Exception { + CMSSignedData cmsSignedData = new CMSSignedData(Base64.getDecoder().decode(builder.buildAsString(true))); + + Assertions.assertNull(cmsSignedData.getSignedContent()); + + Collection certificateHolderCollection = cmsSignedData.getCertificates().getMatches(null); + Assertions.assertEquals(1, certificateHolderCollection.size()); + X509CertificateHolder readSigningCertificate = certificateHolderCollection.iterator().next(); + Assertions.assertNotNull(readSigningCertificate); + + cmsSignedData = new CMSSignedData(new CMSProcessableByteArray(payload), Base64.getDecoder().decode(builder.buildAsString(true))); + + Assertions.assertEquals(1, cmsSignedData.getSignerInfos().size()); + SignerInformation signerInformation = cmsSignedData.getSignerInfos().iterator().next(); + + Assertions.assertTrue(signerInformation.verify(new JcaSimpleSignerInfoVerifierBuilder().build(signingCertificate))); + Assertions.assertTrue(signerInformation.verify(new JcaSimpleSignerInfoVerifierBuilder().build(readSigningCertificate))); + } +} diff --git a/src/test/java/eu/europa/ec/dgc/signing/SignedByteArrayMessageParserTest.java b/src/test/java/eu/europa/ec/dgc/signing/SignedByteArrayMessageParserTest.java new file mode 100644 index 0000000..0147cbe --- /dev/null +++ b/src/test/java/eu/europa/ec/dgc/signing/SignedByteArrayMessageParserTest.java @@ -0,0 +1,260 @@ +/*- + * ---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 eu.europa.ec.dgc.testdata.CertificateTestUtils; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.Base64; +import java.util.Random; +import org.bouncycastle.asn1.cms.CMSObjectIdentifiers; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cms.CMSProcessableByteArray; +import org.bouncycastle.cms.CMSSignedData; +import org.bouncycastle.cms.CMSSignedDataGenerator; +import org.bouncycastle.cms.SignerInfoGenerator; +import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.DefaultAlgorithmNameFinder; +import org.bouncycastle.operator.DigestCalculatorProvider; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +class SignedByteArrayMessageParserTest { + + KeyPair signingKeyPair; + X509Certificate signingCertificate; + byte[] payload; + + SignedByteArrayMessageBuilder builder; + + @BeforeEach + void setupTestData() throws Exception { + payload = new byte[100]; + new Random().nextBytes(payload); + + signingKeyPair = KeyPairGenerator.getInstance("ec").generateKeyPair(); + signingCertificate = CertificateTestUtils.generateCertificate(signingKeyPair, "DE", "SigningCertificate"); + + builder = new SignedByteArrayMessageBuilder() + .withPayload(payload) + .withSigningCertificate(new X509CertificateHolder(signingCertificate.getEncoded()), signingKeyPair.getPrivate()); + } + + @Test + void parserShouldParseByteArray() throws IOException, CertificateEncodingException { + SignedByteArrayMessageParser parser = new SignedByteArrayMessageParser( + Base64.getEncoder().encode(builder.build())); + + Assertions.assertEquals(SignedByteArrayMessageParser.ParserState.SUCCESS, parser.getParserState()); + Assertions.assertArrayEquals(payload, parser.getPayload()); + Assertions.assertArrayEquals(signingCertificate.getEncoded(), parser.getSigningCertificate().getEncoded()); + Assertions.assertTrue(parser.isSignatureVerified()); + checkSignatureFromParser(parser.getSignature()); + } + + @Test + void parserShouldParseByteArrayWithDetachedPayload() throws IOException, CertificateEncodingException { + byte[] cms = Base64.getEncoder().encode(builder.build(true)); + + SignedByteArrayMessageParser parser = new SignedByteArrayMessageParser( + cms, + Base64.getEncoder().encode(payload)); + + Assertions.assertEquals(SignedByteArrayMessageParser.ParserState.SUCCESS, parser.getParserState()); + Assertions.assertArrayEquals(payload, parser.getPayload()); + Assertions.assertArrayEquals(signingCertificate.getEncoded(), parser.getSigningCertificate().getEncoded()); + Assertions.assertTrue(parser.isSignatureVerified()); + Assertions.assertEquals(new String(cms), parser.getSignature()); + } + + @Test + void parserShouldParseByteArrayWithDetachedPayloadAsString() throws IOException, CertificateEncodingException { + byte[] cms = Base64.getEncoder().encode(builder.build(true)); + + SignedByteArrayMessageParser parser = new SignedByteArrayMessageParser( + cms, + Base64.getEncoder().encodeToString(payload)); + + Assertions.assertEquals(SignedByteArrayMessageParser.ParserState.SUCCESS, parser.getParserState()); + Assertions.assertArrayEquals(payload, parser.getPayload()); + Assertions.assertArrayEquals(signingCertificate.getEncoded(), parser.getSigningCertificate().getEncoded()); + Assertions.assertTrue(parser.isSignatureVerified()); + checkSignatureFromParser(parser.getSignature()); + } + + @Test + void parserShouldParseString() throws IOException, CertificateEncodingException { + SignedByteArrayMessageParser parser = new SignedByteArrayMessageParser( + builder.buildAsString()); + + Assertions.assertEquals(SignedByteArrayMessageParser.ParserState.SUCCESS, parser.getParserState()); + Assertions.assertArrayEquals(payload, parser.getPayload()); + Assertions.assertArrayEquals(signingCertificate.getEncoded(), parser.getSigningCertificate().getEncoded()); + Assertions.assertTrue(parser.isSignatureVerified()); + checkSignatureFromParser(parser.getSignature()); + } + + @Test + void parserShouldParseStringWithDetachedPayload() throws IOException, CertificateEncodingException { + SignedByteArrayMessageParser parser = new SignedByteArrayMessageParser( + builder.buildAsString(true), + Base64.getEncoder().encode(payload)); + + Assertions.assertEquals(SignedByteArrayMessageParser.ParserState.SUCCESS, parser.getParserState()); + Assertions.assertArrayEquals(payload, parser.getPayload()); + Assertions.assertArrayEquals(signingCertificate.getEncoded(), parser.getSigningCertificate().getEncoded()); + Assertions.assertTrue(parser.isSignatureVerified()); + checkSignatureFromParser(parser.getSignature()); + } + + @Test + void parserShouldDetectBrokenBase64() { + SignedByteArrayMessageParser parser = new SignedByteArrayMessageParser("randomBadBase64String"); + + Assertions.assertEquals(SignedByteArrayMessageParser.ParserState.FAILURE_INVALID_BASE64, parser.getParserState()); + Assertions.assertFalse(parser.isSignatureVerified()); + } + + @Test + void parserShouldDetectBrokenCms() { + SignedByteArrayMessageParser parser = new SignedByteArrayMessageParser(Base64.getEncoder().encode("randomString".getBytes(StandardCharsets.UTF_8))); + + Assertions.assertEquals(SignedByteArrayMessageParser.ParserState.FAILURE_INVALID_CMS, parser.getParserState()); + Assertions.assertFalse(parser.isSignatureVerified()); + } + + @Test + void parserShouldDetectInvalidCmsContentType() throws Exception { + DigestCalculatorProvider digestCalculatorProvider = new JcaDigestCalculatorProviderBuilder().build(); + + X509CertificateHolder signingCertificateHolder = new X509CertificateHolder(signingCertificate.getEncoded()); + + CMSSignedDataGenerator signedDataGenerator = new CMSSignedDataGenerator(); + + String signingAlgorithmName = + new DefaultAlgorithmNameFinder().getAlgorithmName(signingCertificateHolder.getSignatureAlgorithm()); + + ContentSigner contentSigner = + new JcaContentSignerBuilder(signingAlgorithmName).build(signingKeyPair.getPrivate()); + + SignerInfoGenerator signerInfoGenerator = new JcaSignerInfoGeneratorBuilder(digestCalculatorProvider) + .build(contentSigner, signingCertificate); + + signedDataGenerator.addSignerInfoGenerator(signerInfoGenerator); + + signedDataGenerator.addCertificate(signingCertificateHolder); + + + CMSProcessableByteArray cmsByteArrayMock = spy(new CMSProcessableByteArray(new byte[0])); + when(cmsByteArrayMock.getContentType()).thenReturn(CMSObjectIdentifiers.encryptedData); + + CMSSignedData signedData = signedDataGenerator.generate(cmsByteArrayMock, true); + + SignedByteArrayMessageParser parser = new SignedByteArrayMessageParser(Base64.getEncoder().encode(signedData.getEncoded())); + + Assertions.assertEquals(SignedByteArrayMessageParser.ParserState.FAILURE_INVALID_CMS_BODY, parser.getParserState()); + Assertions.assertFalse(parser.isSignatureVerified()); + } + + @Test + void parserShouldDetectInvalidCertificateAmount() throws Exception { + DigestCalculatorProvider digestCalculatorProvider = new JcaDigestCalculatorProviderBuilder().build(); + + X509CertificateHolder signingCertificateHolder = new X509CertificateHolder(signingCertificate.getEncoded()); + + CMSSignedDataGenerator signedDataGenerator = new CMSSignedDataGenerator(); + + String signingAlgorithmName = + new DefaultAlgorithmNameFinder().getAlgorithmName(signingCertificateHolder.getSignatureAlgorithm()); + + ContentSigner contentSigner = + new JcaContentSignerBuilder(signingAlgorithmName).build(signingKeyPair.getPrivate()); + + SignerInfoGenerator signerInfoGenerator = new JcaSignerInfoGeneratorBuilder(digestCalculatorProvider) + .build(contentSigner, signingCertificate); + + signedDataGenerator.addSignerInfoGenerator(signerInfoGenerator); + + signedDataGenerator.addCertificate(signingCertificateHolder); + signedDataGenerator.addCertificate(signingCertificateHolder); + + CMSProcessableByteArray cmsByteArray = new CMSProcessableByteArray(payload); + CMSSignedData signedData = signedDataGenerator.generate(cmsByteArray, true); + + SignedByteArrayMessageParser parser = new SignedByteArrayMessageParser(Base64.getEncoder().encode(signedData.getEncoded())); + + Assertions.assertEquals(SignedByteArrayMessageParser.ParserState.FAILURE_CMS_SIGNING_CERT_INVALID, parser.getParserState()); + Assertions.assertFalse(parser.isSignatureVerified()); + } + + @Test + void parserShouldDetectInvalidSignerInfoAmount() throws Exception { + DigestCalculatorProvider digestCalculatorProvider = new JcaDigestCalculatorProviderBuilder().build(); + + X509CertificateHolder signingCertificateHolder = new X509CertificateHolder(signingCertificate.getEncoded()); + + CMSSignedDataGenerator signedDataGenerator = new CMSSignedDataGenerator(); + + String signingAlgorithmName = + new DefaultAlgorithmNameFinder().getAlgorithmName(signingCertificateHolder.getSignatureAlgorithm()); + + ContentSigner contentSigner = + new JcaContentSignerBuilder(signingAlgorithmName).build(signingKeyPair.getPrivate()); + + SignerInfoGenerator signerInfoGenerator = new JcaSignerInfoGeneratorBuilder(digestCalculatorProvider) + .build(contentSigner, signingCertificate); + + signedDataGenerator.addSignerInfoGenerator(signerInfoGenerator); + signedDataGenerator.addSignerInfoGenerator(signerInfoGenerator); + + signedDataGenerator.addCertificate(signingCertificateHolder); + + CMSProcessableByteArray cmsByteArray = new CMSProcessableByteArray(payload); + CMSSignedData signedData = signedDataGenerator.generate(cmsByteArray, true); + + SignedByteArrayMessageParser parser = new SignedByteArrayMessageParser(Base64.getEncoder().encode(signedData.getEncoded())); + + Assertions.assertEquals(SignedByteArrayMessageParser.ParserState.FAILURE_CMS_SIGNER_INFO, parser.getParserState()); + Assertions.assertFalse(parser.isSignatureVerified()); + } + + private void checkSignatureFromParser(String signature) throws CertificateEncodingException, IOException { + SignedByteArrayMessageParser parser = new SignedByteArrayMessageParser( + signature, Base64.getEncoder().encodeToString(payload)); + + Assertions.assertEquals(SignedByteArrayMessageParser.ParserState.SUCCESS, parser.getParserState()); + Assertions.assertArrayEquals(payload, parser.getPayload()); + Assertions.assertEquals(new X509CertificateHolder(signingCertificate.getEncoded()), parser.getSigningCertificate()); + Assertions.assertTrue(parser.isSignatureVerified()); + Assertions.assertEquals(signature, parser.getSignature()); + } +} +