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
}
}