방법: 메시지 서명 및 포함
이 예제에서는 System.Security.Cryptography.Pkcs를 사용하여 CMS/PKCS #7 포함 및 서명된 메시지를 만듭니다. 메시지를 먼저 한 명의 서명자가 서명한 다음 한 명의 받는 사람에 대해 암호화합니다. 그런 다음 받는 사람의 개인 키를 사용하여 메시지를 해독하고 서명을 확인합니다. 이 예제에서는 SignedCms 개체를 사용하여 한 명 이상의 서명자가 메시지에 서명 및 연대 서명할 수 있습니다. 또한 EnvelopedCms 개체를 사용하여 한 명 이상의 받는 사람에 대해 메시지를 암호화하거나 포함할 수도 있습니다.
예제
설명
이 예제에서는 다음 클래스를 사용합니다.
EnvelopedCms
SignedCms
단일 컴퓨터에서 실행하려면 이 예제에서는 다음 사항이 필요합니다.
주체 이름이 "MessageSigner1"인 공개 키 인증서 하나를 내 인증서 저정소에 배치합니다.
주체 이름이 "Recipient1"인 다른 인증서를 AddressBook 및 내 인증서 저장소 모두에 배치합니다.
관련된 개인 키를 단일 컴퓨터에 저장합니다.
이 예제 코드에서는 먼저 메시지 보낸 사람 역할을 수행한 다음 메시지 받는 사람 역할을 수행합니다. 이 예제에서는 각 역할에 같은 공개 키 자격 증명을 사용합니다. 따라서 주체 이름 "Recipient1"에 대한 공개 키 자격 증명이 두 개의 저장소에 있어야 합니다. 보낸 사람으로서 AddressBook 인증서 저장소에서 받는 사람 인증서를 검색하고 이를 사용하여 메시지를 암호화합니다. 받는 사람으로서 내 인증서 저장소에서 인증서를 검색하고 연관된 개인 키를 사용하여 메시지를 해독합니다.
참고: |
---|
이 예제는 예시 목적으로만 사용됩니다. 프로덕션 환경에서는 메시지를 보낸 사람 및 받는 사람이 고유한 공개 키 자격 증명을 사용하여 서로 다른 프로세스에서 실행하는 다른 모델을 사용할 수 있습니다. |
이를 위한 여러 방법 중 하나로서 Makecert.exe 유틸리티를 사용하여 이 예제를 설정합니다. Certificate Creation Tool (Makecert.exe)는 인증서를 테스트하는 편리한 유틸리티입니다. 프로덕션 환경에서는 인증 기관에서 인증서를 생성합니다.
다음 Makecert 명령은 필요한 공개 키 인증서 및 개인 키를 생성합니다.
Makecert -n "CN=MessageSigner1" -ss My
Makecert -n "CN=Recipient1" -sky exchange -ss M
이러한 명령은 내 인증서 저장소에 적절한 공개 키 인증서를 배치합니다. AddressBook 인증서 저장소에서 주체 이름 "Recipient1"에 대한 공개 키를 가져오려면 방법: 공개 키 인증서 내보내기 및 가져오기의 절차에 따라 공개 키 인증서를 내보낸 다음 AddressBook 저장소로 가져옵니다.
코드
// 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();
}
}
}
참고 항목
작업
방법: 한 명의 서명자가 메시지 서명
방법: 여러 명의 서명자가 메시지 서명
방법: 메시지 연대 서명
방법: 한 명의 받는 사람의 메시지 포함
방법: 여러 명의 받는 사람의 메시지 포함
Copyright © 2007 by Microsoft Corporation. All rights reserved.