■ X.509 인증서 만들기
------------------------------------------------------------------------------------------------------------------------
▶ 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 } }
|
▶ 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 } }
|
▶ 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
|
'C# > Common' 카테고리의 다른 글
[C#/COMMON] String 클래스 : Format 정적 메소드를 사용해 문자열 정렬하기 (0) | 2020.12.05 |
---|---|
[C#/COMMON] Convert 클래스 : ToInt32 정적 메소드를 사용해 16진수 문자열에서 10진수 문자열 구하기 (0) | 2020.12.05 |
[C#/COMMON] Convert 클래스 : ToString 정적 메소드를 사용해 10진수 문자열에서 16진수 문자열 구하기 (0) | 2020.12.05 |
[C#/COMMON] 암스트롱 수 여부 구하기 (0) | 2020.11.23 |
[C#/COMMON] Enum 클래스 : IsDefined 정적 메소드를 사용해 정의된 열거형 값 여부 구하기 (0) | 2020.11.17 |
[C#/COMMON] X.509 인증서 만들기 (0) | 2020.11.15 |
[C#/COMMON] DateTime 구조체 : W3C 날짜/시간 문자열에서 날짜/시간 구하기 (0) | 2020.11.12 |
[C#/COMMON] DateTime 구조체 : W3C 날짜/시간 문자열 구하기 (0) | 2020.11.12 |
[C#/COMMON] DirectoryInfo 클래스 : Delete 메소드를 사용해 하위 디렉토리 및 파일 포함 디렉토리 삭제하기 (0) | 2020.10.24 |
[C#/COMMON] DirectoryInfo 클래스 : Create 메소드를 사용해 하위 디렉토리 일괄 생성하기 (0) | 2020.10.24 |
[C#/COMMON] 누겟 설치 : AspNetWebApi.SelfHost (0) | 2020.10.09 |
댓글을 달아 주세요