in src/System.Private.ServiceModel/tools/CertificateGenerator/CertificateGenerator.cs [237:458]
private X509CertificateContainer CreateCertificate(bool isAuthority, bool isMachineCert, X509Certificate signingCertificate, CertificateCreationSettings certificateCreationSettings)
{
if (certificateCreationSettings == null)
{
if (isAuthority)
{
certificateCreationSettings = new CertificateCreationSettings();
}
else
{
throw new Exception("Parameter certificateCreationSettings cannot be null when isAuthority is false");
}
}
// Set to default cert creation settings if not set
if (certificateCreationSettings.ValidityNotBefore == default(DateTime))
{
certificateCreationSettings.ValidityNotBefore = _defaultValidityNotBefore;
}
if (certificateCreationSettings.ValidityNotAfter == default(DateTime))
{
certificateCreationSettings.ValidityNotAfter = _defaultValidityNotAfter;
}
if (!isAuthority ^ (signingCertificate != null))
{
throw new ArgumentException("Either isAuthority == true or signingCertificate is not null");
}
string subject = certificateCreationSettings.Subject;
// If certificateCreationSettings.SubjectAlternativeNames == null, then we should add exactly one SubjectAlternativeName == Subject
// so that the default certificate generated is compatible with mainline scenarios
// However, if certificateCreationSettings.SubjectAlternativeNames == string[0], then allow this as this is a legit scenario we want to test out
if (certificateCreationSettings.SubjectAlternativeNames == null)
{
certificateCreationSettings.SubjectAlternativeNames = new string[1] { subject };
}
string[] subjectAlternativeNames = certificateCreationSettings.SubjectAlternativeNames;
if (!isAuthority && string.IsNullOrWhiteSpace(subject))
{
throw new ArgumentException("Certificate Subject must not be an empty string or only whitespace", "creationSettings.Subject");
}
EnsureInitialized();
s_certGenerator.Reset();
// Tag on the generation time to prevent caching of the cert CRL in Linux
X509Name authorityX509Name = CreateX509Name(string.Format("{0} {1}", _authorityCanonicalName, DateTime.Now.ToString("s")));
var serialNum = new BigInteger(64 /*sizeInBits*/, _random).Abs();
var keyPair = isAuthority ? _authorityKeyPair : _keyPairGenerator.GenerateKeyPair();
if (isAuthority)
{
s_certGenerator.SetIssuerDN(authorityX509Name);
s_certGenerator.SetSubjectDN(authorityX509Name);
var authorityKeyIdentifier = new AuthorityKeyIdentifier(
SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(_authorityKeyPair.Public),
new GeneralNames(new GeneralName(authorityX509Name)),
serialNum);
s_certGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, authorityKeyIdentifier);
s_certGenerator.AddExtension(X509Extensions.KeyUsage, false, new KeyUsage(X509KeyUsage.DigitalSignature | X509KeyUsage.KeyAgreement | X509KeyUsage.KeyCertSign | X509KeyUsage.KeyEncipherment | X509KeyUsage.CrlSign));
}
else
{
X509Name subjectName = CreateX509Name(subject);
s_certGenerator.SetIssuerDN(PrincipalUtilities.GetSubjectX509Principal(signingCertificate));
s_certGenerator.SetSubjectDN(subjectName);
s_certGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(_authorityKeyPair.Public));
s_certGenerator.AddExtension(X509Extensions.KeyUsage, false, new KeyUsage(X509KeyUsage.DigitalSignature | X509KeyUsage.KeyAgreement | X509KeyUsage.KeyEncipherment));
}
s_certGenerator.AddExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(keyPair.Public));
s_certGenerator.SetSerialNumber(serialNum);
s_certGenerator.SetNotBefore(certificateCreationSettings.ValidityNotBefore);
s_certGenerator.SetNotAfter(certificateCreationSettings.ValidityNotAfter);
s_certGenerator.SetPublicKey(keyPair.Public);
s_certGenerator.AddExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(isAuthority));
if (certificateCreationSettings.EKU == null || certificateCreationSettings.EKU.Count == 0)
{
s_certGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, false, new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth, KeyPurposeID.IdKPClientAuth));
}
else
{
s_certGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, false, new ExtendedKeyUsage(certificateCreationSettings.EKU));
}
if (!isAuthority)
{
if (isMachineCert)
{
List<Asn1Encodable> subjectAlternativeNamesAsAsn1EncodableList = new List<Asn1Encodable>();
// All endpoints should also be in the Subject Alt Names
for (int i = 0; i < subjectAlternativeNames.Length; i++)
{
if (!string.IsNullOrWhiteSpace(subjectAlternativeNames[i]))
{
// Machine certs can have additional DNS names
subjectAlternativeNamesAsAsn1EncodableList.Add(new GeneralName(GeneralName.DnsName, subjectAlternativeNames[i]));
}
}
s_certGenerator.AddExtension(X509Extensions.SubjectAlternativeName, true, new DerSequence(subjectAlternativeNamesAsAsn1EncodableList.ToArray()));
}
else
{
if (subjectAlternativeNames.Length > 1)
{
var subjectAlternativeNamesAsAsn1EncodableList = new Asn1EncodableVector();
// Only add a SAN for the user if there are any
for (int i = 1; i < subjectAlternativeNames.Length; i++)
{
if (!string.IsNullOrWhiteSpace(subjectAlternativeNames[i]))
{
Asn1EncodableVector otherNames = new Asn1EncodableVector();
otherNames.Add(new DerObjectIdentifier(_upnObjectId));
otherNames.Add(new DerTaggedObject(true, 0, new DerUtf8String(subjectAlternativeNames[i])));
Asn1Object genName = new DerTaggedObject(false, 0, new DerSequence(otherNames));
subjectAlternativeNamesAsAsn1EncodableList.Add(genName);
}
}
s_certGenerator.AddExtension(X509Extensions.SubjectAlternativeName, true, new DerSequence(subjectAlternativeNamesAsAsn1EncodableList));
}
}
}
if (isAuthority || certificateCreationSettings.IncludeCrlDistributionPoint)
{
var crlDistributionPoints = new DistributionPoint[1]
{
new DistributionPoint(
new DistributionPointName(
new GeneralNames(
new GeneralName(
GeneralName.UniformResourceIdentifier, string.Format("{0}", _crlUri, serialNum.ToString(radix: 16))))),
null,
null)
};
var revocationListExtension = new CrlDistPoint(crlDistributionPoints);
s_certGenerator.AddExtension(X509Extensions.CrlDistributionPoints, false, revocationListExtension);
}
ISignatureFactory signatureFactory = new Asn1SignatureFactory(_signatureAlgorithm, _authorityKeyPair.Private, _random);
X509Certificate cert = s_certGenerator.Generate(signatureFactory);
switch (certificateCreationSettings.ValidityType)
{
case CertificateValidityType.Revoked:
RevokeCertificateBySerialNumber(serialNum.ToString(radix: 16));
break;
case CertificateValidityType.Expired:
break;
default:
EnsureCertificateIsValid(cert);
break;
}
// For now, given that we don't know what format to return it in, preserve the formats so we have
// the flexibility to do what we need to
X509CertificateContainer container = new X509CertificateContainer();
X509CertificateEntry[] chain = new X509CertificateEntry[1];
chain[0] = new X509CertificateEntry(cert);
Pkcs12Store store = new Pkcs12StoreBuilder().Build();
store.SetKeyEntry(
certificateCreationSettings.FriendlyName != null ? certificateCreationSettings.FriendlyName : string.Empty,
new AsymmetricKeyEntry(keyPair.Private),
chain);
using (MemoryStream stream = new MemoryStream())
{
store.Save(stream, _password.ToCharArray(), _random);
container.Pfx = stream.ToArray();
}
X509Certificate2 outputCert;
if (isAuthority)
{
// don't hand out the private key for the cert when it's the authority
outputCert = new X509Certificate2(cert.GetEncoded());
}
else
{
// Otherwise, allow encode with the private key. note that X509Certificate2.RawData will not provide the private key
// you will have to re-export this cert if needed
outputCert = new X509Certificate2(container.Pfx, _password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
}
container.Subject = subject;
container.InternalCertificate = cert;
container.Certificate = outputCert;
container.Thumbprint = outputCert.Thumbprint;
Trace.WriteLine("[CertificateGenerator] generated a certificate:");
Trace.WriteLine(string.Format(" {0} = {1}", "isAuthority", isAuthority));
if (!isAuthority)
{
Trace.WriteLine(string.Format(" {0} = {1}", "Signed by", signingCertificate.SubjectDN));
Trace.WriteLine(string.Format(" {0} = {1}", "Subject (CN) ", subject));
Trace.WriteLine(string.Format(" {0} = {1}", "Subject Alt names ", string.Join(", ", subjectAlternativeNames)));
Trace.WriteLine(string.Format(" {0} = {1}", "Friendly Name ", certificateCreationSettings.FriendlyName));
}
Trace.WriteLine(string.Format(" {0} = {1}", "HasPrivateKey:", outputCert.HasPrivateKey));
Trace.WriteLine(string.Format(" {0} = {1}", "Thumbprint", outputCert.Thumbprint));
Trace.WriteLine(string.Format(" {0} = {1}", "CertificateValidityType", certificateCreationSettings.ValidityType));
return container;
}