I'm investigating upgrading an application from SHA1 as the default PKCS#7 SignedData digest algorithm to stronger digests such as SHA256, in ways that preserve backwards compatibility for signature verifiers which do not support digest algorithms other than SHA1. I want to check my understanding of the PKCS#7 format and available options.
What think I want to do is digest message content with both SHA1 and SHA256 (or more generally, a set of digest algorithms) such that older applications can continue to verify via the SHA1, and upgraded applications can begin verifying via the SHA256 (more generally, the strongest digest provided), ignoring the weaker algorithm(s). [If there is a better approach, please let me know.]
It appears that within the PKCS#7 standard, the only way to provide multiple digests is to provide multiple SignerInfos, one for each digest algorithm. Unfortunately, this would seem to lead to a net decrease in security, as an attacker is able to strip all but the the SignerInfo with the weakest digest algorithm, which alone will still form a valid signature. Is this understanding correct?
If so, my idea was to use custom attributes within the authenticatedAttributes field of SignerInfo to provide additional message-digests for the additional digest algorithms (leaving SHA1 as the "default" algorithm for backwards compatibility). Since this field is authenticated as a single block, this would prevent the above attack. Does this seem like a viable approach? Is there a way to accomplish this or something similar without going outside of the PKCS standard?
Yes, you are right, in the current CMS RFC it says about the message digest attribute that
The SignedAttributes in a signerInfo MUST include only one instance of the message-digest attribute. Similarly, the AuthAttributes in an AuthenticatedData MUST include only one instance of the message-digest attribute.
So it is true that the only way to provide multiple message digest values using the standard signed attributes is to provide several signedInfos.
And yes, any security system is as strong as its weakest link, so theoretically you will not gain anything by adding a SignedInfo with SHA-256 if you also still accept SHA-1 - as you said, the stronger signatures can always be stripped.
Your scheme with custom attributes is a bit harder to break - but there is still a SHA-1 hash floating around that can be attacked. It's no longer as easy as just stripping the attribute - as it's covered by the signature. But:
There is also the digest algorithm that is used to digest the signed attributes which serves as the basis of the final signature value. What do you intend to use there? SHA-256 or SHA-1? If it's SHA-1, then you will be in the same situation as before:
If I can produce collisions for SHA-1, then I would strip off your custom SHA-256 attribute and forge the SHA-1 attribute in such a way that the final SHA-1 digest for the signature adds up again. This shows that there will only be a gain in security if the signature digest algorithm would be SHA-256, too, but I'm guessing this is no option since you want to stay backwards-compatible.
What I would suggest in your situation is to keep using SHA-1 throughout but apply an RFC 3161-compliant timestamp to your signature as an unsigned attribute. Those timestamps are in fact signatures of their own. The good thing is you can use SHA-256 for the message imprint there and often the timestamp server applies its signature using the same digest algorithm you provided. Then reject any signature that either does not contain such a timestamp or contains only timestamps with message imprint/signature digest algorithms weaker than SHA-256.
What's the benefit of this solution? Your legacy applications should check for the presence of an unsigned timestamp attribute and if a strong digest was used for it, but otherwise ignore them and keep on verifying the signatures the same way they did before. New applications on the other hand will verify the signature but additionally verify the timestamp, too. As the timestamp signature "covers" the signature value, there's no longer a way for an attacker to forge the signature. Although the signature uses SHA-1 for the digest values an attacker would have to be able to break break the stronger digest of the timestamp, too.
An additional benefit of a timestamp is that you can associate a date of production with your signature - you can safely claim that the signature has been produced before the time of the timestamp. So even if a signature certificate were to be revoked, with the help of the timestamp you could still precisely decide whether to reject or accept a signature based on the time that the certificate was revoked. If the certificate was revoked after the timestamp, then you can accept the signature (add a safety margin (aka "grace period") - it takes some time until the information gets published), if it was revoked prior to the time of the timestamp then you want to reject the signature.
A last benefit of timestamps is that you can renew them over time if certain algorithms get weak. You could for example apply a new timestamp every 5-10 years using up-to-date algorithms and have the new timestamps cover all of the older signatures (including older timestamps). This way weak algorithms are then covered by the newer, stronger timestamp signature. Have a look at CAdES (there exists also an RFC, but it's outdated by now), which is based on CMS and makes an attempt at applying these strategies to provide for long-term archiving of CMS signatures.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With