Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit

Permalink
Merge pull request #19021 from bartonjs/certreq_rsasigpadding
Browse files Browse the repository at this point in the history
Make RSASignaturePadding a required parameter for RSA certs
  • Loading branch information
stephentoub authored Apr 27, 2017
2 parents c191ee7 + b3988fb commit a4b00ad
Show file tree
Hide file tree
Showing 7 changed files with 414 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ namespace System.Security.Cryptography.X509Certificates
public sealed partial class CertificateRequest
{
public CertificateRequest(System.Security.Cryptography.X509Certificates.X500DistinguishedName subjectName, System.Security.Cryptography.ECDsa key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { }
public CertificateRequest(System.Security.Cryptography.X509Certificates.X500DistinguishedName subjectName, System.Security.Cryptography.RSA key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { }
public CertificateRequest(System.Security.Cryptography.X509Certificates.X500DistinguishedName subjectName, System.Security.Cryptography.RSA key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.RSASignaturePadding padding) { }
public CertificateRequest(System.Security.Cryptography.X509Certificates.X500DistinguishedName subjectName, System.Security.Cryptography.X509Certificates.PublicKey publicKey, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { }
public CertificateRequest(string subjectName, System.Security.Cryptography.ECDsa key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { }
public CertificateRequest(string subjectName, System.Security.Cryptography.RSA key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { }
public CertificateRequest(string subjectName, System.Security.Cryptography.RSA key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.RSASignaturePadding padding) { }
public System.Collections.ObjectModel.Collection<System.Security.Cryptography.X509Certificates.X509Extension> CertificateExtensions { get { throw null; } }
public System.Security.Cryptography.HashAlgorithmName HashAlgorithm { get { throw null; } }
public System.Security.Cryptography.X509Certificates.PublicKey PublicKey { get { throw null; } }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@
<data name="Cryptography_Cert_AlreadyHasPrivateKey" xml:space="preserve">
<value>The certificate already has an associated private key.</value>
</data>
<data name="Cryptography_CertReq_AlgorithmMustMatch" xml:space="preserve">
<value>The issuer certificate public key algorithm ({0}) does not match the value for this certificate request ({1}), use the X509SignatureGenerator overload.</value>
</data>
<data name="Cryptography_CertReq_DatesReversed" xml:space="preserve">
<value>The provided notBefore value is later than the notAfter value.</value>
</data>
Expand All @@ -121,6 +124,9 @@
<data name="Cryptography_CertReq_NoKeyProvided" xml:space="preserve">
<value>This method cannot be used since no signing key was provided via a constructor, use an overload accepting an X509SignatureGenerator instead.</value>
</data>
<data name="Cryptography_CertReq_RSAPaddingRequired" xml:space="preserve">
<value>The issuer certificate uses an RSA key but no RSASignaturePadding was provided to a constructor. If one cannot be provided, use the X509SignatureGenerator overload.</value>
</data>
<data name="Cryptography_CSP_NoPrivateKey" xml:space="preserve">
<value>Object contains only the public half of a key pair. A private key must also be provided.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public sealed class CertificateRequest
{
private readonly AsymmetricAlgorithm _key;
private readonly X509SignatureGenerator _generator;
private readonly RSASignaturePadding _rsaPadding;

/// <summary>
/// The X.500 Distinguished Name to use as the Subject in a created certificate or certificate request.
Expand Down Expand Up @@ -112,20 +113,26 @@ public CertificateRequest(X500DistinguishedName subjectName, ECDsa key, HashAlgo
/// <param name="hashAlgorithm">
/// The hash algorithm to use when signing the certificate or certificate request.
/// </param>
/// <param name="padding">
/// The RSA signature padding to apply if self-signing or being signed with an <see cref="X509Certificate2" />.
/// </param>
/// <seealso cref="X500DistinguishedName(string)"/>
public CertificateRequest(string subjectName, RSA key, HashAlgorithmName hashAlgorithm)
public CertificateRequest(string subjectName, RSA key, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
{
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));
if (padding == null)
throw new ArgumentNullException(nameof(padding));

SubjectName = new X500DistinguishedName(subjectName);

_key = key;
_generator = X509SignatureGenerator.CreateForRSA(key, RSASignaturePadding.Pkcs1);
_generator = X509SignatureGenerator.CreateForRSA(key, padding);
_rsaPadding = padding;
PublicKey = _generator.PublicKey;
HashAlgorithm = hashAlgorithm;
}
Expand All @@ -143,19 +150,29 @@ public CertificateRequest(string subjectName, RSA key, HashAlgorithmName hashAlg
/// <param name="hashAlgorithm">
/// The hash algorithm to use when signing the certificate or certificate request.
/// </param>
public CertificateRequest(X500DistinguishedName subjectName, RSA key, HashAlgorithmName hashAlgorithm)
/// <param name="padding">
/// The RSA signature padding to apply if self-signing or being signed with an <see cref="X509Certificate2" />.
/// </param>
public CertificateRequest(
X500DistinguishedName subjectName,
RSA key,
HashAlgorithmName hashAlgorithm,
RSASignaturePadding padding)
{
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));
if (padding == null)
throw new ArgumentNullException(nameof(padding));

SubjectName = subjectName;

_key = key;
_generator = X509SignatureGenerator.CreateForRSA(key, RSASignaturePadding.Pkcs1);
_generator = X509SignatureGenerator.CreateForRSA(key, padding);
_rsaPadding = padding;
PublicKey = _generator.PublicKey;
HashAlgorithm = hashAlgorithm;
}
Expand Down Expand Up @@ -354,6 +371,13 @@ public X509Certificate2 CreateSelfSigned(DateTimeOffset notBefore, DateTimeOffse
/// <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,
Expand All @@ -365,6 +389,16 @@ public X509Certificate2 Create(
if (!issuerCertificate.HasPrivateKey)
throw new ArgumentException(SR.Cryptography_CertReq_IssuerRequiresPrivateKey, nameof(issuerCertificate));

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

AsymmetricAlgorithm key = null;
string keyAlgorithm = issuerCertificate.GetKeyAlgorithm();
X509SignatureGenerator generator;
Expand All @@ -374,9 +408,14 @@ public X509Certificate2 Create(
switch (keyAlgorithm)
{
case Oids.RsaRsa:
if (_rsaPadding == null)
{
throw new InvalidOperationException(SR.Cryptography_CertReq_RSAPaddingRequired);
}

RSA rsa = issuerCertificate.GetRSAPrivateKey();
key = rsa;
generator = X509SignatureGenerator.CreateForRSA(rsa, RSASignaturePadding.Pkcs1);
generator = X509SignatureGenerator.CreateForRSA(rsa, _rsaPadding);
break;
case Oids.Ecc:
ECDsa ecdsa = issuerCertificate.GetECDsaPrivateKey();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public static void SelfSign_ArgumentValidation()
{
rsa.ImportParameters(TestData.RsaBigExponentParams);

CertificateRequest request = new CertificateRequest("CN=Test", rsa, HashAlgorithmName.SHA256);
var request = new CertificateRequest("CN=Test", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

Assert.Throws<ArgumentException>(
null,
Expand All @@ -54,8 +54,13 @@ public static void SelfSign_ArgumentValidation()
public static void Sign_ArgumentValidation()
{
using (X509Certificate2 testRoot = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword))
using (RSA publicKey = testRoot.GetRSAPublicKey())
{
CertificateRequest request = new CertificateRequest("CN=Test", testRoot.GetRSAPublicKey(), HashAlgorithmName.SHA256);
var request = new CertificateRequest(
"CN=Test",
publicKey,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);

Assert.Throws<ArgumentNullException>(
"generator",
Expand All @@ -74,5 +79,155 @@ public static void Sign_ArgumentValidation()
() => request.Create(testRoot, DateTimeOffset.MinValue, DateTimeOffset.MaxValue, Array.Empty<byte>()));
}
}

[Fact]
public static void CtorValidation_ECDSA_string()
{
string subjectName = null;
ECDsa key = null;
HashAlgorithmName hashAlgorithm = default(HashAlgorithmName);

Assert.Throws<ArgumentNullException>(
"subjectName",
() => new CertificateRequest(subjectName, key, hashAlgorithm));

subjectName = "";

Assert.Throws<ArgumentNullException>(
"key",
() => new CertificateRequest(subjectName, key, hashAlgorithm));

key = ECDsa.Create(EccTestData.Secp384r1Data.KeyParameters);

using (key)
{
Assert.Throws<ArgumentException>(
"hashAlgorithm",
() => new CertificateRequest(subjectName, key, hashAlgorithm));
}
}

[Fact]
public static void CtorValidation_ECDSA_X500DN()
{
X500DistinguishedName subjectName = null;
ECDsa key = null;
HashAlgorithmName hashAlgorithm = default(HashAlgorithmName);

Assert.Throws<ArgumentNullException>(
"subjectName",
() => new CertificateRequest(subjectName, key, hashAlgorithm));

subjectName = new X500DistinguishedName("");

Assert.Throws<ArgumentNullException>(
"key",
() => new CertificateRequest(subjectName, key, hashAlgorithm));

key = ECDsa.Create(EccTestData.Secp384r1Data.KeyParameters);

using (key)
{
Assert.Throws<ArgumentException>(
"hashAlgorithm",
() => new CertificateRequest(subjectName, key, hashAlgorithm));
}
}

[Fact]
public static void CtorValidation_RSA_string()
{
string subjectName = null;
RSA key = null;
HashAlgorithmName hashAlgorithm = default(HashAlgorithmName);
RSASignaturePadding padding = null;

Assert.Throws<ArgumentNullException>(
"subjectName",
() => new CertificateRequest(subjectName, key, hashAlgorithm, padding));

subjectName = "";

Assert.Throws<ArgumentNullException>(
"key",
() => new CertificateRequest(subjectName, key, hashAlgorithm, padding));

key = RSA.Create(TestData.RsaBigExponentParams);

using (key)
{
Assert.Throws<ArgumentException>(
"hashAlgorithm",
() => new CertificateRequest(subjectName, key, hashAlgorithm, padding));

hashAlgorithm = HashAlgorithmName.SHA256;

Assert.Throws<ArgumentNullException>(
"padding",
() => new CertificateRequest(subjectName, key, hashAlgorithm, padding));
}
}

[Fact]
public static void CtorValidation_RSA_X500DN()
{
X500DistinguishedName subjectName = null;
RSA key = null;
HashAlgorithmName hashAlgorithm = default(HashAlgorithmName);
RSASignaturePadding padding = null;

Assert.Throws<ArgumentNullException>(
"subjectName",
() => new CertificateRequest(subjectName, key, hashAlgorithm, padding));

subjectName = new X500DistinguishedName("");

Assert.Throws<ArgumentNullException>(
"key",
() => new CertificateRequest(subjectName, key, hashAlgorithm, padding));

key = RSA.Create(TestData.RsaBigExponentParams);

using (key)
{
Assert.Throws<ArgumentException>(
"hashAlgorithm",
() => new CertificateRequest(subjectName, key, hashAlgorithm, padding));

hashAlgorithm = HashAlgorithmName.SHA256;

Assert.Throws<ArgumentNullException>(
"padding",
() => new CertificateRequest(subjectName, key, hashAlgorithm, padding));
}
}

[Fact]
public static void CtorValidation_PublicKey_X500DN()
{
X500DistinguishedName subjectName = null;
PublicKey publicKey = null;
HashAlgorithmName hashAlgorithm = default(HashAlgorithmName);

Assert.Throws<ArgumentNullException>(
"subjectName",
() => new CertificateRequest(subjectName, publicKey, hashAlgorithm));

subjectName = new X500DistinguishedName("");

Assert.Throws<ArgumentNullException>(
"publicKey",
() => new CertificateRequest(subjectName, publicKey, hashAlgorithm));

using (ECDsa ecdsa = ECDsa.Create(EccTestData.Secp384r1Data.KeyParameters))
{
X509SignatureGenerator generator = X509SignatureGenerator.CreateForECDsa(ecdsa);
publicKey = generator.PublicKey;
}

Assert.Throws<ArgumentException>(
"hashAlgorithm",
() => new CertificateRequest(subjectName, publicKey, hashAlgorithm));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ private static CertificateRequest OpenCertRequest(
RSA rsa = key as RSA;

if (rsa != null)
return new CertificateRequest(dn, rsa, hashAlgorithm);
return new CertificateRequest(dn, rsa, hashAlgorithm, RSASignaturePadding.Pkcs1);

ECDsa ecdsa = key as ECDsa;

Expand All @@ -81,6 +81,22 @@ private static CertificateRequest OpenCertRequest(
$"Had no handler for key of type {key?.GetType().FullName ?? "null"}");
}

private static X509SignatureGenerator OpenGenerator(AsymmetricAlgorithm key)
{
RSA rsa = key as RSA;

if (rsa != null)
return X509SignatureGenerator.CreateForRSA(rsa, RSASignaturePadding.Pkcs1);

ECDsa ecdsa = key as ECDsa;

if (ecdsa != null)
return X509SignatureGenerator.CreateForECDsa(ecdsa);

throw new InvalidOperationException(
$"Had no handler for key of type {key?.GetType().FullName ?? "null"}");
}

private static CertificateRequest CreateChainRequest(
string dn,
AsymmetricAlgorithm key,
Expand Down Expand Up @@ -219,6 +235,9 @@ private static void CreateAndTestChain(
leafRequest.CertificateExtensions.Add(
new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));

X509SignatureGenerator rootGenerator = OpenGenerator(rootPrivKey);
X509SignatureGenerator intermed2Generator = OpenGenerator(intermed2PrivKey);

X509Certificate2 rootCertWithKey = null;
X509Certificate2 intermed1CertWithKey = null;
X509Certificate2 intermed2CertWithKey = null;
Expand Down Expand Up @@ -248,16 +267,20 @@ private static void CreateAndTestChain(
rng.GetBytes(leafSerial, 2, leafSerial.Length - 2);
}

X509Certificate2 intermed1Tmp = intermed1Request.Create(rootCertWithKey, now, intermedEnd, intermed1Serial);
X509Certificate2 intermed2Tmp = intermed2Request.Create(rootCertWithKey, now, intermedEnd, intermed1Serial);
X509Certificate2 intermed1Tmp =
intermed1Request.Create(rootCertWithKey.SubjectName, rootGenerator, now, intermedEnd, intermed1Serial);

X509Certificate2 intermed2Tmp =
intermed2Request.Create(rootCertWithKey.SubjectName, rootGenerator, now, intermedEnd, intermed1Serial);

intermed1CertWithKey = CloneWithPrivateKey(intermed1Tmp, intermed1PrivKey);
intermed2CertWithKey = CloneWithPrivateKey(intermed2Tmp, intermed2PrivKey);

intermed1Tmp.Dispose();
intermed2Tmp.Dispose();

leafCert = leafRequest.Create(intermed2CertWithKey, now, leafEnd, leafSerial);
leafCert = leafRequest.Create(
intermed2CertWithKey.SubjectName, intermed2Generator, now, leafEnd, leafSerial);

using (X509Chain chain = new X509Chain())
{
Expand Down
Loading

0 comments on commit a4b00ad

Please sign in to comment.