Skip to content

Commit

Permalink
Fix: Signature Calculation of Signed CMS
Browse files Browse the repository at this point in the history
  • Loading branch information
f11h authored May 10, 2021
2 parents f73877c + c30ddf6 commit 8bd9504
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
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.operator.OperatorCreationException;
Expand Down Expand Up @@ -103,7 +104,7 @@ public SignedCertificateMessageParser(@NonNull byte[] cmsMessage) {
* The result of parsing process will be immediately available.
*
* @param cmsSignature base64 encoded detached CMS signature bytes.
* @param cmsPayload base64 encoded CMS message payload.
* @param cmsPayload base64 encoded CMS message payload.
*/
public SignedCertificateMessageParser(@NonNull byte[] cmsSignature, @NonNull byte[] cmsPayload) {
raw = cmsSignature;
Expand All @@ -128,7 +129,7 @@ public SignedCertificateMessageParser(@NonNull String cmsMessage) {
* 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.
* @param cmsPayload base64 encoded CMS message payload string.
*/
public SignedCertificateMessageParser(@NonNull String cmsSignature, @NonNull String cmsPayload) {
raw = cmsSignature.getBytes(StandardCharsets.UTF_8);
Expand All @@ -141,7 +142,7 @@ public SignedCertificateMessageParser(@NonNull String cmsSignature, @NonNull Str
* 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.
* @param cmsPayload base64 encoded CMS message payload string.
*/
public SignedCertificateMessageParser(@NonNull byte[] cmsSignature, @NonNull String cmsPayload) {
raw = cmsSignature;
Expand All @@ -154,7 +155,7 @@ public SignedCertificateMessageParser(@NonNull byte[] cmsSignature, @NonNull Str
* 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.
* @param cmsPayload base64 encoded CMS message payload bytes.
*/
public SignedCertificateMessageParser(@NonNull String cmsSignature, @NonNull byte[] cmsPayload) {
raw = cmsSignature.getBytes(StandardCharsets.UTF_8);
Expand Down Expand Up @@ -218,6 +219,14 @@ private void afterPropertiesSet() {
}
signingCertificate = certificateHolderCollection.iterator().next();

// Try to extract detached CMS Signature
try {
signature = Base64.getEncoder().encodeToString(repackToDetachedCms(cmsSignedData).getEncoded());
} catch (IOException | CMSException e) {
signature = null;
log.error("Failed to repack CMS to get detached signature.");
}

// Get signer information and verify signature
if (cmsSignedData.getSignerInfos().size() != 1) {
log.error("Signed Message contains more than 1 signer information");
Expand All @@ -229,14 +238,30 @@ private void afterPropertiesSet() {
signatureVerified = signerInformation.verify(
new JcaSimpleSignerInfoVerifierBuilder().build(signingCertificate)
);
signature = Base64.getEncoder().encodeToString(signerInformation.getSignature());
} catch (CMSException | OperatorCreationException | CertificateException e) {
log.error("Failed to validate Signature");
}

parserState = ParserState.SUCCESS;
}

/**
* Recreates a CMS without encapsulated Data.
*
* @param input input CMS Message
* @return CMS message without encapsulated data.
* @throws CMSException if repacking fails.
*/
private CMSSignedData repackToDetachedCms(CMSSignedData input) throws CMSException {
CMSSignedDataGenerator cmsGenerator = new CMSSignedDataGenerator();
cmsGenerator.addCertificates(input.getCertificates());
cmsGenerator.addSigners(input.getSignerInfos());
cmsGenerator.addAttributeCertificates(input.getAttributeCertificates());
cmsGenerator.addCRLs(input.getCRLs());

return cmsGenerator.generate(input.getSignedContent(), false);
}

public enum ParserState {
IDLE,
SUCCESS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,50 +74,56 @@ void setupTestData() throws Exception {

@Test
void parserShouldParseByteArray() throws IOException, CertificateEncodingException {
SignedCertificateMessageParser parser = new SignedCertificateMessageParser(Base64.getEncoder().encode(builder.build()));
SignedCertificateMessageParser parser = new SignedCertificateMessageParser(
Base64.getEncoder().encode(builder.build()));

Assertions.assertEquals(SignedCertificateMessageParser.ParserState.SUCCESS, parser.getParserState());
Assertions.assertArrayEquals(payloadCertificate.getEncoded(), parser.getPayloadCertificate().getEncoded());
Assertions.assertArrayEquals(signingCertificate.getEncoded(), parser.getSigningCertificate().getEncoded());
Assertions.assertTrue(parser.isSignatureVerified());
Assertions.assertNotNull(parser.getSignature());
checkSignatureFromParser(parser.getSignature());
}

@Test
void parserShouldParseByteArrayWithDetachedPayload() throws IOException, CertificateEncodingException {
byte[] cms = Base64.getEncoder().encode(builder.build(true));

SignedCertificateMessageParser parser = new SignedCertificateMessageParser(
Base64.getEncoder().encode(builder.build(true)),
cms,
Base64.getEncoder().encode(payloadCertificate.getEncoded()));

Assertions.assertEquals(SignedCertificateMessageParser.ParserState.SUCCESS, parser.getParserState());
Assertions.assertArrayEquals(payloadCertificate.getEncoded(), parser.getPayloadCertificate().getEncoded());
Assertions.assertArrayEquals(signingCertificate.getEncoded(), parser.getSigningCertificate().getEncoded());
Assertions.assertTrue(parser.isSignatureVerified());
Assertions.assertNotNull(parser.getSignature());
Assertions.assertEquals(new String(cms), parser.getSignature());
}

@Test
void parserShouldParseByteArrayWithDetachedPayloadAsString() throws IOException, CertificateEncodingException {
byte[] cms = Base64.getEncoder().encode(builder.build(true));

SignedCertificateMessageParser parser = new SignedCertificateMessageParser(
Base64.getEncoder().encode(builder.build(true)),
cms,
Base64.getEncoder().encodeToString(payloadCertificate.getEncoded()));

Assertions.assertEquals(SignedCertificateMessageParser.ParserState.SUCCESS, parser.getParserState());
Assertions.assertArrayEquals(payloadCertificate.getEncoded(), parser.getPayloadCertificate().getEncoded());
Assertions.assertArrayEquals(signingCertificate.getEncoded(), parser.getSigningCertificate().getEncoded());
Assertions.assertTrue(parser.isSignatureVerified());
Assertions.assertNotNull(parser.getSignature());
checkSignatureFromParser(parser.getSignature());
}

@Test
void parserShouldParseString() throws IOException, CertificateEncodingException {
SignedCertificateMessageParser parser = new SignedCertificateMessageParser(builder.buildAsString());
SignedCertificateMessageParser parser = new SignedCertificateMessageParser(
builder.buildAsString());

Assertions.assertEquals(SignedCertificateMessageParser.ParserState.SUCCESS, parser.getParserState());
Assertions.assertArrayEquals(payloadCertificate.getEncoded(), parser.getPayloadCertificate().getEncoded());
Assertions.assertArrayEquals(signingCertificate.getEncoded(), parser.getSigningCertificate().getEncoded());
Assertions.assertTrue(parser.isSignatureVerified());
Assertions.assertNotNull(parser.getSignature());
checkSignatureFromParser(parser.getSignature());
}

@Test
Expand All @@ -130,7 +136,7 @@ void parserShouldParseStringWithDetachedPayload() throws IOException, Certificat
Assertions.assertArrayEquals(payloadCertificate.getEncoded(), parser.getPayloadCertificate().getEncoded());
Assertions.assertArrayEquals(signingCertificate.getEncoded(), parser.getSigningCertificate().getEncoded());
Assertions.assertTrue(parser.isSignatureVerified());
Assertions.assertNotNull(parser.getSignature());
checkSignatureFromParser(parser.getSignature());
}

@Test
Expand Down Expand Up @@ -274,5 +280,16 @@ void parserShouldDetectInvalidSignerInfoAmount() throws Exception {
Assertions.assertEquals(SignedCertificateMessageParser.ParserState.FAILURE_CMS_SIGNER_INFO, parser.getParserState());
Assertions.assertFalse(parser.isSignatureVerified());
}

private void checkSignatureFromParser(String signature) throws CertificateEncodingException, IOException {
SignedCertificateMessageParser parser = new SignedCertificateMessageParser(
signature, Base64.getEncoder().encodeToString(payloadCertificate.getEncoded()));

Assertions.assertEquals(SignedCertificateMessageParser.ParserState.SUCCESS, parser.getParserState());
Assertions.assertEquals(new X509CertificateHolder(payloadCertificate.getEncoded()), parser.getPayloadCertificate());
Assertions.assertEquals(new X509CertificateHolder(signingCertificate.getEncoded()), parser.getSigningCertificate());
Assertions.assertTrue(parser.isSignatureVerified());
Assertions.assertEquals(signature, parser.getSignature());
}
}

0 comments on commit 8bd9504

Please sign in to comment.