첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
본 블로그는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 블로그 콘텐츠 향상을 위해 쓰여집니다.

728x90
반응형
728x170

TestProject.zip
다운로드

▶ Program.cs

using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;

namespace TestProject
{
    /// <summary>
    /// 프로그램
    /// </summary>
    class Program
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Private

        #region 프로그램 시작하기 - Main()

        /// <summary>
        /// 프로그램 시작하기
        /// </summary>
        private static void Main()
        {
            bool result = NetworkHelper.DeletePortBinding(443);

            Console.WriteLine("포트 바인딩 삭제 결과 : " + result);

            CertificateKeyPair rootCertificateKeyPair;

            DateTime fromDate = DateTime.Now.AddDays(-1);
            DateTime toDate   = fromDate.AddYears(1);

            X509Certificate2 rootCertificate = CertificateHelper.CreateRootCertificate
            (
                "CN=TestRootCA",
                fromDate,
                toDate,
                out rootCertificateKeyPair
            );

            List<string> sanList = new List<string>();

            sanList.Add("github.com");

            X509Certificate2 certificate = CertificateHelper.CreateCertificate
            (
                rootCertificate,
                rootCertificateKeyPair,
                "CN=Test",
                fromDate,
                toDate,
                sanList
            );

            CertificateHelper.Store(rootCertificate, StoreName.AuthRoot, StoreLocation.LocalMachine);

            CertificateHelper.Store(certificate, StoreName.My, StoreLocation.CurrentUser);

            result = NetworkHelper.BindToPort(certificate, 443, "A613FCA4-51D2-4C33-8377-362BB6D54A00");

            Console.WriteLine("포트 바인딩 결과 : " + result);

            result = NetworkHelper.FlushDNS();

            Console.WriteLine("DNS 플러시 결과 : " + result);

            CertificateHelper.SaveToCer(certificate, "d:\\test.cer");
            CertificateHelper.SaveToPfx(certificate, "d:\\test.pfx");
        }

        #endregion
    }
}

 

728x90

 

▶ NetworkHelper.cs

using System.Diagnostics;
using System.Security.Cryptography.X509Certificates;

namespace TestProject
{
    /// <summary>
    /// 네트워크 헬퍼
    /// </summary>
    public static class NetworkHelper
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 포트 바인딩하기 - BindToPort(x509Certificate2, port, applicationID)

        /// <summary>
        /// 포트 바인딩하기
        /// </summary>
        /// <param name="x509Certificate2">X.509 인증서 2</param>
        /// <param name="port">포트</param>
        /// <param name="applicationID">애플리케이션 ID</param>
        /// <returns>처리 결과</returns>
        public static bool BindToPort(X509Certificate2 x509Certificate2, int port, string applicationID)
        {
            string command = string.Format
            (
                "http add sslcert ipport=0.0.0.0:{0} certhash={1} appid={{{2}}}",
                port,
                x509Certificate2.Thumbprint,
                applicationID
            );

            Process process = new Process();

            process.StartInfo.FileName    = "netsh.exe";
            process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            process.StartInfo.Arguments   = command;

            process.Start();

            process.WaitForExit();

            return process.ExitCode == 0;
        }

        #endregion
        #region 포트 바인딩 삭제하기 - DeletePortBinding(port)

        /// <summary>
        /// 포트 바인딩 삭제하기
        /// </summary>
        /// <param name="port">포트</param>
        /// <returns>처리 결과</returns>
        public static bool DeletePortBinding(int port)
        {
            string command = string.Format("http delete sslcert ipport=0.0.0.0:{0}", port);

            Process process = new Process();

            process.StartInfo.FileName    = "netsh.exe";
            process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            process.StartInfo.Arguments   = command;

            process.Start();

            process.WaitForExit();

            return process.ExitCode == 0;
        }

        #endregion
        #region DNS 캐시 지우기 - FlushDNS()

        /// <summary>
        /// DNS 캐시 지우기
        /// </summary>
        /// <returns>처리 결과</returns>
        public static bool FlushDNS()
        {
            string command = string.Format("/flushdns");

            Process process = new Process();

            process.StartInfo.FileName    = "ipconfig.exe";
            process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            process.StartInfo.Arguments   = command;

            process.Start();

            process.WaitForExit();

            return process.ExitCode == 0;
        }

        #endregion
    }
}

 

300x250

 

▶ CertificateKeyPair.cs

using Org.BouncyCastle.Crypto;

namespace TestProject
{
    /// <summary>
    /// 인증서 키 쌍
    /// </summary>
    public class CertificateKeyPair
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 비대칭 암호화 키 쌍
        /// </summary>
        private AsymmetricCipherKeyPair asymmetricCipherKeyPair;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 개인 키 - PrivateKey

        /// <summary>
        /// 개인 키
        /// </summary>
        public AsymmetricKeyParameter PrivateKey
        {
            get
            {
                return this.asymmetricCipherKeyPair.Private;
            }
        }

        #endregion
        #region 공개 키 - PublicKey

        /// <summary>
        /// 공개 키
        /// </summary>
        public AsymmetricKeyParameter PublicKey
        {
            get
            {
                return this.asymmetricCipherKeyPair.Public;
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - CertificateKeyPair(asymmetricCipherKeyPair)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="asymmetricCipherKeyPair">비대칭 암호화 키 쌍</param>
        public CertificateKeyPair(AsymmetricCipherKeyPair asymmetricCipherKeyPair)
        {
            this.asymmetricCipherKeyPair = asymmetricCipherKeyPair;
        }

        #endregion
    }
}

 

▶ CertificateHelper.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.X509.Extension;
using Org.BouncyCastle.Asn1;

using X509Certificate = Org.BouncyCastle.X509.X509Certificate;

namespace TestProject
{
    /// <summary>
    /// 인증서 헬퍼
    /// </summary>
    public class CertificateHelper
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 루트 인증서 생성하기 - CreateRootCertificate(domainName, fromDate, toDate, certificateKeyPair, keySize)

        /// <summary>
        /// 루트 인증서 생성하기
        /// </summary>
        /// <param name="domainName">도메인명</param>
        /// <param name="fromDate">FROM 날짜</param>
        /// <param name="toDate">TO 날짜</param>
        /// <param name="certificateKeyPair">인증서 키 쌍</param>
        /// <param name="keySize">키 크기</param>
        /// <returns>X.509 인증서 2</returns>
        public static X509Certificate2 CreateRootCertificate
        (
            string                 domainName,
            DateTime               fromDate,
            DateTime               toDate,
            out CertificateKeyPair certificateKeyPair,
            int                    keySize = 1024
        )
        {
            RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator();

            rsaKeyPairGenerator.Init(new KeyGenerationParameters(new SecureRandom(), keySize));

            AsymmetricCipherKeyPair cipherKeyPair = rsaKeyPairGenerator.GenerateKeyPair();

            X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();

            certificateGenerator.SetSerialNumber(BigInteger.ProbablePrime(120, new Random()));
            certificateGenerator.SetIssuerDN(new X509Name(domainName));
            certificateGenerator.SetNotBefore(fromDate);
            certificateGenerator.SetNotAfter(toDate);
            certificateGenerator.SetSubjectDN(new X509Name(domainName));
            certificateGenerator.SetPublicKey(cipherKeyPair.Public);
            certificateGenerator.SetSignatureAlgorithm("SHA1WithRSAEncryption");

            X509Certificate certificate = certificateGenerator.Generate
            (
                cipherKeyPair.Private,
                new SecureRandom()
            );

            certificate.Verify(cipherKeyPair.Public);

            certificateKeyPair = new CertificateKeyPair(cipherKeyPair);

            return new X509Certificate2(DotNetUtilities.ToX509Certificate(certificate));
        }

        #endregion
        #region 인증서 생성하기 - CreateCertificate(rootCertificate, certificateKeyPair, domainName, fromDate, toDate, sanEnumerable, keySize)

        /// <summary>
        /// 인증서 생성하기
        /// </summary>
        /// <param name="rootCertificate">루트 인증서</param>
        /// <param name="certificateKeyPair">인증서 키 쌍</param>
        /// <param name="domainName">도메인명</param>
        /// <param name="fromDate">FROM 날짜</param>
        /// <param name="toDate">TO 날짜</param>
        /// <param name="sanEnumerable">Subject Alternative Name 열거 가능형</param>
        /// <param name="keySize">키 크기</param>
        /// <returns>X.509 인증서 2</returns>
        public static X509Certificate2 CreateCertificate
        (
            X509Certificate2    rootCertificate,
            CertificateKeyPair  certificateKeyPair,
            string              domainName,
            DateTime            fromDate,
            DateTime            toDate,
            IEnumerable<string> sanEnumerable,
            int                 keySize = 1024
        )
        {
            new RsaKeyPairGenerator().Init(new KeyGenerationParameters(new SecureRandom(), keySize));

            RsaKeyPairGenerator keyPairGenerator = new RsaKeyPairGenerator();

            keyPairGenerator.Init(new KeyGenerationParameters(new SecureRandom(), keySize));

            AsymmetricCipherKeyPair cipherKeyPair = keyPairGenerator.GenerateKeyPair();

            X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();

            certificateGenerator.SetSerialNumber(BigInteger.ProbablePrime(120, new Random()));
            certificateGenerator.SetSubjectDN(new X509Name(domainName));
            certificateGenerator.SetIssuerDN(new X509Name(rootCertificate.SubjectName.Name));
            certificateGenerator.SetNotBefore(fromDate);
            certificateGenerator.SetNotAfter(toDate);
            certificateGenerator.SetPublicKey(cipherKeyPair.Public);
            certificateGenerator.SetSignatureAlgorithm("SHA1WithRSAEncryption");

            Asn1Encodable[] asn1EncodableArray = sanEnumerable.Select
            (
                name => (Asn1Encodable)new GeneralName(GeneralName.DnsName, name)
            ).ToArray();

            DerSequence derSequence = new DerSequence(asn1EncodableArray);

            certificateGenerator.AddExtension
            (
                X509Extensions.SubjectAlternativeName,
                false,
                derSequence
            );

            certificateGenerator.AddExtension
            (
                X509Extensions.AuthorityKeyIdentifier,
                false,
                new AuthorityKeyIdentifierStructure(DotNetUtilities.FromX509Certificate(rootCertificate))
            );

            certificateGenerator.AddExtension
            (
                X509Extensions.SubjectKeyIdentifier,
                false,
                new SubjectKeyIdentifierStructure(cipherKeyPair.Public)
            );

            X509Certificate certificate = certificateGenerator.Generate(certificateKeyPair.PrivateKey);

            certificate.Verify(certificateKeyPair.PublicKey);

            AsymmetricAlgorithm privateKey = GetPrivateKey((RsaPrivateCrtKeyParameters)cipherKeyPair.Private);

            X509Certificate2 certificate2 = new X509Certificate2(DotNetUtilities.ToX509Certificate(certificate));

            certificate2.PrivateKey = privateKey;

            return certificate2;
        }

        #endregion
        #region .cer 파일 저장하기 - SaveToCer(certificate, filePath)

        /// <summary>
        /// .cer 파일 저장하기
        /// </summary>
        /// <param name="certificate">인증서</param>
        /// <param name="filePath">파일 경로</param>
        public static void SaveToCer(X509Certificate2 certificate, string filePath)
        {
            byte[] byteArray = certificate.Export(X509ContentType.Cert);

            File.WriteAllBytes(filePath, byteArray);
        }

        #endregion
        #region .pfx 파일 저장하기 - SaveToPfx(certificate, filePath, password)

        /// <summary>
        /// .pfx 파일 저장하기
        /// </summary>
        /// <param name="certificate">인증서</param>
        /// <param name="filePath">파일 경로</param>
        /// <param name="password">패스워드</param>
        public static void SaveToPfx(X509Certificate2 certificate, string filePath, string password = null)
        {
            byte[] byteArray = certificate.Export(X509ContentType.Pfx, password);

            File.WriteAllBytes(filePath, byteArray);
        }

        #endregion
        #region 저장하기 - Store(certificate, storeName, storeLocation)

        /// <summary>
        /// 저장하기
        /// </summary>
        /// <param name="certificate">인증서</param>
        /// <param name="storeName">저장소명</param>
        /// <param name="storeLocation">저장 위치</param>
        public static void Store(X509Certificate2 certificate, StoreName storeName, StoreLocation storeLocation)
        {
            X509Store store = new X509Store(storeName, storeLocation);

            store.Open(OpenFlags.ReadWrite);

            store.Add(certificate);

            store.Close();
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Private

        #region 개인 키 구하기 - GetPrivateKey(rsaPrivateCrtKeyParameters)

        /// <summary>
        /// 개인 키 구하기
        /// </summary>
        /// <param name="rsaPrivateCrtKeyParameters">RSA 개인 인증서 키 매개 변수</param>
        /// <returns>개인 키</returns>
        private static AsymmetricAlgorithm GetPrivateKey(RsaPrivateCrtKeyParameters rsaPrivateCrtKeyParameters)
        {
            CspParameters cspParameters = new CspParameters
            {
                KeyContainerName = Guid.NewGuid().ToString(),
                KeyNumber        = (int)KeyNumber.Exchange,
                Flags            = CspProviderFlags.UseMachineKeyStore
            };

            RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider(cspParameters);

            RSAParameters rsaParameters = new RSAParameters
            {
                Modulus  = rsaPrivateCrtKeyParameters.Modulus.ToByteArrayUnsigned(),
                P        = rsaPrivateCrtKeyParameters.P.ToByteArrayUnsigned(),
                Q        = rsaPrivateCrtKeyParameters.Q.ToByteArrayUnsigned(),
                DP       = rsaPrivateCrtKeyParameters.DP.ToByteArrayUnsigned(),
                DQ       = rsaPrivateCrtKeyParameters.DQ.ToByteArrayUnsigned(),
                InverseQ = rsaPrivateCrtKeyParameters.QInv.ToByteArrayUnsigned(),
                D        = rsaPrivateCrtKeyParameters.Exponent.ToByteArrayUnsigned(),
                Exponent = rsaPrivateCrtKeyParameters.PublicExponent.ToByteArrayUnsigned()
            };

            rsaCryptoServiceProvider.ImportParameters(rsaParameters);

            return rsaCryptoServiceProvider;
        }

        #endregion
    }
}

 

※ 관리자 모드에서 실행해야 한다.

 

※ 패키지 설치 : BouncyCastle

 

1. 비주얼 스튜디오를 실행한다.

2. 비주얼 스튜디오에서 [도구] / [NuGet 패키지 관리자] / [패키지 관리자 콘솔] 메뉴를 클릭한다.

3. [패키지 관리자 콘솔]에서 아래 명령을 실행한다.

Install-Package BouncyCastle
728x90
반응형
그리드형(광고전용)
Posted by 사용자 icodebroker

댓글을 달아 주세요