/// <summary> /// Create a CertificateRequest for the specified subject name, GOST3410_2012_512 key, and hash algorithm. /// </summary> /// <param name="subjectName"> /// The parsed representation of the subject name for the certificate or certificate request. /// </param> /// <param name="key"> /// n GOST3410 key whose public key material will be included in the certificate or certificate request. /// This key will be used as a private key if <see cref="CreateSelfSigned" /> is called. /// </param> /// <param name="hashAlgorithm"> /// The hash algorithm to use when signing the certificate or certificate request. /// </param> public CertificateRequest(X500DistinguishedName subjectName, Gost3410_2012_512 key, HashAlgorithmName hashAlgorithm) { if (subjectName == null) { throw new ArgumentNullException(nameof(subjectName)); } if (key == null) { throw new ArgumentNullException(nameof(key)); } if (string.IsNullOrEmpty(hashAlgorithm.Name)) { throw new ArgumentException(SR.Cryptography_HashAlgorithmNameNullOrEmpty, nameof(hashAlgorithm)); } SubjectName = subjectName; _key = key; _generator = X509SignatureGenerator.CreateForGost(key); PublicKey = _generator.PublicKey; HashAlgorithm = hashAlgorithm; }
/// <summary> /// Create a certificate using the established subject, key, and optional extensions using /// the provided certificate as the issuer. /// </summary> /// <param name="issuerCertificate"> /// An X509Certificate2 instance representing the issuing Certificate Authority (CA). /// </param> /// <param name="notBefore"> /// The oldest date and time where this certificate is considered valid. /// Typically <see cref="DateTimeOffset.UtcNow"/>, plus or minus a few seconds. /// </param> /// <param name="notAfter"> /// The date and time where 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 (big) integer in big endian byte ordering. /// </param> /// <returns> /// An <see cref="X509Certificate2"/> with the specified values. The returned object will /// not assert <see cref="X509Certificate2.HasPrivateKey" />. /// </returns> /// <exception cref="ArgumentNullException"><paramref name="issuerCertificate"/> is null.</exception> /// <exception cref="ArgumentException"> /// The <see cref="X509Certificate2.HasPrivateKey"/> value for <paramref name="issuerCertificate"/> is false. /// </exception> /// <exception cref="ArgumentException"> /// The type of signing key represented by <paramref name="issuerCertificate"/> could not be determined. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="notAfter"/> represents a date and time before <paramref name="notBefore"/>. /// </exception> /// <exception cref="ArgumentException"><paramref name="serialNumber"/> is null or has length 0.</exception> /// <exception cref="ArgumentException"> /// <paramref name="issuerCertificate"/> has a different key algorithm than the requested certificate. /// </exception> /// <exception cref="InvalidOperationException"> /// <paramref name="issuerCertificate"/> is an RSA certificate and this object was created via a constructor /// which does not accept a <see cref="RSASignaturePadding"/> value. /// </exception> public X509Certificate2 Create( X509Certificate2 issuerCertificate, DateTimeOffset notBefore, DateTimeOffset notAfter, byte[] serialNumber) { if (issuerCertificate == null) { throw new ArgumentNullException(nameof(issuerCertificate)); } if (!issuerCertificate.HasPrivateKey) { throw new ArgumentException(SR.Cryptography_CertReq_IssuerRequiresPrivateKey, nameof(issuerCertificate)); } if (notAfter < notBefore) { throw new ArgumentException(SR.Cryptography_CertReq_DatesReversed); } if (serialNumber == null || serialNumber.Length < 1) { throw new ArgumentException(SR.Arg_EmptyOrNullArray, nameof(serialNumber)); } if (issuerCertificate.PublicKey.Oid.Value != PublicKey.Oid.Value) { throw new ArgumentException( SR.Format( SR.Cryptography_CertReq_AlgorithmMustMatch, issuerCertificate.PublicKey.Oid.Value, PublicKey.Oid.Value), nameof(issuerCertificate)); } DateTime notBeforeLocal = notBefore.LocalDateTime; if (notBeforeLocal < issuerCertificate.NotBefore) { throw new ArgumentException( SR.Format( SR.Cryptography_CertReq_NotBeforeNotNested, notBeforeLocal, issuerCertificate.NotBefore), nameof(notBefore)); } DateTime notAfterLocal = notAfter.LocalDateTime; // Round down to the second, since that's the cert accuracy. // This makes one method which uses the same DateTimeOffset for chained notAfters // not need to do the rounding locally. long notAfterLocalTicks = notAfterLocal.Ticks; long fractionalSeconds = notAfterLocalTicks % TimeSpan.TicksPerSecond; notAfterLocalTicks -= fractionalSeconds; notAfterLocal = new DateTime(notAfterLocalTicks, notAfterLocal.Kind); if (notAfterLocal > issuerCertificate.NotAfter) { throw new ArgumentException( SR.Format( SR.Cryptography_CertReq_NotAfterNotNested, notAfterLocal, issuerCertificate.NotAfter), nameof(notAfter)); } // Check the Basic Constraints and Key Usage extensions to help identify inappropriate certificates. // Note that this is not a security check. The system library backing X509Chain will use these same criteria // to determine if a chain is valid; and a user can easily call the X509SignatureGenerator overload to // bypass this validation. We're simply helping them at signing time understand that they've // chosen the wrong cert. var basicConstraints = (X509BasicConstraintsExtension)issuerCertificate.Extensions[Oids.BasicConstraints2]; var keyUsage = (X509KeyUsageExtension)issuerCertificate.Extensions[Oids.KeyUsage]; if (basicConstraints == null) { throw new ArgumentException(SR.Cryptography_CertReq_BasicConstraintsRequired, nameof(issuerCertificate)); } if (!basicConstraints.CertificateAuthority) { throw new ArgumentException(SR.Cryptography_CertReq_IssuerBasicConstraintsInvalid, nameof(issuerCertificate)); } if (keyUsage != null && (keyUsage.KeyUsages & X509KeyUsageFlags.KeyCertSign) == 0) { throw new ArgumentException(SR.Cryptography_CertReq_IssuerKeyUsageInvalid, nameof(issuerCertificate)); } AsymmetricAlgorithm key = null; string keyAlgorithm = issuerCertificate.GetKeyAlgorithm(); X509SignatureGenerator generator; try { switch (keyAlgorithm) { case Oids.Rsa: if (_rsaPadding == null) { throw new InvalidOperationException(SR.Cryptography_CertReq_RSAPaddingRequired); } RSA rsa = issuerCertificate.GetRSAPrivateKey(); key = rsa; generator = X509SignatureGenerator.CreateForRSA(rsa, _rsaPadding); break; case Oids.EcPublicKey: ECDsa ecdsa = issuerCertificate.GetECDsaPrivateKey(); key = ecdsa; generator = X509SignatureGenerator.CreateForECDsa(ecdsa); break; //begin: gost case Oids.Gost3410EL: Gost3410 gost3410 = issuerCertificate.GetGost3410PrivateKey(); key = gost3410; generator = X509SignatureGenerator.CreateForGost(gost3410); break; case Oids.Gost3410_2012_256: Gost3410_2012_256 gost3410_2012_256 = issuerCertificate.GetGost3410_2012_256PrivateKey(); key = gost3410_2012_256; generator = X509SignatureGenerator.CreateForGost(gost3410_2012_256); break; case Oids.Gost3410_2012_512: Gost3410_2012_512 gost3410_2012_512 = issuerCertificate.GetGost3410_2012_512PrivateKey(); key = gost3410_2012_512; generator = X509SignatureGenerator.CreateForGost(gost3410_2012_512); break; //end: gost default: throw new ArgumentException( SR.Format(SR.Cryptography_UnknownKeyAlgorithm, keyAlgorithm), nameof(issuerCertificate)); } return(Create(issuerCertificate.SubjectName, generator, notBefore, notAfter, serialNumber)); } finally { key?.Dispose(); } }