Starting from .NET 4.7.2 it is possible to generate RSA and EC certificates using .NET CertificateRequest. However I can't find anything that would allow me to generate DSA certs. Here is how I'd do it for RSA and EC:
private static X509Certificate2 GenerateRsaCertificate()
{
var hashAlgorithm = HashAlgorithmName.SHA256;
var rsaKey = RSA.Create(2048);
var subject = new X500DistinguishedName("CN=mycert");
var request = new CertificateRequest(subject, rsaKey, hashAlgorithm, RSASignaturePadding.Pkcs1);
var certificate = request.CreateSelfSigned(DateTime.Now - TimeSpan.FromDays(5), DateTime.Now + TimeSpan.FromDays(365));
return certificate;
}
private static X509Certificate2 GenerateEcDsaCertificate()
{
var hashAlgorithm = HashAlgorithmName.SHA256;
var curve = ECCurve.NamedCurves.nistP256;
var ecDsaKey = ECDsa.Create(curve);
var subject = new X500DistinguishedName("CN=mycert");
var request = new CertificateRequest(subject, ecDsaKey, hashAlgorithm);
var certificate = request.CreateSelfSigned(DateTime.Now - TimeSpan.FromDays(5), DateTime.Now + TimeSpan.FromDays(365));
return certificate;
}
Previously I used Bouncy Castle to generate all three types of certs, but with migration to .NET I'm able to use only RSA and ECDsa in CertificateRequest calls. Are there any reasons why DSA is not included? I can still generate a key with DSA.Create(keySize)
however. Also .NET Framework includes other classes that work with DSA: DSA, DSACng, DSACryptoServiceProvider, DSACertificateExtensions
, but I don't see anything for certificate generation. Are there any problems with the algorithm itself (maybe I shouldn't use it at all)? Or am I missing something in the API?
Are there any problems with the algorithm itself (maybe I shouldn't use it at all)?
Non-EC DSA is dying.
I'll speculate that the thing that really did it in is the original specification (FIPS 186-1) limited the keys to 1024-bit and the algorithm to SHA-1. In 2009 the algorithm got updated in FIPS 186-3 to support slightly larger keys and the SHA-2 hashes. FIPS 186-1 (and FIPS 186-2) DSA signatures only required data and a private key (verification only required data, signature, and a public key), FIPS 186-3 signatures also require the hash algorithm as an input... so the API isn't exactly compatible.
Windows CAPI (the older of the two Windows cryptography platforms) ignored the FIPS 186-3 update, as did Apple's Security.framework. Windows CNG and OpenSSL both support "new DSA". Apple can't process certificates signed with "new DSA" (and maybe not even with "DSA classic", I forget), and Windows doesn't support "new DSA" in cert chains, only "DSA classic".
So DSA certificates are generally limited to FIPS 186-1/186-2 restrictions, which means SHA-1 (not on anyone's good side these days) and 1024-bit keys (which are too small by today's reckoning). If you know you're being validated by OpenSSL you can use better DSA keys.
DSA is also generally much slower at signature verification than RSA.
At the 80 bits of security level, using OpenSSL's speed tool on a random VM of mine (output slightly modified for presentation purposes, sorted by verify/s descending):
sign verify sign/s verify/s
rsa 1024 bits 0.000301s 0.000018s 3326.3 56419.7
dsa 1024 bits 0.000309s 0.000236s 3236.2 4240.5
ecdsa 160 bits (secp160r1) 0.0005s 0.0004s 1984.6 2385.7
112 bits of security
sign verify sign/s verify/s
rsa 2048 bits 0.002030s 0.000062s 492.6 16062.4
ecdsa 224 bits (nistp224) 0.0001s 0.0002s 9020.6 4252.2
dsa 2048 bits 0.000885s 0.000802s 1129.4 1247.3
128 bits of security
sign verify sign/s verify/s
rsa 3072 bits 0.006935s 0.000135s 144.2 7401.6
ecdsa 256 bits (nistp256) 0.0001s 0.0002s 16901.5 5344.7
ecdsa 256 bits (brainpoolP256t1) 0.0010s 0.0008s 980.1 1262.5
ecdsa 256 bits (brainpoolP256r1) 0.0010s 0.0008s 1012.9 1209.5
dsa 3072 bits (not in test suite)
192 bits of security
sign verify sign/s verify/s
rsa 7680 bits 0.122805s 0.000820s 8.1 1220.2
ecdsa 384 bits (nistp384) 0.0024s 0.0018s 416.1 571.2
ecdsa 384 bits (brainpoolP384t1) 0.0024s 0.0018s 410.0 545.1
ecdsa 384 bits (brainpoolP384r1) 0.0025s 0.0019s 407.4 540.1
dsa 7680 bits (beyond FIPS 186-3 DSA maximum of 3072 bits)
256 bits of security
sign verify sign/s verify/s
ecdsa 521 bits (nistp521) 0.0006s 0.0012s 1563.1 841.3
ecdsa 512 bits (brainpoolP512t1) 0.0038s 0.0027s 265.2 369.1
ecdsa 512 bits (brainpoolP512r1) 0.0038s 0.0028s 262.4 360.5
rsa 15360 bits 0.783846s 0.003190s 1.3 313.5
dsa 15360 bits (beyond FIPS 186-3 DSA maximum of 3072 bits)
I'm able to use only RSA and ECDsa in CertificateRequest calls. Are there any reasons why DSA is not included?
From the thread with the original feature proposal:
Based on new data from Windows (and their lack of support for FIPS 186-3 DSA certificates) I'm going to pull the DSA typed constructor and leave DSA as a "power user" scenario (custom X509SignatureGenerator class, etc)
So, it was removed mainly because DSA is dying.
Or am I missing something in the API?
The API allows for custom signature generators to be provided. In the tests for CertificateRequest it proves this out with a DSAX509SignatureGenerator
X509SignatureGenerator dsaGen = new DSAX509SignatureGenerator(dsaCsp);
// Use SHA-1 because that's all DSACryptoServiceProvider understands.
HashAlgorithmName hashAlgorithm = HashAlgorithmName.SHA1;
CertificateRequest request = new CertificateRequest(
new X500DistinguishedName($"CN={KeyName}-{provType}"),
dsaGen.PublicKey,
hashAlgorithm);
DateTimeOffset now = DateTimeOffset.UtcNow;
using (X509Certificate2 cert = request.Create(request.SubjectName, dsaGen, now, now.AddDays(1), new byte[1]))
using (X509Certificate2 certWithPrivateKey = cert.CopyWithPrivateKey(dsaCsp))
using (DSA dsa = certWithPrivateKey.GetDSAPrivateKey())
{
byte[] signature = dsa.SignData(Array.Empty<byte>(), hashAlgorithm);
Assert.True(dsaCsp.VerifyData(Array.Empty<byte>(), signature, hashAlgorithm));
}