/// <summary> /// Creates a certificate using the established subject, key, and optional /// extensions using the specified certificate as the issuer. /// </summary> /// <param name="issuerCertificate">Certificate instance representing the issuing /// Certificate Authority (CA).</param> /// <param name="issuerPrivatekey">Key representing the private key of the issuing /// certificate authority. /// <param name="notBefore">The oldest date and time when this certificate is considered /// valid. Typically UtcNow, plus or minus a few seconds.</param> /// <param name="notAfter">The date and time when this certificate is no longer considered /// valid.</param> /// <param name="serialNumber">The serial number to use for the new certificate. /// This value should be unique per issuer. The value is interpreted as /// an unsigned integer of arbitrary size in big-endian byte ordering. /// RFC 3280 recommends confining it to 20 bytes or less.</param> /// <returns>A Certificate with the specified values. The returned object /// won't assert HasPrivateKey.</returns> public PkiCertificate Create(PkiCertificate issuerCertificate, PkiKey issuerPrivateKey, DateTimeOffset notBefore, DateTimeOffset notAfter, byte[] serialNumber) { var isur = new X509Name(issuerCertificate.SubjectName); var name = new X509Name(SubjectName); return(Create(isur, issuerPrivateKey, name, notBefore, notAfter, serialNumber)); }
internal static bool Verify(string algor, PkiKey pub, byte[] input, byte[] sig) { // Based on: // http://mytenpennies.wikidot.com/blog:using-bouncy-castle var signer = SignerUtilities.GetSigner(algor); signer.Init(false, pub.NativeKey); signer.BlockUpdate(input, 0, input.Length); return(signer.VerifySignature(sig)); }
internal static byte[] Sign(string algor, PkiKey prv, byte[] input, int transcodeLength = 0) { // Based on: // http://mytenpennies.wikidot.com/blog:using-bouncy-castle var signer = SignerUtilities.GetSigner(algor); signer.Init(true, prv.NativeKey); signer.BlockUpdate(input, 0, input.Length); var sig = signer.GenerateSignature(); if (transcodeLength != 0) { sig = TranscodeSignatureToConcat(sig, transcodeLength); } return(sig); }
public PkiCertificateSigningRequest(PkiEncodingFormat format, byte[] encoded, PkiHashAlgorithm hashAlgorithm) { Pkcs10CertificationRequest pkcs10; switch (format) { case PkiEncodingFormat.Pem: var encodedString = Encoding.UTF8.GetString(encoded); using (var sr = new StringReader(encodedString)) { var pemReader = new PemReader(sr); pkcs10 = pemReader.ReadObject() as Pkcs10CertificationRequest; if (pkcs10 == null) { throw new Exception("invalid PEM object is not PKCS#10 archive"); } } break; case PkiEncodingFormat.Der: pkcs10 = new Pkcs10CertificationRequest(encoded); break; default: throw new NotSupportedException(); } var info = pkcs10.GetCertificationRequestInfo(); var nativePublicKey = pkcs10.GetPublicKey(); var rsaKey = nativePublicKey as RsaKeyParameters; var ecdsaKey = nativePublicKey as ECPublicKeyParameters; if (rsaKey != null) { PublicKey = new PkiKey(nativePublicKey, PkiAsymmetricAlgorithm.Rsa); } else if (ecdsaKey != null) { PublicKey = new PkiKey(nativePublicKey, PkiAsymmetricAlgorithm.Ecdsa); } else { throw new NotSupportedException("unsupported asymmetric algorithm key"); } SubjectName = info.Subject.ToString(); HashAlgorithm = hashAlgorithm; // // // Based on: // // // http://forum.rebex.net/4284/pkcs10-certificate-request-example-provided-castle-working // // var extGen = new X509ExtensionsGenerator(); // // foreach (var ext in CertificateExtensions) // // { // // extGen.AddExtension(ext.Identifier, ext.IsCritical, ext.Value); // // } // // var attr = new AttributeX509(PkcsObjectIdentifiers.Pkcs9AtExtensionRequest, // // new DerSet(extGen.Generate())); // Based on: // http://unitstep.net/blog/2008/10/27/extracting-x509-extensions-from-a-csr-using-the-bouncy-castle-apis/ // https://stackoverflow.com/q/24448909/5428506 foreach (var attr in info.Attributes.ToArray()) { if (attr is DerSequence derSeq && derSeq.Count == 2) { var attrX509 = AttributeX509.GetInstance(attr); if (object.Equals(attrX509.AttrType, PkcsObjectIdentifiers.Pkcs9AtExtensionRequest)) { // The `Extension Request` attribute is present. // The X509Extensions are contained as a value of the ASN.1 Set. // Assume that it is the first value of the set. if (attrX509.AttrValues.Count >= 1) { var csrExts = X509Extensions.GetInstance(attrX509.AttrValues[0]); foreach (var extOid in csrExts.GetExtensionOids()) { if (object.Equals(extOid, X509Extensions.SubjectAlternativeName)) { var ext = csrExts.GetExtension(extOid); var extVal = ext.Value; var der = extVal.GetDerEncoded(); // The ext value, which is an ASN.1 Octet String, **MIGHT** be tagged with // a leading indicator that it's an Octet String and its length, so we want // to remove it if that's the case to extract the GeneralNames collection if (der.Length > 2 && der[0] == 4 && der[1] == der.Length - 2) { der = der.Skip(2).ToArray(); } var asn1obj = Asn1Object.FromByteArray(der); var gnames = GeneralNames.GetInstance(asn1obj); CertificateExtensions.Add(new PkiCertificateExtension { Identifier = extOid, IsCritical = ext.IsCritical, Value = gnames, }); } } // No need to search any more. break; } } } } }
internal PkiCertificate Create(X509Name issuerName, PkiKey issuerPrivateKey, X509Name subjectName, DateTimeOffset notBefore, DateTimeOffset notAfter, byte[] serialNumber, X509KeyUsage keyUsage = null, KeyPurposeID[] extKeyUsage = null) { // Based on: // https://stackoverflow.com/a/39456955/5428506 // https://github.com/bcgit/bc-csharp/blob/master/crypto/test/src/test/CertTest.cs var pubKey = _keyPair?.PublicKey.NativeKey ?? PublicKey.NativeKey; var sigFactory = ComputeSignatureAlgorithm(issuerPrivateKey.NativeKey); var certGen = new X509V3CertificateGenerator(); certGen.SetSerialNumber(new BigInteger(serialNumber)); certGen.SetIssuerDN(issuerName); certGen.SetSubjectDN(subjectName); certGen.SetNotBefore(notBefore.UtcDateTime); certGen.SetNotAfter(notAfter.UtcDateTime); certGen.SetPublicKey(pubKey); if (keyUsage == null) { keyUsage = new X509KeyUsage(X509KeyUsage.KeyEncipherment | X509KeyUsage.DigitalSignature); } if (extKeyUsage == null) { extKeyUsage = new[] { KeyPurposeID.IdKPClientAuth, KeyPurposeID.IdKPServerAuth } } ; certGen.AddExtension("2.5.29.15", true, keyUsage); certGen.AddExtension("2.5.29.37", true, new DerSequence(extKeyUsage)); // Based on: // https://boredwookie.net/blog/bouncy-castle-add-a-subject-alternative-name-when-creating-a-cer foreach (var ext in CertificateExtensions) { // certGen.AddExtension(ext.Identifier, ext.Value.IsCritical, ext.Value.Value); certGen.AddExtension(ext.Identifier, ext.IsCritical, ext.Value); } var bcCert = certGen.Generate(sigFactory); return(new PkiCertificate { NativeCertificate = bcCert, }); // Compare to LE-issued Certs: // Enhanced Key Usage: // Server Authentication (1.3.6.1.5.5.7.3.1) // Client Authentication (1.3.6.1.5.5.7.3.2) // Subject Key Identifier: // e05bf2ba81d8d3845ff45b5638551e64ca19133d // Authority Key Identifier: // KeyID=a84a6a63047dddbae6d139b7a64565eff3a8eca1 // Authority Information Access: // [1]Authority Info Access // Access Method=On-line Certificate Status Protocol (1.3.6.1.5.5.7.48.1) // Alternative Name: // URL=http://ocsp.int-x3.letsencrypt.org // [2]Authority Info Access // Access Method=Certification Authority Issuer (1.3.6.1.5.5.7.48.2) // Alternative Name: // URL=http://cert.int-x3.letsencrypt.org/ // Certificate Policies: // ... // SCT List: // ... // Key Usage: // Digital Signature, Key Encipherment (a0) // Basic Constraints: // Subject Type=End Entity // Path Length Constraint=None // CA: // Key Usage: // Digital Signature, Certificate Signing, Off-line CRL Signing, CRL Signing (86) }
public byte[] Export(PkiArchiveFormat format, PkiKey privateKey = null, IEnumerable <PkiCertificate> chain = null, char[] password = null) { // Based on: // https://stackoverflow.com/a/44798441/5428506 switch (format) { case PkiArchiveFormat.Pem: using (var buff = new MemoryStream()) { byte[] bytes = privateKey?.Export(PkiEncodingFormat.Pem, password); if (bytes != null) { buff.Write(bytes, 0, bytes.Length); } bytes = Export(PkiEncodingFormat.Pem); buff.Write(bytes, 0, bytes.Length); if (chain != null) { foreach (var c in chain) { bytes = c.Export(PkiEncodingFormat.Pem); buff.Write(bytes, 0, bytes.Length); } } return(buff.ToArray()); } case PkiArchiveFormat.Pkcs12: var alias = AliasOf(this); var store = new Pkcs12StoreBuilder().Build(); if (privateKey != null) { store.SetKeyEntry(alias, new AsymmetricKeyEntry(privateKey.NativeKey), new[] { new X509CertificateEntry(NativeCertificate) }); } else { store.SetCertificateEntry(alias, new X509CertificateEntry(NativeCertificate)); } if (chain != null) { foreach (var c in chain) { store.SetCertificateEntry(AliasOf(c), new X509CertificateEntry(c.NativeCertificate)); } } using (var buff = new MemoryStream()) { store.Save(buff, password ?? new char[0], new SecureRandom()); return(buff.ToArray()); } default: throw new NotSupportedException(); } }
public RecoverableSerialForm(PkiKey key) { _algorithm = key.Algorithm; _key = key.Export(PkiEncodingFormat.Der); _isPrivate = key.IsPrivate; }