[OS/UBUNTU] openssl 명령 : 인증서 파일 생성하기
■ openssl 명령을 사용해 인증서 파일을 생성하는 방법을 보여준다. 1. CTRL + ALT + T 키를 눌러서 [터미널]을 실행한다. 2. [터미널]에서 아래
■ openssl 명령을 사용해 인증서 파일을 생성하는 방법을 보여준다. 1. CTRL + ALT + T 키를 눌러서 [터미널]을 실행한다. 2. [터미널]에서 아래
■ HttpClient 클래스 사용시 특정 인증서 오류를 무시하는 방법을 보여준다. ▶ 예제 코드 (C#)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
using System.Net.Security; using(HttpClientHandler httpClientHandler = new HttpClientHandler()) { httpClientHandler.ServerCertificateCustomValidationCallback = (message, certificates, chain, sslPolicyErrors) => { if(sslPolicyErrors == SslPolicyErrors.None) { return true; } if(certificates.GetCertHashString() == "99E92D8447AEF30483B1D7527812C9B7B3A915A7") { return true; } return false; }; using(HttpClient httpClient = new HttpClient(httpClientHandler)) { HttpResponseMessage responseMessage = httpClient.GetAsync("https://example.com").Result; } } |
■ ServicePointManager 클래스의 ServerCertificateValidationCallback 속성을 사용해 특정 인증서 오류를 무시하는 방법을 보여준다. ▶ 예제 코드 (C#)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; ServicePointManager.ServerCertificateValidationCallback += delegate (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { if(sslPolicyErrors == SslPolicyErrors.None) { return true; } if(certificate.GetCertHashString() == "99E92D8447AEF30483B1D7527812C9B7B3A915A7") { return true; } return false; }; |
■ BouncyCastle 누겟을 설치하는 방법을 보여준다. 1. Visual Studio를 실행한다. 2. [도구] / [NuGet 패키지 관리자] / [패키지 관리자 콘솔] 메뉴를 실행한다.
■ X.509 인증서를 만드는 방법을 보여준다. ▶ Program.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
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 } } |
※ 관리자 모드에서 실행해야