Discuss this help topic in SecureBlackbox Forum
S/MIME: Analyze and decrypt/verify message
When a message is received, you need to parse it, check if the message is secured, and if it is, decrypt and/or verify it. To do this
There are several combinations of methods, how the message can be encrypted and/or signed. Suppose you have created a message with a text part and an attachment, then encrypted and signed it in different ways. Below all possible combinations of signatures and encryptions are listed and explained.
[0] multipart/signed [1] multipart/mixed [2] text/plain [2] application/octet-stream (attachment) [1] application/pkcs7-signature (attachment)
[0] application/pkcs7-mime; smime-type=signed-data (attachment) Decoded part: [0] multipart/mixed [1] text/plain [1] application/octet-stream (attachment)
[0] application/pkcs7-mime; smime-type=enveloped-data (attachment) Decoded part: [0] multipart/mixed [1] text/plain [1] application/octet-stream (attachment)
[0] application/pkcs7-mime; smime-type=enveloped-data (attachment) Decoded part: [0] multipart/signed (encrypted) [1] multipart/mixed (encrypted)(signed) [2] text/plain (encrypted)(signed) [2] application/octet-stream (attachment)(encrypted)(signed) [1] application/pkcs7-signature (attachment)(encrypted)
[0] application/pkcs7-mime; smime-type=enveloped-data (attachment) Decoded part: [0] application/pkcs7-mime; smime-type=signed-data (attachment)(encrypted) Decoded part 2: [0] multipart/mixed (encrypted)(signed) [1] text/plain (encrypted)(signed) [1] application/octet-stream (attachment)(encrypted)(signed)Also, messages could be first encrypted, then signed. Both signature types could be used to sign the encrypted part.
[0] multipart/signed [1] application/pkcs7-mime; smime-type=enveloped-data (attachment)(signed) [1] application/pkcs7-signature (attachment)Next it is needed to check Message.MainPart.GetPart(0).MessagePartHandler property. If it is not null/nil and it is of class TElMessagePartHandlerSMime, call its MessagePartHandler.DecoderIsCrypted() method to determine if the part is encrypted. Then assign a certificate storage which contains the private key needed to decrypt the data, as described in section 3 above, and call the MessagePartHandler.Decode() method to decrypt the message data. After this, the DecodedPart property will contain the decrypted message part:
[0] multipart/mixed (signed)(encrypted) [1] text/plain (signed)(encrypted) [1] application/octet-stream (attachment)(signed)(encrypted)
[0] application/pkcs7-mime; smime-type=signed-data (attachment)Decoded part:
[0] application/pkcs7-mime; smime-type=enveloped-data (attachment)(signed)Decoded part 2:
[0] multipart/mixed (signed)(encrypted) [1] text/plain (signed)(encrypted) [1] application/octet-stream (attachment)(signed)(encrypted)
Below is a code snippet that analyzes a message, decrypts it if necessary, verifies its signature (if present) and returns the data part for further processing:
C#:
// The method processes the passed message, decrypts its data (if the message
// is encrypted), checks included signature (if any) and returns the decoded
// message part which should be handled as the main part of an unprotected message
TElMessagePart ProcessSMimeMessage(TElMessage message)
{
if (message == null || message.MainPart == null)
throw new ArgumentNullException();
// check if the message is S/MIME protected
if (!(message.MainPart.MessagePartHandler is TElMessagePartHandlerSMime))
{
// the message is not S/MIME protected, so just return its main part
return message.MainPart;
}
// typecast the handler for further use
TElMessagePartHandlerSMime handler =
(TElMessagePartHandlerSMime)message.MainPart.MessagePartHandler;
// check if the message is encrypted
if (handler.DecoderIsCrypted())
{
TElMessagePart decryptedPart = ProcessEncryptedMessage(message, handler);
// check if the decrypted message part is signed also
if ((decryptedPart != null) &&
(decryptedPart.MessagePartHandler is TElMessagePartHandlerSMime))
{
handler = (TElMessagePartHandlerSMime)decryptedPart.MessagePartHandler;
if (handler.DecoderIsSigned())
return ProcessSignedMessage(decryptedPart);
}
return decryptedPart;
}
else
// check if the message is signed
if (handler.DecoderIsSigned())
{
TElMessagePart signedPart = ProcessSignedMessage(message.MainPart);
// check if the signed part is encrypted also
if ((signedPart != null) &&
(signedPart.MessagePartHandler is TElMessagePartHandlerSMime))
{
handler = (TElMessagePartHandlerSMime)signedPart.MessagePartHandler;
if (handler.DecoderIsCrypted())
return ProcessEncryptedMessage(message, handler);
}
return signedPart;
}
// something goes wrong
return null;
}
// The method processes the encrypted message part, decrypts it and
// returns the decrypted message part
TElMessagePart ProcessEncryptedMessage(TElMessage message,
TElMessagePartHandlerSMime handler)
{
if (message == null || handler == null)
throw new ArgumentNullException();
// check message's To_ and Cc address lists and load the appropriate
// certificate and its private key for message decryption
TElMemoryCertStorage decryptingStorage = new TElMemoryCertStorage();
...
handler.CertificatesStorage = decryptingStorage;
int err = handler.Decode(false);
if (err != 0)
throw new Exception(String.Format(
"Failed to decrypt the message: (code {0}) {1}",
err, handler.ErrorText));
return handler.DecodedPart;
}
TElMessagePart ProcessSignedMessage(TElMessagePart part)
{
if (part == null)
throw new ArgumentNullException();
// check if the part is protected
if (part.MessagePartHandler == null)
return part;
// check if the part is S/MIME protected
if (!(part.MessagePartHandler is TElMessagePartHandlerSMime))
throw new Exception("Invalid message part handler type");
TElMessagePartHandlerSMime handler =
(TElMessagePartHandlerSMime)part.MessagePartHandler;
// check if the message part is signed
if (!handler.DecoderIsSigned())
{
if (handler.DecodedPart != null)
return handler.DecodedPart;
return part;
}
// check if the message part is signed with "clear format" signature
if (handler.DecoderIsSignedOnly())
{
// check if the part is really a "multipart/signed" part
if (!part.IsMultipart() ||
!String.Equals(part.ContentSubtype, "signed",
StringComparison.OrdinalIgnoreCase) ||
(part.PartsCount != 2))
throw new Exception("Invalid message part type or invalid signature structure");
// is the signature verified ok?
if (!handler.DecoderSignIsCorrectly)
throw new Exception(
String.Format("Failed to verify the signature: {0}",
handler.ErrorText));
if (handler.DecoderSignCertStorage != null)
{
// verify the signing certificate(s) available in the storage
}
// check if the part #0 is the data part
TElMessagePart dataPart = part.GetPart(0);
if (!dataPart.IsApplication() ||
!String.Equals(dataPart.ContentSubtype, "pkcs7-signature",
StringComparison.OrdinalIgnoreCase))
return dataPart;
// check if the part #1 is the data part
dataPart = part.GetPart(1);
if (!dataPart.IsApplication() ||
!String.Equals(dataPart.ContentSubtype, "pkcs7-signature",
StringComparison.OrdinalIgnoreCase))
throw new Exception("Invalid signature structure");
return dataPart;
}
// the message part is signed with "enveloped format" signature;
// it is needed to decode the enveloped data
int err = handler.Decode(false);
if (!handler.DecoderSignIsCorrectly)
throw new Exception(
String.Format("Failed to verify the signature: (code {0}) {1}",
err, handler.ErrorText));
if (handler.DecoderSignCertStorage != null)
{
// verify signing certificate(s) available in the storage
}
return handler.DecodedPart;
}