共用方式為


HOW TO:副署訊息

此範例會使用 System.Security.Cryptography.Pkcs 建立 CMS/PKCS #7 簽署訊息。此訊息由單一簽署者簽署後,該簽章再由另外兩位簽署者副署。接下來該訊息的簽章與副署必須再經過驗證。

範例

本範例使用下列類別:

下例範例要求我的憑證存放區中必須有三個公開金鑰憑證,其主體名稱分別是 "MessageSigner1"、"CounterSigner1" 和 "CounterSigner2",且它們必須有關聯的私密金鑰。

Note注意:

此範例僅用於示範。實際執行環境中可能使用不同的模型,其中訊息的傳送者和收件者會使用其唯一公開金鑰認證,在不同的處理序中執行。

使用 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();
        }
    }
}

請參閱

工作

HOW TO:由一位簽署者簽署訊息
HOW TO:由多位簽署者簽署一個訊息
HOW TO:封套單一收件者的訊息
HOW TO:封套多位收件者的訊息

概念

HOW TO:簽署和封套訊息

Footer image

Copyright © 2007 by Microsoft Corporation. All rights reserved.