Compartir a través de


Cómo firmar y envolver mensajes

Este ejemplo crea un mensaje firmado CMS/PKCS #7 envuelto con System.Security.Cryptography.Pkcs. En primer lugar, el mensaje lo firma un único firmante y, a continuación, se cifra para un único destinatario. Después, se descifra con la clave privada del destinatario y se comprueba la firma. El ejemplo utiliza el objeto de SignedCms, que permite que uno o varios firmantes firmen y contrafirmen un mensaje. También utiliza el objeto de EnvelopedCms, que permite cifrar mensajes para uno o varios destinatarios, lo que también se denomina envolverlos.

Ejemplo

Descripción

Este ejemplo utiliza las siguientes clases:

Para ejecutarlo en un equipo individual, este ejemplo requiere:

  • Un certificado de clave pública en el que el nombre de sujeto "MessageSigner1" esté en el almacén de certificados de My.

  • Otro certificado en el que el nombre de sujeto "Recipient1" se encuentre en los almacenes de certificados de AddressBook y My.

  • Que las claves privadas asociadas estén almacenadas en el equipo individual.

El código de ejemplo primero actúa como remitente del mensaje y, después, como destinatario del mismo. El ejemplo utiliza las mismas credenciales de clave pública en todas las funciones. Como tal, el ejemplo requiere que el certificado de clave pública del nombre de sujeto "Recipient1" esté en dos almacenes. Como remitente, busca el certificado del destinatario en el almacén de certificados de AddressBook y lo utiliza para cifrar el mensaje. Como destinatario, busca el certificado en el almacén de certificados de My y utiliza la clave privada asociada para descifrar el mensaje.

Nota:

Este ejemplo sólo tiene fines ilustrativos. Los entornos de producción pueden utilizar un modelo diferente en el que el remitente y el destinatario del mensaje se ejecuten en procesos diferentes con sus credenciales de clave pública únicas.

Configure este ejemplo con la utilidad Makecert.exe, que es una de las distintas formas de hacerlo. Certificate Creation Tool (Makecert.exe) resulta muy cómodo para probar certificados. En un entorno de producción, una entidad de certificación genera los certificados.

Los siguientes comandos de Makecert generan los certificados de clave pública y las claves privadas necesarias.

Makecert -n "CN=MessageSigner1" -ss My

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

Estos comandos colocan los certificados de clave pública apropiados en el almacén de certificados de My. Para obtener el certificado de clave pública del nombre de sujeto "Recipient1" en el almacén de certificados de AddressBook, exporte el certificado de clave pública y, seguidamente, impórtelo en el almacén de AddressBook, para lo que debe seguir el procedimiento de Cómo importar y exportar certificados de clave pública.

Código

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

Consulte también

Tareas

Cómo firmar mensajes con un firmante
Cómo firmar un mensaje con varios firmantes
Cómo contrafirmar un mensaje
Cómo envolver un mensaje para un destinatario
Cómo envolver un mensaje para varios destinatarios

Copyright © 2007 Microsoft Corporation. Reservados todos los derechos.