邮件签名和加密
电子邮件是最常用的网络应用之一,已经成为人们信息交流的重要途径。电子邮件的来往一般是比IM更正规的一种交流,是严肃、严谨的一种方式,普通电子邮件以明文方式在网上传输、存储,存在巨大的安全风险,一直以来都是黑客重点攻击的目标,对信息安全构成重大威胁和挑战。
同时各用户单位普遍使用的大多为传统邮件系统,缺乏安全设计,邮件数据明文传输和明文存储,安全性十分脆弱,存在着易泄密、易被监听和破解等严重安全隐患。整体来说缺乏系统性地实现防病毒、反垃圾、远程实时监测、恶意代码防范、攻击报警等安全监测保障功能,加上专业信息安全人员编制少,安全现状不容乐观。
如何保障我们的信息安全,对于非专业人士非常的麻烦,S/MIME协议全称“安全的多功能互联网邮件扩展”(Secure/Multipurpose Internet Mail Extensions),是通过在RFCl847中定义的多部件媒体类型在MIME中打包安全服务的另一个技术。它提供验证、信件完整性、数字签名和加密。
例如,微软Outlook已经从Outlook97就开始支持S/MIME;具体的使用就是在outlook上设置自己的默认签名加密证书,而在开发中我们也需要使用这个功能。在下图中展示了邮件的签名和加密。
CMS/PKCS #7 提供 EnvelopedCms类来为邮件进行数字封装。使用 EnvelopedCms 类的一种 Encrypt 方法之一为邮件加密,CmsRecipient 类存储收件人的 X509 证书以及在邮件发件人和收件人之间建立会话密钥所用的技术。EnvelopedCms 类支持为多个收件人封装邮件。
1 /// <summary> 2 /// 证书加密 3 /// </summary> 4 /// <param name="clearText">加密的内容</param> 5 /// <param name="certificate">加密的公钥</param> 6 /// <returns></returns> 7 static byte[] EncryptWithCertificate(byte[] clearText, X509Certificate2 certificate) 8 { 9 // create ContentInfo 10 ContentInfo contentInfo = new ContentInfo(clearText); 11 // EnvelopedCms represents encrypted data 12 //EnvelopedCms envelopedCms = new EnvelopedCms(contentInfo); 13 EnvelopedCms envelopedCms = new EnvelopedCms(contentInfo, new AlgorithmIdentifier(new Oid("AES256")));//3DES 指定的加密方式(可选择其他加密方式) 14 // add a recipient 15 CmsRecipient recipient = new CmsRecipient(certificate); 16 // encrypt data with public key of recipient 17 envelopedCms.Encrypt(recipient); 18 // create PKCS #7 byte array 19 // return encrypted data 20 return envelopedCms.Encode(); 21 }
使用 EnvelopedCms 类的 Decrypt 方法为封装的邮件解密。EnvelopedCms 邮件包含解决所需的收件人标识信息。该信息包含在 RecipientInfos 属性中。
1 /// <summary> 2 /// 证书解密 3 /// </summary> 4 /// <param name="cipherText">加密内容</param> 5 /// <returns></returns> 6 static byte[] DecryptWithCertificate(byte[] cipherText) 7 { 8 // create EnvelopedCms 9 EnvelopedCms envelopedCms = new EnvelopedCms(); 10 // deserialize PKCS#7 byte array 11 envelopedCms.Decode(cipherText); 12 // decryt data 13 envelopedCms.Decrypt(); 14 // return plain text data 15 return envelopedCms.ContentInfo.Content; 16 }
CMS/PKCS #7 提供 SignedCms 类来为邮件进行数字签名。使用 S/MIME 安全标准的电子邮件是一个说明如何使用 SignedCms 类提供安全性的示例。除了其他安全服务之外,S/MIME 还指定验证电子邮件发件人的真实性以及检查邮件本身的完整性的能力。
可以使用 SignedCms 类的 ComputeSignature 方法之一计算邮件签名。SignedCms 邮件可以是“非分离式”,也可以是“分离式”,SignedCms.Detached确定是否分离邮件。SignedCms 邮件可以具有关联的签名特定的属性。这些属性可以签名,也可以不签名,Pkcs9SignTime签名时间属性就是可能使用的签名属性的示例。此属性包含邮件的签名时间。
1 /// <summary> 2 /// 签名 3 /// </summary> 4 /// <param name="clearText">签名内容</param> 5 /// <param name="signingCertificate">签名证书(私钥)</param> 6 /// <returns></returns> 7 private static byte[] SignData(byte[] clearText, X509Certificate2 signingCertificate) 8 { 9 // create ContentInfo 10 ContentInfo contentInfo = new ContentInfo(clearText); 11 12 CmsSigner signer = new CmsSigner(signingCertificate); 13 //SHA256 14 15 //设置摘要算法 16 signer.DigestAlgorithm = new Oid("SHA256"); 17 18 Pkcs9SigningTime pkcsTime = new Pkcs9SigningTime(DateTime.Now); 19 //设置签名时间 20 signer.SignedAttributes.Add(new AsnEncodedData(pkcsTime)); 21 22 //创建确定是否分离签名 23 SignedCms signedCms = new SignedCms(SubjectIdentifierType.IssuerAndSerialNumber, contentInfo, false); 24 25 //如果此属性为 true,则分离签名。如果此属性为 false,则不分离签名。 26 27 28 // sign the data 29 signedCms.ComputeSignature(signer); 30 //signedCms.SignerInfos[0].RemoveCounterSignature(0); 31 32 // create PKCS #7 byte array 33 // return signed data 34 return signedCms.Encode(); 35 }
SignedCms 类的 CheckSignature 方法之一验证邮件签名、副署和签名属性。SignedCms 邮件包含验证所需的签名者证书。签名验证可否验证签名者证书,取决于这些方法的 verifySignatureOnly 参数的值。
1 /// <summary> 2 /// 验证签名和提取内容 3 /// </summary> 4 /// <param name="signedCmsAsBytes"></param> 5 /// <param name="signingSubjects"></param> 6 /// <returns></returns> 7 static byte[] ValidateSignatureAndExtractContent(byte[] signedCmsAsBytes, ICollection<string> signingSubjects) 8 { 9 // create SignedCms 10 SignedCms signedCms = new SignedCms(); 11 // deserialize PKCS #7 byte array 12 signedCms.Decode(signedCmsAsBytes); 13 // check signature 14 // false checks signature and certificate 15 // true only checks signature 16 signedCms.CheckSignature(true); 17 // access signature certificates (if needed) 18 foreach (SignerInfo signerInfo in signedCms.SignerInfos) 19 { 20 X509Certificate2 signingCertificate = signerInfo.Certificate; 21 signingSubjects.Add(signingCertificate.Subject); 22 23 //signerInfo.SignedAttributes. 24 foreach (var tempattr in signerInfo.SignedAttributes) 25 { 26 Oid tempoid = tempattr.Oid; 27 byte[] strValueByte = tempattr.Values[0].RawData; 28 string oidvalue = Encoding.UTF8.GetString(strValueByte, 0, strValueByte.Length); 29 } 30 31 } 32 33 // return plain data without signature 34 return signedCms.ContentInfo.Content; 35 }
上述签名代码中,可以更改签名方法,设置签名时间,同时签名和内容是可以分离开来的,同时在签名中也可以设置和读取其他属性,但目前本代码不支持,还在研究中。国内外相关的资料非常少,如找到相关代码,请不吝赐教,不胜感激。