如何:对邮件进行副署
此示例使用 System.Security.Cryptography.Pkcs 创建 CMS/PKCS #7 签名邮件。该邮件由一个签名者签名,然后由两个其他签名者对该签名进行副署。接着验证该邮件的签名和副署。
示例
此示例使用下列类:
以下示例要求“我的证书”存储中包含三个公钥证书,使用者名称分别为“MessageSigner1”、“CounterSigner1”和“CounterSigner2”,并且包含关联的私钥。
注意: |
---|
这只是一个说明性的示例。生产环境可能会使用不同的模型,邮件的发件人和收件人在不同的进程中使用唯一的公钥凭据执行操作。 |
使用 Makecert.exe 实用程序设置此示例,可以通过多种方法实现。Certificate Creation Tool (Makecert.exe) 是用于生成测试证书的一种很方便的实用程序。在生产环境中,证书由证书颁发机构生成。
下列 Makecert 命令生成所需的公钥证书和私钥。
Makecert -n "CN=MessageSigner1" -ss My
Makecert -n "CN=CounterSigner1" -ss My
Makecert -n "CN=CounterSigner2" -ss My
// Copyright (c) Microsoft Corporation. All rights reserved.
#region Using directives
using System;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
using System.Text;
#endregion
namespace CountersignAMessage
{
class SignedCmsCountersigned
{
const String signerName = "MessageSigner1";
const String countersignerName1 = "CounterSigner1";
const String countersignerName2 = "CounterSigner2";
static void Main(string[] args)
{
Console.WriteLine("System.Security.Cryptography.Pkcs " +
"Sample: Single-signer multiple " +
"countersigner signed and verified message.");
// The original message.
const String msg = "The following is the approved corporate" +
" reorganization plan.";
Console.WriteLine("\nOriginal message (len {0}): {1} ",
msg.Length, msg);
// Convert the message to an array of bytes for signing.
Encoding unicode = Encoding.Unicode;
byte[] msgBytes = unicode.GetBytes(msg);
Console.WriteLine("\n\n------------------------------");
Console.WriteLine(" SETUP OF CREDENTIALS ");
Console.WriteLine("------------------------------\n");
X509Certificate2Collection signerCerts = GetSignerCerts();
X509Certificate2Collection countersignerCerts =
GetCountersignerCerts();
Console.WriteLine("\n\n----------------------");
Console.WriteLine(" SENDER SIDE ");
Console.WriteLine("----------------------\n");
byte[] encodedSignedCms = null;
try
{
encodedSignedCms = SignMsg(msgBytes, signerCerts,
countersignerCerts);
}
catch (ArgumentOutOfRangeException e)
{
Console.WriteLine(e.Message);
Console.WriteLine("The proper sample certificates are " +
"not in the My store. Refer to the documentation " +
"about this sample for instructions to correct this.");
return;
}
Console.WriteLine("\n\n------------------------");
Console.WriteLine(" RECIPIENT SIDE ");
Console.WriteLine("------------------------\n");
if (VerifyMsg(encodedSignedCms))
{
Console.WriteLine("\nMessage verified");
}
else
{
Console.WriteLine("\nMessage failed to verify");
}
}
// Open the My (or Personal) certificate store and search for
// credentials with which to sign the message. There must be
// a certificate with subject name "MessageSigner1".
static public X509Certificate2Collection GetSignerCerts()
{
// Open the My certificate store.
X509Store storeMy = new X509Store(StoreName.My,
StoreLocation.CurrentUser);
storeMy.Open(OpenFlags.ReadOnly);
// Display certificates to help troubleshoot
// the example's setup.
Console.WriteLine("Found certs with the following subject " +
"names in the {0} store:",
storeMy.Name);
foreach (X509Certificate2 cert in storeMy.Certificates)
Console.WriteLine("\t{0}", cert.SubjectName.Name);
// Start the collection of signer certificates to be returned.
X509Certificate2Collection signerCertsColl = new
X509Certificate2Collection();
// Find the signer's certificate.
// Add it to the signers' certificates collection.
X509Certificate2Collection foundCertColl = storeMy.
Certificates.Find(X509FindType.FindBySubjectName,
signerName, false);
Console.WriteLine(
"Found {0} certificates in the {1} store with name {2}",
foundCertColl.Count, storeMy.Name, signerName);
// Check to see if the certificate suggested by the example
// requirements is not present.
if (foundCertColl.Count == 0)
{
Console.WriteLine(
"A suggested certificate to use for this example " +
"is not in the certificate store. Select " +
"an alternate certificate to use for " +
"signing the message.");
}
signerCertsColl.Add(foundCertColl[0]);
storeMy.Close();
return signerCertsColl;
}
// Open the My (or Personal) certificate store and search for
// credentials with which to countersign the message. There must
// be two certificates: one with the subject name
// "Countersigner1", and the other with the subject name
// "Countersigner2".
static public X509Certificate2Collection GetCountersignerCerts()
{
// Open the My certificate store.
X509Store storeMy = new X509Store(StoreName.My,
StoreLocation.CurrentUser);
storeMy.Open(OpenFlags.ReadOnly);
// Start the collection of signer certificates to be returned.
X509Certificate2Collection countersignerCertsColl = new
X509Certificate2Collection();
// Find the first countersigner's certificate.
// Add it to the countersigners' certificates collection.
X509Certificate2Collection foundCertColl = storeMy.
Certificates.Find(X509FindType.FindBySubjectName,
countersignerName1, false);
Console.WriteLine(
"Found {0} certificates in the {1} store with name {2}",
foundCertColl.Count, storeMy.Name, countersignerName1);
// Check to see if the certificate suggested by the example
// requirements is not present.
if (foundCertColl.Count == 0)
{
Console.WriteLine(
"A suggested certificate to use for this example " +
"is not in the certificate store. Select " +
"an alternate certificate to use for " +
"signing the message.");
}
countersignerCertsColl.Add(foundCertColl[0]);
// Find the second countersigner's certificate.
// Add it to the countersigners' certificates collection.
foundCertColl = storeMy.
Certificates.Find(X509FindType.FindBySubjectName,
countersignerName2, false);
Console.WriteLine(
"Found {0} certificates in the {1} store with name {2}",
foundCertColl.Count, storeMy.Name, countersignerName2);
// Check to see if the certificate suggested by the example
// requirements is not present.
if (foundCertColl.Count == 0)
{
Console.WriteLine(
"A suggested certificate to use for this example " +
"is not in the certificate store. Select " +
"an alternate certificate to use for " +
"signing the message.");
}
countersignerCertsColl.Add(foundCertColl[0]);
storeMy.Close();
return countersignerCertsColl;
}
// Sign the message with the private key of the signer,
// and countersign that signature with the private key
// of each of the countersigners.
static public byte[] SignMsg(
Byte[] msg,
X509Certificate2Collection signerCerts,
X509Certificate2Collection countersignerCerts)
{
// There must be at least one certificate in the collection.
if (signerCerts.Count == 0 || countersignerCerts.Count == 0)
{
throw new ArgumentOutOfRangeException("Empty certificate" +
" collection passed to SignMsg.");
}
// Place the message in a ContentInfo object.
// This is required to build a SignedCms object.
ContentInfo contentInfo = new ContentInfo(msg);
// Instantiate a SignedCms object with the ContentInfo above.
// Use the default SubjectIdentifierType
// IssuerAndSerialNumber.
// Use the default Detached property value FALSE so that the
// message is included in the encoded SignedCms.
SignedCms signedCms = new SignedCms(contentInfo);
// Sign the CMS/PKCS #7 message once for each
// signer certificate.
foreach (X509Certificate2 cert in signerCerts)
{
Console.Write("Computing signature with signer subject " +
"name {0} ... ", cert.SubjectName.Name);
signedCms.ComputeSignature(new CmsSigner(cert));
Console.WriteLine("Done.");
}
// Countersign the first signature in the CMS/PKCS #7 message
// once for each countersigner certificate.
foreach (X509Certificate2 cert in countersignerCerts)
{
Console.Write("Computing countersignature with signer " +
"subject name {0} ... ",
cert.SubjectName.Name);
signedCms.SignerInfos[0].ComputeCounterSignature(new
CmsSigner(cert));
Console.WriteLine("Done.");
}
// Encode the CMS/PKCS #7 message.
return signedCms.Encode();
}
// Verify the encoded SignedCms message and return a Boolean
// value that specifies whether the verification was successful.
static public bool VerifyMsg(byte[] encodedSignedCms)
{
// Prepare an object in which to decode and verify.
SignedCms signedCms = new SignedCms();
signedCms.Decode(encodedSignedCms);
// Check the number of signers and countersigners.
DisplaySignedCmsProperties("In VerifyMsg method", signedCms);
// Catch a verification exception if you want to
// advise the message recipient that security actions
// might be appropriate.
try
{
// Verify signature. Do not validate the signer
// certificate chain for the purposes of this example.
// Note that in a production environment, validating
// the signer certificate chain will probably
// be necessary.
Console.Write("Checking signature and countersignatures" +
" on message ... ");
signedCms.CheckSignature(true);
Console.WriteLine("Done.");
}
catch (System.Security.Cryptography.CryptographicException e)
{
Console.WriteLine("VerifyMsg caught exception: {0}",
e.Message);
Console.WriteLine("Verification of the signed " +
"CMS/PKCS #7 failed. The message, signatures, or " +
"countersignatures might have been modified " +
"in transit or storage. The message signers or " +
"countersigners might not be who they claim to be. " +
"The message's authenticity or integrity, or both, " +
"are not guaranteed.");
return false;
}
return true;
}
// This method displays some properties of a signed
// CMS/PKCS #7 message. These properties include the number of
// signers and countersigners for each signer, whether the
// message is detached, and the version of the message.
static void DisplaySignedCmsProperties(String info, SignedCms s)
{
Console.WriteLine();
Console.WriteLine("\n>>>>> SignedCms Signer Info: {0}", info);
Console.WriteLine("\tNumber of signers:\t\t\t{0}",
s.SignerInfos.Count);
for (int i = 0; i < s.SignerInfos.Count; i++)
{
Console.WriteLine("\tSubject name of signer #{0}:\t\t{1}",
i + 1, s.SignerInfos[i].Certificate.SubjectName.Name);
Console.WriteLine("\tNumber of countersigners for " +
"signer #{0}:\t{1}",
i + 1, s.SignerInfos[i].CounterSignerInfos.Count);
}
Console.WriteLine("\tMessage detached state:\t\t\t{0}",
s.Detached);
Console.WriteLine("\tMessage version:\t\t\t{0}", s.Version);
Console.WriteLine();
}
}
}
请参见
任务
如何:由一个签名者为邮件签名
如何:由多个签名者为邮件签名
如何:为一个收件人封装邮件
如何:为多个收件人封装邮件
概念
版权所有 (C) 2007 Microsoft Corporation。保留所有权利。