I am using Mimekit to do rsa pss cms sign, to simulate this openssl command openssl cms -sign -in keys.zip -binary -nodetach -signer selfsigned.crt -inkey keypair.pem -out keys.zip.signed -keyopt rsa_padding_mode:pss
public byte[] Sign(byte[] data, byte[] signCert, byte[] privateKey, CmsKeyOpt cmsKeyOpt)
{
MimeMessage message = new MimeMessage
{
Body = new MimePart()
{
Content = new MimeContent(new MemoryStream(data)),
ContentTransferEncoding = ContentEncoding.Binary,
},
};
// Load private key from byte array
StreamReader stream = new StreamReader(new MemoryStream(privateKey), Encoding.Default);
AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)new PemReader(stream).ReadObject();
// load certifacte from byte array
X509CertificateParser parser = new X509CertificateParser();
X509Certificate certificate = parser.ReadCertificate(signCert);
// Create RSA PSS CMS signer
CmsSigner signer = new CmsSigner(certificate, keyPair.Private)
{
RsaSignaturePadding = RsaSignaturePadding.Pss,
DigestAlgorithm = DigestAlgorithm.Sha256,
};
// Create BouncyCastle Secure MimeContext
BouncyCastleSecureMimeContext ctx = new TemporarySecureMimeContext();
ctx.EncapsulatedSign(signer, new MemoryStream(data));
// Get signed message body and override it
message.Body = MultipartSigned.Create(ctx, signer, message.Body);
byte[] singedData;
using (var memory = new MemoryStream())
{
message.WriteTo(memory);
singedData = memory.ToArray();
}
return singedData;
}
And everything works fine, my question how I can achieve verify by Mimekit/BouncyCastle, to simulate this openssl command openssl cms -verify -in keys.zip.signed.dec -CAfile selfsigned.crt -out keys_dec_unsigned.zip
I tried this but I got exception System.NotSupportedException: 'SQLite is not available on pkcs7.Verify(out original) line
public byte[] Verify(byte[] data, byte[] signCert, byte[] privateKey)
{
bool valid = false;
// Load private key from byte array
StreamReader stream = new StreamReader(new MemoryStream(privateKey), Encoding.Default);
AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)new PemReader(stream).ReadObject();
// load certifacte from byte array
X509CertificateParser parser = new X509CertificateParser();
X509Certificate certificate = parser.ReadCertificate(signCert);
MimeMessage message = MimeMessage.Load(new MemoryStream(data));
// Create BouncyCastle Secure MimeContext
MimeEntity original;
ApplicationPkcs7Mime pkcs7 = message.Body as ApplicationPkcs7Mime;
if (pkcs7 != null && pkcs7.SecureMimeType == SecureMimeType.SignedData)
{
foreach (var signature in pkcs7.Verify(out original))
{
try
{
valid = signature.Verify();
}
catch (DigitalSignatureVerifyException)
{
// There was an error verifying the signature.
}
}
}
byte[] res = { 0 };
return res;
}
Is there any guide I can follow to verify and return the data to it's origin forum, or an example?
By default, MimeKit tries to instantiate a S/MIME verify context based on its SQLite backend for certificate storage.
If you don't have SQLite installed and/or don't have your certificates stored in the SQLite database that MimeKit will maintain for you, then you need to either register a different backend for certificate storage/retrieval or instantiate your own S/MIME context (like you did for signing).
MimeKit comes with 2 options:
Once you decide on which of those to use, you will need to import your certificate(s) into the context (which will import them into the appropriate backend storage location).
Then, after that, you can do this:
Update:
This devolved into misuse of the openssl cms sign command and an inability to verify the signature using MimeKit because MimeKit expects the encapsulated signed data to be in MIME format as it is supposed to be according to the S/MIME specifications.
Here's the deal:
The openssl cms sign command can be used to sign arbitrary data and the corresponding openssl cms verify command can be used to verify such signed output. HOWEVER, it is only valid S/MIME if the original content signed by the openssl cms sign command is/was in MIME format.
MimeKit expects valid S/MIME because it is... surprise, surprise... a MIME library.
As you might have noticed, the Verify() method has an output parameter (
out MimeEntity originalEntity
). This means that the Verify() method extracts the encapsulated content from within the signed data and parses it into a MIME entity and returns it to you, the caller, in the form of said output parameter.If the encapsulated content is not in MIME format, then, obviously, the parser will fail to parse it.
Additional background with how CMS signatures work:
When you sign content using the CMS signing routine (e.g.
openssl cms sign ...
), it produces something that looks a bit like this:The base64 content in the above MIME part includes both the original content and the CMS signature data (i.e. a list of signatures).
Therefor, MimeKit's Verify() method needs to separate the signatures from the original content after base64 decoding it. MimeKit then returns to you, the caller, the original content (which it expects to be in MIME format) and the list of signatures that you can then independently verify the authenticity of.
When I kept repeating myself that it mattered what format the
file.bin
file was in when you signed the content, I was not saying that openssl or MimeKit needed to parsefile.bin
when verifying the signature, I was saying thatfile.bin
has exactly the same content as what would be extracted from the base64 blob in the S/MIME output produced byopenssl cms sign ...
and therefore, it needs to be in MIME format.