Procédure de signature et de mise sous enveloppe d'un message

Cet exemple crée un message CMS/PKCS #7 signé et mis sous enveloppe à l'aide de System.Security.Cryptography.Pkcs. Ce message est d'abord signé par un seul signataire, puis chiffré pour un seul destinataire. Il est ensuite déchiffré à l'aide de la clé privée du destinataire et la signature est vérifiée. L'exemple utilise l'objet SignedCms, qui permet à un ou plusieurs utilisateurs de signer et contre-signer un message. Il utilise également l'objet EnvelopedCms, qui permet de chiffrer des messages pour un ou plusieurs destinataires, c'est-à-dire de les mettre sous enveloppe.

Exemple

Cet exemple utilise les classes suivantes :

Pour s'exécuter sur un seul ordinateur, cet exemple nécessite que :

  • le magasin de certificats My contienne un certificat de clé publique avec le nom d'objet « SignataireMessage1 » ;

  • les magasins de certificats AddressBook et My contiennent un autre certificat avec le nom d'objet « Destinataire1 » ;

  • les clés privées associées soient stockées sur l'ordinateur.

L'exemple de code opère d'abord en tant qu'émetteur du message, puis en tant que destinataire du message. L'exemple utilise les mêmes informations d'identification de clé publique dans chaque rôle. En tant que tel, l'exemple nécessite que le certificat de clé publique pour le nom d'objet « Destinataire1 » se trouve dans deux magasins. En tant qu'émetteur, il recherche dans le magasin de certificats AddressBook le certificat du destinataire qu'il utilise pour chiffrer le message. En tant que destinataire, il recherche le certificat dans le magasin de certificats My et utilise la clé privée associée pour déchiffrer le message.

NoteRemarque :

Cet exemple n'est utilisé qu'à des fins d'illustration. Les environnements de production peuvent utiliser un modèle différent dans lequel l'expéditeur et le destinataire du message s'exécutent selon des processus différents avec les informations d'identification de leur clé publique unique.

Vous pouvez configurer cet exemple à l'aide de l'utilitaire Makecert.exe, qui constitue une des multiples façons de procéder. Certificate Creation Tool (Makecert.exe) est un utilitaire très pratique pour tester les certificats. Dans un environnement de production, les certificats sont générés par une autorité de certification.

Les commandes Makecert suivantes génèrent les certificats de clé publique ainsi que les clés privées nécessaires.

Makecert -n "CN=SignataireMessage2" -ss My

Makecert -n "CN=Destinataire1" -sky exchange -ss M

Ces commandes placent les certificats de clé publique appropriés dans le magasin de certificats My. Pour obtenir le certificat de clé publique pour le nom d'objet « Destinataire1 » dans le magasin de certificats AddressBook, exportez le certificat de clé publique, puis importez-le dans le magasin AddressBook en suivant la procédure indiquée dans Procédure d'exportation et d'importation d'un certificat de clé publique.

// Copyright (c) Microsoft Corporation. All rights reserved.

#region Using directives

using System;
using System.Security.Cryptography;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
using System.Text;

#endregion

namespace SigningAndEnvelopingMessage
{
    class EnvelopedSignedCms
    {
        const String signerName = "MessageSigner1";
        const String recipientName = "Recipient1";

        static void Main(string[] args)
        {
            byte[] origMsg;

            Console.WriteLine("System.Security.Cryptography.Pkcs " +
                "Sample: Encrypted, signed, decrypted, and " +
                "verified message");
            //  Original message.
            const String msg = "Here are the sales figures for the " +
                "upcoming quarterly report to Wall Street.";

            Console.WriteLine("\nOriginal message (len {0}): {1}  ",
                              msg.Length, msg);

            //  Convert message to 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");

            //  The signer's private key, obtained by association with
            //  their signing certificate, is necessary to sign the 
            //  message.
            X509Certificate2 signerCert = GetSignerCert();

            //  The recipient's certificate is necessary to encrypt
            //  the message for that recipient.
            X509Certificate2 recipientCert = GetRecipientCert();

            Console.WriteLine("\n\n----------------------");
            Console.WriteLine("     SENDER SIDE      ");
            Console.WriteLine("----------------------\n");

            byte[] encodedSignedCms = SignMsg(msgBytes, signerCert);

            //  Encrypt the encoded SignedCms message.
            byte[] encodedEnvelopedCms = EncryptMsg(encodedSignedCms,
                recipientCert);

            Console.Write("\nMessage after encryption (len {0}):  ",
                encodedEnvelopedCms.Length);
            foreach (byte b in encodedEnvelopedCms)
            {
                Console.Write("{0:x}", b);
            }
            Console.WriteLine();


            Console.WriteLine("\n\n------------------------");
            Console.WriteLine("     RECIPIENT SIDE     ");
            Console.WriteLine("------------------------\n");

            encodedSignedCms = DecryptMsg(encodedEnvelopedCms);

            //  Get the original message back after verification so
            //  it can be displayed.
            if (VerifyMsg(encodedSignedCms, out origMsg))
            {
                Console.WriteLine("\nMessage verified");
            }
            else
            {
                Console.WriteLine("\nMessage failed to verify");
            }

            //  Convert Unicode bytes to the original message string.
            Console.WriteLine("\nDecrypted Authenticated Message: {0}",
                unicode.GetString(origMsg));

        }

        //  Open the My (or Personal) certificate store. Search for
        //  credentials with which to sign the message. The certificate
        //  must have the subject name "MessageSigner1".
        static public X509Certificate2 GetSignerCert()
        {
            //  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);
            }

            //  Find the signer's certificate.
            X509Certificate2Collection certColl =
                storeMy.Certificates.Find(X509FindType.FindBySubjectName,
                signerName, false);
            Console.WriteLine(
                "Found {0} certificates in the {1} store with name {2}",
                certColl.Count, storeMy.Name, signerName);

            //  Check to see if the certificate suggested by the example
            //  requirements is not present.
            if (certColl.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.");
            }

            storeMy.Close();

            return certColl[0];
        }

        //  Open the AddressBook (called Other in Internet Explorer) 
        //  certificate store and search for a recipient
        //  certificate with which to encrypt the message. The certificate
        //  must have the subject name "Recipient1".
        static public X509Certificate2 GetRecipientCert()
        {
            //  Open the AddressBook local user X509 certificate store.
            X509Store storeAddressBook = new X509Store(StoreName.
                AddressBook, StoreLocation.CurrentUser);
            storeAddressBook.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:",
                storeAddressBook.Name);
            foreach (X509Certificate2 cert in storeAddressBook.Certificates)
            {
                Console.WriteLine("\t{0}", cert.SubjectName.Name);
            }

            //  Get recipient certificate.
            //  For purposes of this sample, do not validate the
            //  certificate. Note that in a production environment,
            //  validating the certificate will probably be necessary.
            X509Certificate2Collection certColl = storeAddressBook.
                Certificates.Find(X509FindType.FindBySubjectName,
                recipientName, false);
            Console.WriteLine(
                "Found {0} certificates in the {1} store with name {2}",
                certColl.Count, storeAddressBook.Name, recipientName);

            //  Check to see if the certificate suggested by the example
            //  requirements is not present.
            if (certColl.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.");
            }

            storeAddressBook.Close();

            return certColl[0];
        }

        //  Sign the message by the using the private key of the signer.
        //  Note that signer's public key certificate is input here 
        //  because it is used to locate the corresponding private key.
        static public byte[] SignMsg(
            Byte[] msg,
            X509Certificate2 signerCert)
        {
            //  Place message in a ContentInfo object.
            //  This is required to build a SignedCms object.
            ContentInfo contentInfo = new ContentInfo(msg);

            //  Instantiate SignedCms object with the ContentInfo above.
            //  Has default SubjectIdentifierType IssuerAndSerialNumber.
            //  Has default Detached property value false, so message is
            //  included in the encoded SignedCms.
            SignedCms signedCms = new SignedCms(contentInfo);

            //  Formulate a CmsSigner object, which has all the needed
            //  characteristics of the signer.
            CmsSigner cmsSigner = new CmsSigner(signerCert);

            //  Sign the PKCS #7 message.
            Console.Write("Computing signature with signer subject " +
                "name {0} ... ", signerCert.SubjectName.Name);
            signedCms.ComputeSignature(cmsSigner);
            Console.WriteLine("Done.");

            //  Encode the PKCS #7 message.
            return signedCms.Encode();
        }

        //  Verify the encoded SignedCms message and return a Boolean
        //  value that specifies whether the verification was successful.
        //  Also return the original message that was signed, which is
        //  available as part of the SignedCms message after it
        //  is decoded.
        static public bool VerifyMsg(byte[] encodedSignedCms,
            out byte[] origMsg)
        {
            //  Prepare a SignedCms object in which to decode
            //  and verify.
            SignedCms signedCms = new SignedCms();

            signedCms.Decode(encodedSignedCms);

            //  Catch a verification exception in the event you want to
            //  advise the message recipient that security actions
            //  might be appropriate.
            try
            {
                //  Verify signature. Do not validate signer
                //  certificate 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 on message ... ");
                signedCms.CheckSignature(true);
                Console.WriteLine("Done.");
            }
            catch (System.Security.Cryptography.CryptographicException e)
            {
                Console.WriteLine("VerifyMsg caught exception:  {0}",
                    e.Message);
                Console.WriteLine("The message may have been modified " +
                    "in transit or storage. Authenticity of the " +
                    "message is not guaranteed.");
                origMsg = null;
                return false;
            }

            origMsg = signedCms.ContentInfo.Content;

            return true;
        }

        //  Encrypt the message with the public key of
        //  the recipient. This is done by enveloping the message by
        //  using a EnvelopedCms object.
        static public byte[] EncryptMsg(Byte[] msg,
            X509Certificate2 recipientCert)
        {
            //  Place message in a ContentInfo object.
            //  This is required to build an EnvelopedCms object.
            ContentInfo contentInfo = new ContentInfo(msg);

            //  Instantiate EnvelopedCms object with the ContentInfo
            //  above.
            //  Has default SubjectIdentifierType IssuerAndSerialNumber.
            //  Has default ContentEncryptionAlgorithm property value
            //  RSA_DES_EDE3_CBC.
            EnvelopedCms envelopedCms = new EnvelopedCms(contentInfo);

            //  Formulate a CmsRecipient object that
            //  represents information about the recipient
            //  to encrypt the message for.
            CmsRecipient recip1 = new CmsRecipient(
                SubjectIdentifierType.IssuerAndSerialNumber,
                recipientCert);

            Console.Write("Encrypting data for a single recipient of " +
                "subject name {0} ... ",
                recip1.Certificate.SubjectName.Name);

            //  Encrypt the message for the recipient.
            envelopedCms.Encrypt(recip1);
            Console.WriteLine("Done.");

            //  The encoded EnvelopedCms message contains the encrypted
            //  message and the information about each recipient that
            //  the message was enveloped for.
            return envelopedCms.Encode();
        }

        //  Decrypt the encoded EnvelopedCms message.
        static public Byte[] DecryptMsg(byte[] encodedEnvelopedCms)
        {
            //  Prepare object in which to decode and decrypt.
            EnvelopedCms envelopedCms = new EnvelopedCms();

            //  Decode the message.
            envelopedCms.Decode(encodedEnvelopedCms);

            //  Display the number of recipients the message is
            //  enveloped for; it should be 1 for this example.
            DisplayEnvelopedCms(envelopedCms, false);

            //  Decrypt the message for the single recipient.
            //  Note that the following call to the Decrypt method
            //  accomplishes the same result:
            //  envelopedCms.Decrypt();
            Console.Write("Decrypting Data ... ");
            envelopedCms.Decrypt(envelopedCms.RecipientInfos[0]);
            Console.WriteLine("Done.");

            return envelopedCms.Encode();
        }

        //  Display the ContentInfo property of a SignedCms object.
        private void DisplaySignedCmsContent(String desc,
            SignedCms signedCms)
        {
            Console.WriteLine(desc + " (length {0}):  ",
                signedCms.ContentInfo.Content.Length);
            foreach (byte b in signedCms.ContentInfo.Content)
            {
                Console.Write(b.ToString() + " ");
            }
            Console.WriteLine();
        }

        //  Display the ContentInfo property of an EnvelopedCms object.
        static private void DisplayEnvelopedCmsContent(String desc,
            EnvelopedCms envelopedCms)
        {
            Console.WriteLine(desc + " (length {0}):  ",
                envelopedCms.ContentInfo.Content.Length);
            foreach (byte b in envelopedCms.ContentInfo.Content)
            {
                Console.Write(b.ToString() + " ");
            }
            Console.WriteLine();
        }

        //  Display some properties of an EnvelopedCms object.
        static private void DisplayEnvelopedCms(EnvelopedCms e,
            Boolean displayContent)
        {
            Console.WriteLine("\nEnveloped PKCS #7 Message Information:");
            Console.WriteLine(
                "\tThe number of recipients for the Enveloped PKCS #7 " +
                "is:  {0}",
                e.RecipientInfos.Count);
            for (int i = 0; i < e.RecipientInfos.Count; i++)
            {
                Console.WriteLine(
                    "\tRecipient #{0} has type {1}.",
                    i + 1,
                    e.RecipientInfos[i].RecipientIdentifier.Type);
            }
            if (displayContent)
            {
                DisplayEnvelopedCmsContent("Enveloped PKCS #7 Content", e);
            }
            Console.WriteLine();
        }
    }
}

Voir aussi

Tâches

Procédure de signature de messages par un signataire
Procédure de signature d'un message par plusieurs signataires
Procédure de contre-signature d'un message
Procédure de mise d'un message sous enveloppe pour un seul destinataire
Procédure de mise d'un message sous enveloppe pour plusieurs destinataires

Footer image

Copyright ©2007 par Microsoft Corporation. Tous droits réservés.