Digital Signature

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

I have a .NET service sending mails using CDOEX.

These mails I need to sign. I got a tip that I should use CAPICOM. That
worked
fine sending a mail with signature.

BUT the problem is that I have to type the password for my certificate every
time my program signs a mail :o(

My program is a windows service running on a server so typing passwords is
bad.

Then I got a new tip, I should use CryptoAPI instead of CAPICOM.

I think I have solved the CryptoAPI mystic and got my certificate, signed my
body of the mail and got the hash for it. But I don't know how to get my
signed code into the mail?

The code looks like this, and gives this error when the outlook client
reseive the mail:

Error: Can't open this item. Your Digital ID name can not be found by the
underlying security system.

This takes a byte array that I send from my CryptoAPI code.
Code:

private void SendMail(byte[] byteSignature)
{
CDO.IBodyPart oBodyPart;
ADODB.Fields cFields;
ADODB.Stream oStream;

// set sender, recipient, and subject.
oMessage = new CDO.Message();

oMessage.To = "(e-mail address removed)";
oMessage.Subject = "Test Mail";
oMessage.Fields["urn:schemas:mailheader:date"].Value = DateTime.UtcNow;
oMessage.Fields.Update();

oMessage.From = "(e-mail address removed)";

oBodyPart = oMessage.BodyPart.AddBodyPart(1);
cFields = oBodyPart.Fields;

cFields["urn:schemas:mailheader:content-type"].Value =
CDO.CdoContentTypeValues.cdoTextPlain;
cFields.Update();

oStream = oBodyPart.GetDecodedContentStream();
oStream.WriteText("Hello this is some test text",0);
oStream.Flush();

//
//
// Start the new message
//
//
CDO.Message oSignedMsg = new CDO.Message();
CDO.IBodyPart oBodyPart2;
ADODB.Fields cFields2;
ADODB.Stream oStream2;

oSignedMsg.From = "(e-mail address removed)";

// this is to be a clear text signed message so we need to copy the
interesting
// parts (sender, recipient, and subject) into the new header
oSignedMsg.To = oMessage.To;
oSignedMsg.CC = oMessage.CC;
oSignedMsg.Subject = oMessage.Subject;

oBodyPart2 = oSignedMsg.BodyPart.AddBodyPart(1);
cFields2 = oBodyPart2.Fields;

cFields2["urn:schemas:mailheader:content-type"].Value =
oMessage.BodyPart.BodyParts[1].Fields["urn:schemas:mailheader:content-type"].Value;
cFields2.Update();

// Attach the signature and let CDO base64 encode it
oBodyPart2 = oSignedMsg.BodyPart.AddBodyPart(1);
cFields2 = oBodyPart2.Fields;
oBodyPart2.Fields["urn:schemas:mailheader:content-type"].Value =
"application/x-pkcs7-signature\rName = " + '\u0022' + "smime.p7s" + '\u0022'
+ "";

oBodyPart2.Fields["urn:schemas:mailheader:content-transfer-encoding"].Value =
"base64";
oBodyPart2.Fields["urn:schemas:mailheader:content-disposition"].Value =
"attachment;\rFileName=" + '\u0022' + "smime.p7s" + '\u0022' + "";
cFields2.Update();
//
oStream2 = oBodyPart2.GetDecodedContentStream();
oStream2.Type = ADODB.StreamTypeEnum.adTypeBinary;
oStream2.Write (byteSignature);
oStream2.Flush();

// Set the messages content type, this needs to be done last to ensure
it is not changed when we add the BodyParts

oSignedMsg.Fields["urn:schemas:mailheader:content-type"].Value =
"multipart/signed;\rprotocol=" + '\u0022' + "application/x-pkcs7-signature" +
'\u0022' + ";\rmicalg=SHA1;";

oSignedMsg.Fields.Update();

oMessage = oSignedMsg;
oMessage.Send();
}
 
Hi

We have reviewed this issue and are currently researching on it. We will
update you ASAP. Thanks for your patience!

Kevin Yu
=======
"This posting is provided "AS IS" with no warranties, and confers no
rights."
 
Hello,
Based on my understanding, you are trying to send encrypted email from
UserA to UserB, but get following error in UserB.
Error: Can't open this item. Your Digital ID name can not be found by the
underlying security system.
This error indicates that the recipient(s) that does not have a Digital ID
should either obtain one or an unencrypted message should be sent. The
certificates you have for the recipients listed are not valid. They may
have expired, may be the wrong type, or may not be supported by your
security policy. You will need to get/set new certificates from/to the
recipients so that the encrypted message could be decrypted successfully.


Best regards,
Rhett Gong [MSFT]
Microsoft Online Partner Support

This posting is provided "AS IS" with no warranties, and confers no rights.
Please reply to newsgroups only. Thanks.
 
Hello,

My Certificate is working perfect.

If I use CAPICOM to send a Sign Mail there is no problem.

To test it I send the mail to my self and I worked perfect with CAPICOM, but
with my code using CryptoAPI + the code here I got the error.

I had talked to some CryptoAPI guys and they say that my CryptoAPI code
should be fine but if you think my Mail code is correct you can se my
CryptoAPI code.

Best Regards

Kim

Rhett Gong said:
Hello,
Based on my understanding, you are trying to send encrypted email from
UserA to UserB, but get following error in UserB.
Error: Can't open this item. Your Digital ID name can not be found by the
underlying security system.
This error indicates that the recipient(s) that does not have a Digital ID
should either obtain one or an unencrypted message should be sent. The
certificates you have for the recipients listed are not valid. They may
have expired, may be the wrong type, or may not be supported by your
security policy. You will need to get/set new certificates from/to the
recipients so that the encrypted message could be decrypted successfully.


Best regards,
Rhett Gong [MSFT]
Microsoft Online Partner Support

This posting is provided "AS IS" with no warranties, and confers no rights.
Please reply to newsgroups only. Thanks.
 
Please post out your code and let me know which line fails.

Thanks,
Rhett Gong [MSFT]
Microsoft Online Partner Support

This posting is provided "AS IS" with no warranties, and confers no rights.
Please reply to newsgroups only. Thanks.
 
There is no error exception in the code, it compiles and run perfectly no
errors.

The error comes when the receiver of the mail tryes to open the mail and
outlook want to open the mail.

The Crypto code:

Just put it into a C# form:

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
//using System.Text;

namespace SecuritySample
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
CDO.Message oMessage = new CDO.Message();

/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// TODO: Add any constructor code after InitializeComponent call
//
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}

private void Form1_Load(object sender, System.EventArgs e)
{
SendMail();
}

const uint CERT_KEY_PROV_INFO_PROP_ID = 2;
const uint ALG_CLASS_HASH = (4 << 13);
const uint ALG_TYPE_ANY = 0;
const uint ALG_SID_SHA = 4;
const uint CALG_SHA = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA);
const uint AT_SIGNATURE = 2;
const uint PP_KEYEXCHANGE_PIN = 32;
const uint PP_SIGNATURE_PIN = 33;
const uint CERT_KEY_PROV_HANDLE_PROP_ID = 1;
const uint CERT_STORE_NO_CRYPT_RELEASE_FLAG = 1;
const uint X509_ASN_ENCODING = 0x00000001;
const uint PKCS_7_ASN_ENCODING = 0x00010000;

[DllImport("crypt32.dll", SetLastError=true)]
public static extern IntPtr CertDuplicateCertificateContext(
IntPtr pCertContext
);

[DllImport("crypt32.dll", SetLastError=true)]
public static extern IntPtr CertEnumCertificatesInStore(
IntPtr hCertStore,
IntPtr pPrevCertContext
);


[DllImport("crypt32.dll", SetLastError=true)]
public static extern IntPtr CertOpenSystemStore(
IntPtr hprov,
string szSubsystemProtocol
);

[DllImport("crypt32.dll", SetLastError=true)]
public static extern bool CertGetCertificateContextProperty(
IntPtr pCertContext,
uint dwPropId,
IntPtr pvData,
ref uint pcbData);

[DllImport("Advapi32.dll", SetLastError=true)]
public static extern bool CryptAcquireContext(
ref IntPtr phProv,
string pszContainer,
string pszProvider,
uint dwProvType,
uint dwFlags);

[DllImport("Advapi32.dll", SetLastError=true)]
public static extern bool CryptCreateHash(
IntPtr hProv,
int Algid,
IntPtr hKey,
uint dwFlags,
ref IntPtr phHash);

[DllImport("Advapi32.dll", SetLastError=true)]
public static extern bool CryptHashData(
IntPtr hHash,
IntPtr pbData,
uint dwDataLen,
uint dwFlags);

[DllImport("Advapi32.dll", SetLastError=true)]
public static extern bool CryptSetProvParam(
IntPtr hProv,
uint dwParam,
IntPtr pbData,
uint dwFlags
);

[DllImport("Advapi32.dll", SetLastError=true)]
public static extern bool CryptSignHash(
IntPtr hHash,
uint dwKeySpec,
string sDescription,
uint dwFlags,
IntPtr pbSignature,
ref uint pdwSigLen
);

[DllImport("Advapi32.dll", SetLastError=true)]
public static extern bool CryptDestroyHash(
IntPtr hHash
);


[DllImport("Advapi32.dll", SetLastError=true)]
public static extern bool CryptReleaseContext(
IntPtr hProv,
uint dwFlags
);

[DllImport("Crypt32.dll", SetLastError=true)]
public static extern bool CertSetCertificateContextProperty(
IntPtr pCertContext,
uint dwPropId,
uint dwFlags,
IntPtr pvData
);

[StructLayoutAttribute(LayoutKind.Sequential, CharSet=CharSet.Auto)]
internal struct CRYPT_KEY_PROV_INFO
{
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszContainerName;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszProvName;

// public IntPtr pwszContainerName;
// public IntPtr pwszProvName;

public uint dwProvType;
public uint dwFlags;
public uint cProvParam;
public uint rgProvParam;
public uint dwKeySpec;
}

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct CRYPTOAPI_BLOB
{
public int cbData;
public IntPtr pbData;
}

[StructLayoutAttribute(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct CRYPT_ALGORITHM_IDENTIFIER
{
[MarshalAs(UnmanagedType.LPStr)]
public string pszObjId;
public CRYPTOAPI_BLOB Parameters;
}

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct CRYPT_SIGN_MESSAGE_PARA
{
public UInt32 cbSize;
public UInt32 dwMsgEncodingType;
public IntPtr pSigningCert;
public CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;
public IntPtr pvHashAuxInfo;
public UInt32 cMsgCert;
public IntPtr rgpMsgCert;
public UInt32 cMsgCrl;
public IntPtr rgpMsgCrl;
public UInt32 cAuthAttr;
public IntPtr rgAuthAttr;
public UInt32 cUnauthAttr;
public IntPtr rgUnauthAttr;
public UInt32 dwFlags;
public UInt32 dwInnerContentType;
public CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm;
public IntPtr pvHashEncryptionAuxInfo;
}

[DllImport("Crypt32.dll", EntryPoint="CryptSignMessage")]
public static extern bool CryptSignMessage(
ref CRYPT_SIGN_MESSAGE_PARA pSignPara,
bool fDetachedSignature,
UInt32 cToBeSigned,
string[] rgpbToBeSigned,
int[] rgcbToBeSigned,
IntPtr pbSignedBlob,
ref UInt32 pcbSignedBlob);

private void HandleError(string s) {throw new Exception(s);}

IntPtr GetCertificate()
{
IntPtr hCertStore = CertOpenSystemStore(IntPtr.Zero, "MY");
// Hvis pointeren er = 0 SÃ¥ kunne den ikke finde STOREN
if (hCertStore.ToInt32() == 0)
{
MessageBox.Show("CertOpenSystemStore failed: " +
Marshal.GetLastWin32Error().ToString());
return System.IntPtr.Zero ;
}
IntPtr pCertContext = CertEnumCertificatesInStore(hCertStore, IntPtr.Zero);
IntPtr tmp = CertDuplicateCertificateContext(pCertContext);
// Sender en pointer til det første Certificat i MY STOREen
//signHash(tmp);
return tmp;
}

byte[] signHash(string BodyText)
{
IntPtr pCert = GetCertificate();

CRYPT_KEY_PROV_INFO pKeyProvInfo = new CRYPT_KEY_PROV_INFO();
IntPtr pbKeyProvInfo=IntPtr.Zero;
uint cbKeyProvInfo=0;
IntPtr hProv = IntPtr.Zero;
IntPtr hHash = IntPtr.Zero;
IntPtr pbSignature = IntPtr.Zero;
uint dwSigLen=0;

// Call method to get size of buffer. The number of bytes needed goes
into cbKeyProvInfo
IntPtr Tmp = IntPtr.Zero;
CertGetCertificateContextProperty(pCert, CERT_KEY_PROV_INFO_PROP_ID, Tmp,
ref cbKeyProvInfo);

// Allocate needed memory and call again with the correct buffer
preallocated
pbKeyProvInfo = Marshal.AllocHGlobal(new IntPtr(cbKeyProvInfo));

if (!CertGetCertificateContextProperty(pCert, CERT_KEY_PROV_INFO_PROP_ID,
pbKeyProvInfo, ref cbKeyProvInfo))
HandleError("CertGetCertificateContextProperty failed");

pKeyProvInfo = (CRYPT_KEY_PROV_INFO)Marshal.PtrToStructure(pbKeyProvInfo,
typeof(CRYPT_KEY_PROV_INFO));

string name = pKeyProvInfo.pwszProvName;
string cont = pKeyProvInfo.pwszContainerName;
if(name != "CRYPTOMATHiC RSA Full Provider 1.2")
return null;

if(!CryptAcquireContext(ref hProv,
//pKeyProvInfo.pwszContainerName,
cont,
//pKeyProvInfo.pwszProvName,
name,
pKeyProvInfo.dwProvType,
pKeyProvInfo.dwFlags))
HandleError("CryptAcquireContext failed");

IntPtr ProvData = Marshal.StringToHGlobalAnsi("Password");
if(pKeyProvInfo.dwKeySpec==AT_SIGNATURE)
{
if(!CryptSetProvParam(hProv, PP_SIGNATURE_PIN, ProvData, 0))
{
//Marshal.FreeHGlobal(Data);
HandleError("CryptSetProvParam failed");
}
}
else
{
if(!CryptSetProvParam(hProv, PP_KEYEXCHANGE_PIN, ProvData, 0))
{
//Marshal.FreeHGlobal(Data);
HandleError("CryptSetProvParam failed");
}
}
//Marshal.FreeHGlobal(Data);

CertSetCertificateContextProperty(pCert, CERT_KEY_PROV_HANDLE_PROP_ID,
CERT_STORE_NO_CRYPT_RELEASE_FLAG, hProv);
CRYPT_SIGN_MESSAGE_PARA signParams;
signParams = new CRYPT_SIGN_MESSAGE_PARA();
signParams.cbSize = (uint) Marshal.SizeOf(typeof(CRYPT_SIGN_MESSAGE_PARA));
signParams.dwMsgEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;
signParams.pSigningCert = pCert;
signParams.HashAlgorithm.pszObjId = "1.2.840.113549.1.1.5";
signParams.HashAlgorithm.Parameters.cbData = 0;
signParams.pvHashAuxInfo = new IntPtr(0);
signParams.cMsgCert = 1;
signParams.rgpMsgCert = Marshal.AllocCoTaskMem ( Marshal.SizeOf
(typeof(IntPtr)));
Marshal.StructureToPtr (pCert, signParams.rgpMsgCert, true);
signParams.cMsgCrl = 0;
signParams.rgpMsgCrl = new IntPtr(0);
signParams.cAuthAttr = 0;
signParams.rgAuthAttr = new IntPtr(0);
signParams.cUnauthAttr = 0;
signParams.rgUnauthAttr = new IntPtr(0);
signParams.dwFlags = 0;
signParams.dwInnerContentType = 0;

string[] ConvertedString = {BodyText};
int[] sizes = {ConvertedString[0].Length};
CryptSignMessage (ref signParams, true, 1, ConvertedString, sizes,
IntPtr.Zero, ref dwSigLen);
pbSignature= Marshal.AllocHGlobal((int)dwSigLen);
CryptSignMessage (ref signParams, true, 1, ConvertedString, sizes,
pbSignature, ref dwSigLen);

byte[] output = new byte[dwSigLen];
Marshal.Copy(pbSignature,output,0,Convert.ToInt32(dwSigLen));
try
{
//SendMail(output);
return output;
}
catch(System.Exception ex)
{
MessageBox.Show(ex.Message);
}

if(!CryptReleaseContext(hProv,0))
HandleError("CryptReleaseContext failed");

Marshal.FreeHGlobal(pbKeyProvInfo);
Marshal.FreeHGlobal(pbSignature);
return null;
}

private void SendMail()
{
CDO.IBodyPart oBodyPart;
ADODB.Fields cFields;
ADODB.Stream oStream;

// set sender, recipient, and subject.
oMessage = new CDO.Message();
oMessage.To = "(e-mail address removed)";
oMessage.Subject = "Test Mail";
oMessage.Fields["urn:schemas:mailheader:date"].Value = DateTime.UtcNow;
// oUtilities.LocalTimeToUTCTime(DateTime.Now);
oMessage.TextBody = "Hello this is a test mail";
oMessage.From = "(e-mail address removed)";
oMessage.Fields.Update();

oBodyPart = oMessage.BodyPart.AddBodyPart(1);
cFields = oBodyPart.Fields;
//cFields[CDO.CdoMailHeader.cdoContentType].Value =
CDO.CdoContentTypeValues.cdoTextPlain;
cFields["urn:schemas:mailheader:content-type"].Value =
CDO.CdoContentTypeValues.cdoTextPlain;
cFields.Update();

oStream = oBodyPart.GetDecodedContentStream();
oStream.WriteText("Hello This is at test message :o)",0);
oStream.Flush();

//
//
// Start the new message
//
//
CDO.Message oSignedMsg = new CDO.Message();
CDO.IBodyPart oBodyPart2;
ADODB.Fields cFields2;
ADODB.Stream oStream2;

// set the from field based off of the selected certificate
oSignedMsg.From = "(e-mail address removed)";
oSignedMsg.DataSource.OpenObject(oMessage,
CDO.CdoInterfaces.cdoIMessage);
// this is to be a clear text signed message so we need to copy the
interesting
// parts (sender, recipient, and subject) into the new header
oSignedMsg.To = oMessage.To;
oSignedMsg.CC = oMessage.CC;
oSignedMsg.Subject = oMessage.Subject;

oBodyPart2 = oSignedMsg.BodyPart.AddBodyPart(1);
cFields2 = oBodyPart2.Fields;
//cFields2[CDO.CdoMailHeader.cdoContentType].Value =
oMessage.BodyPart.BodyParts[1].Fields[CDO.CdoMailHeader.cdoContentType].Value;
cFields2["urn:schemas:mailheader:content-type"].Value =
oMessage.BodyPart.BodyParts[1].Fields["urn:schemas:mailheader:content-type"].Value;
cFields2.Update();

// Attach the signature and let CDO base64 encode it
oBodyPart2 = oSignedMsg.BodyPart.AddBodyPart(1);
cFields2 = oBodyPart2.Fields;
oBodyPart2.Fields["urn:schemas:mailheader:content-type"].Value =
"application/x-pkcs7-signature\rName = " + '\u0022' + "smime.p7s" + '\u0022'
+ "";
oBodyPart2.Fields["urn:schemas:mailheader:content-transfer-encoding"].Value = "base64";
oBodyPart2.Fields["urn:schemas:mailheader:content-disposition"].Value =
"attachment;\rFileName=" + '\u0022' + "smime.p7s" + '\u0022' + "";
cFields2.Update();
//
oStream2 = oBodyPart2.GetDecodedContentStream();
oStream2.Type = ADODB.StreamTypeEnum.adTypeBinary;
oStream2.Write (byteSignature);

oStream2.Flush();
//
// Set the messages content type, this needs to be done last to ensure it
is not changed when we add the BodyParts
oSignedMsg.Fields["urn:schemas:mailheader:content-type"].Value =
"multipart/signed;\rprotocol=" + '\u0022' + "application/x-pkcs7-signature" +
'\u0022' + ";\rmicalg=SHA1;";
oSignedMsg.Fields.Update();


oMessage = oSignedMsg;

//oMessage.Send();
oSignedMsg.Send();
}
}
}
 
Hello Kim,
Are you sure your code you posted can work perfectly?
I found this line oStream2.Write (byteSignature); seems you did not declare
byteSignature anywhere and you did not use your cert to sign the textbody
before sending them.

I am currently trying to make your code work in my side and i will update
you later.

Thanks,
Rhett Gong [MSFT]
Microsoft Online Partner Support

This posting is provided "AS IS" with no warranties, and confers no rights.
Please reply to newsgroups only. Thanks.
 
Hello Rhett Gong,

You say that you would update me later when you had something up and
running, what happen? I haven't heard from you!

It would be nice if you could find a solution for me.

Thanks

Kim
 
Hi Kim,

We are really sorry for the late response. Ray is out of office and will be
back on next Monday. I will look for somebody look into it first.

Thanks very much for your understanding.

Best regards,
Yanhong Huang
Microsoft Community Support

Get Secure! ¨C www.microsoft.com/security
Register to Access MSDN Managed Newsgroups!
-http://support.microsoft.com/default.aspx?scid=/servicedesks/msdn/nospam.as
p&SD=msdn

This posting is provided "AS IS" with no warranties, and confers no rights.
 
Hi Kim,

I'm checked your code. Just as Rhett suggested, I didn't find the
declaration of it in the code you have provided. Is it the array which
contains the byte array of email signature? Could you provide us with more
information?

Here I think you have to call signHash to sign the body text before
assigning it directly to the oMessage.TextBody property.

Kevin Yu
=======
"This posting is provided "AS IS" with no warranties, and confers no
rights."
 
Hello Kim,
Sorry for the late response.

I tried your code in my machine but it failed to recognize variable
"byteSignature". Could you post your code which is used to fill the
"byteSignature" so that I can repro your problem in my side and post my
suggestion after doing some research?

Thanks,
Rhett Gong [MSFT]
Microsoft Online Partner Support

This posting is provided "AS IS" with no warranties, and confers no rights.
Please reply to newsgroups only. Thanks.
 
Hi Kim,

I found your posts by doing a search on goolge and went through your code as
I need to do the same type of thing. Yes you did not declare byteSignature
but I saw your byte[] should come from the signHash function.
I then ran the code and got an error in the client email application.
Something about a problem with the headers of the email. I then did some
diging, hacking, cracking and reading and found the problem. Since you did
all the work in getting the API to work properly in c# I only saw it fit to
give you my final solution. This function will replace your SendMail
function...

private void SendMail(string to, string from, string cc, string subject,
string body)
{
CDO.Message oSignedMsg = new CDO.Message();
oSignedMsg.From = "(e-mail address removed)";
oSignedMsg.To = to;
if(cc != null && cc.Trim() != "")
{
oSignedMsg.CC = cc;
}
oSignedMsg.Subject = subject;
oSignedMsg.BodyPart.ContentMediaType = "multipart/signed;\rprotocol=" +
'\u0022' + "application/x-pkcs7-signature" + '\u0022' + ";\rmicalg=SHA1;";

CDO.IBodyPart oBodyPart =
oSignedMsg.BodyPart.AddBodyPart(oSignedMsg.BodyPart.BodyParts.Count + 1);
oBodyPart.ContentMediaType = "multipart/mixed";

CDO.IBodyPart oBodyPart2 =
oBodyPart.AddBodyPart(oBodyPart.BodyParts.Count + 1);
oBodyPart2.ContentMediaType = "text/plain;charset=\"iso-8859-1\"";
ADODB.Stream oStream = oBodyPart2.GetDecodedContentStream();
oStream.WriteText(body, 0);
oStream.Flush();
oStream.Close();

CDO.IBodyPart oSigniturePart =
oSignedMsg.BodyPart.AddBodyPart(oSignedMsg.BodyPart.BodyParts.Count + 1);
oSigniturePart.ContentMediaType = "application/x-pkcs7-signature";
oSigniturePart.ContentTransferEncoding = "base64";
oSigniturePart.Fields["urn:schemas:mailheader:content-disposition"].Value
= "attachment;\rFileName=" + '\u0022' + "smime.p7s" + '\u0022' + "";
oSigniturePart.Fields.Update();

oStream = oSignedMsg.GetStream();
string messageToSign = oStream.ReadText(oStream.Size);
byte[] byteSignature = signHash(messageToSign);

oStream = oSigniturePart.GetDecodedContentStream();
oStream.Type = ADODB.StreamTypeEnum.adTypeBinary;
oStream.Write(byteSignature);
oStream.Flush();
oStream.Close();

oSignedMsg.Send();
}

The problem, simply, was with your bodyparts, when attaching the signiture
this must be in a new bodypart it seems and your mail body/s should form
part of the main bodypart... There is one more problem though. The mail is
not signed 100% correctly, the certificate is attached and content signed,
but it seems that when it is opened the Mail client reports it as being
tampered with... If you find a solution to that, please let me know... I now
have a nice little library to sign the messages, if you would like me to
attach the whole project please don't hesitate to ask...

Please let me know if this works for you or not...

Regards,
Fanie Oosthuysen

Kim said:
Hello Rhett Gong,

You say that you would update me later when you had something up and
running, what happen? I haven't heard from you!

It would be nice if you could find a solution for me.

Thanks

Kim

Rhett Gong said:
Hello Kim,
Are you sure your code you posted can work perfectly?
I found this line oStream2.Write (byteSignature); seems you did not declare
byteSignature anywhere and you did not use your cert to sign the textbody
before sending them.

I am currently trying to make your code work in my side and i will update
you later.

Thanks,
Rhett Gong [MSFT]
Microsoft Online Partner Support

This posting is provided "AS IS" with no warranties, and confers no rights.
Please reply to newsgroups only. Thanks.
 
Hi Kim,

I found your posts by doing a search on goolge and went through your code as
I need to do the same type of thing. Yes you did not declare byteSignature
but I saw your byte[] should come from the signHash function.
I then ran the code and got an error in the client email application.
Something about a problem with the headers of the email. I then did some
diging, hacking, cracking and reading and found the problem. Since you did
all the work in getting the API to work properly in c# I only saw it fit to
give you my final solution. This function will replace your SendMail
function...

private void SendMail(string to, string from, string cc, string subject,
string body)
{
CDO.Message oSignedMsg = new CDO.Message();
oSignedMsg.From = "(e-mail address removed)";
oSignedMsg.To = to;
if(cc != null && cc.Trim() != "")
{
oSignedMsg.CC = cc;
}
oSignedMsg.Subject = subject;
oSignedMsg.BodyPart.ContentMediaType = "multipart/signed;\rprotocol=" +
'\u0022' + "application/x-pkcs7-signature" + '\u0022' + ";\rmicalg=SHA1;";

CDO.IBodyPart oBodyPart =
oSignedMsg.BodyPart.AddBodyPart(oSignedMsg.BodyPart.BodyParts.Count + 1);
oBodyPart.ContentMediaType = "multipart/mixed";

CDO.IBodyPart oBodyPart2 =
oBodyPart.AddBodyPart(oBodyPart.BodyParts.Count + 1);
oBodyPart2.ContentMediaType = "text/plain;charset=\"iso-8859-1\"";
ADODB.Stream oStream = oBodyPart2.GetDecodedContentStream();
oStream.WriteText(body, 0);
oStream.Flush();
oStream.Close();

CDO.IBodyPart oSigniturePart =
oSignedMsg.BodyPart.AddBodyPart(oSignedMsg.BodyPart.BodyParts.Count + 1);
oSigniturePart.ContentMediaType = "application/x-pkcs7-signature";
oSigniturePart.ContentTransferEncoding = "base64";
oSigniturePart.Fields["urn:schemas:mailheader:content-disposition"].Value
= "attachment;\rFileName=" + '\u0022' + "smime.p7s" + '\u0022' + "";
oSigniturePart.Fields.Update();

oStream = oSignedMsg.GetStream();
string messageToSign = oStream.ReadText(oStream.Size);
byte[] byteSignature = signHash(messageToSign);

oStream = oSigniturePart.GetDecodedContentStream();
oStream.Type = ADODB.StreamTypeEnum.adTypeBinary;
oStream.Write(byteSignature);
oStream.Flush();
oStream.Close();

oSignedMsg.Send();
}

The problem, simply, was with your bodyparts, when attaching the signiture
this must be in a new bodypart it seems and your mail body/s should form
part of the main bodypart... There is one more problem though. The mail is
not signed 100% correctly, the certificate is attached and content signed,
but it seems that when it is opened the Mail client reports it as being
tampered with... If you find a solution to that, please let me know... I now
have a nice little library to sign the messages, if you would like me to
attach the whole project please don't hesitate to ask...

Please let me know if this works for you or not...

Regards,
Fanie Oosthuysen

Kim said:
I have a .NET service sending mails using CDOEX.

These mails I need to sign. I got a tip that I should use CAPICOM. That
worked
fine sending a mail with signature.

BUT the problem is that I have to type the password for my certificate every
time my program signs a mail :o(

My program is a windows service running on a server so typing passwords is
bad.

Then I got a new tip, I should use CryptoAPI instead of CAPICOM.

I think I have solved the CryptoAPI mystic and got my certificate, signed my
body of the mail and got the hash for it. But I don't know how to get my
signed code into the mail?

The code looks like this, and gives this error when the outlook client
reseive the mail:

Error: Can't open this item. Your Digital ID name can not be found by the
underlying security system.

This takes a byte array that I send from my CryptoAPI code.
Code:

private void SendMail(byte[] byteSignature)
{
CDO.IBodyPart oBodyPart;
ADODB.Fields cFields;
ADODB.Stream oStream;

// set sender, recipient, and subject.
oMessage = new CDO.Message();

oMessage.To = "(e-mail address removed)";
oMessage.Subject = "Test Mail";
oMessage.Fields["urn:schemas:mailheader:date"].Value = DateTime.UtcNow;
oMessage.Fields.Update();

oMessage.From = "(e-mail address removed)";

oBodyPart = oMessage.BodyPart.AddBodyPart(1);
cFields = oBodyPart.Fields;

cFields["urn:schemas:mailheader:content-type"].Value =
CDO.CdoContentTypeValues.cdoTextPlain;
cFields.Update();

oStream = oBodyPart.GetDecodedContentStream();
oStream.WriteText("Hello this is some test text",0);
oStream.Flush();

//
//
// Start the new message
//
//
CDO.Message oSignedMsg = new CDO.Message();
CDO.IBodyPart oBodyPart2;
ADODB.Fields cFields2;
ADODB.Stream oStream2;

oSignedMsg.From = "(e-mail address removed)";

// this is to be a clear text signed message so we need to copy the
interesting
// parts (sender, recipient, and subject) into the new header
oSignedMsg.To = oMessage.To;
oSignedMsg.CC = oMessage.CC;
oSignedMsg.Subject = oMessage.Subject;

oBodyPart2 = oSignedMsg.BodyPart.AddBodyPart(1);
cFields2 = oBodyPart2.Fields;

cFields2["urn:schemas:mailheader:content-type"].Value =
oMessage.BodyPart.BodyParts[1].Fields["urn:schemas:mailheader:content-type"]
..Value;
cFields2.Update();

// Attach the signature and let CDO base64 encode it
oBodyPart2 = oSignedMsg.BodyPart.AddBodyPart(1);
cFields2 = oBodyPart2.Fields;
oBodyPart2.Fields["urn:schemas:mailheader:content-type"].Value =
"application/x-pkcs7-signature\rName = " + '\u0022' + "smime.p7s" + '\u0022'
+ "";

oBodyPart2.Fields["urn:schemas:mailheader:content-transfer-encoding"].Value
=
"base64";
oBodyPart2.Fields["urn:schemas:mailheader:content-disposition"].Value
=
"attachment;\rFileName=" + '\u0022' + "smime.p7s" + '\u0022' + "";
cFields2.Update();
//
oStream2 = oBodyPart2.GetDecodedContentStream();
oStream2.Type = ADODB.StreamTypeEnum.adTypeBinary;
oStream2.Write (byteSignature);
oStream2.Flush();

// Set the messages content type, this needs to be done last to ensure
it is not changed when we add the BodyParts

oSignedMsg.Fields["urn:schemas:mailheader:content-type"].Value =
"multipart/signed;\rprotocol=" + '\u0022' +
"application/x-pkcs7-signature" +
 
Back
Top