8
\$\begingroup\$

The purpose of this code is to generate the CSR and the Private Key.

The scenario is that we have some clients for whom we run websites and for the SSL component we want to have a page on our site where they can input the fields like domain name, company name etc and then the system will generate the CSR (and return it to them on the screen so they can get it signed by a third party, e.g. geotrust) and the private key will automatically be put into the correct spot on the web server ready for use.

The 'main' part of this is below.

        /// <summary>
        ///     Generates certificate request in PKCS#10 format defined by RFC 2986.
        ///     This will also output the private key at the same time.
        ///     *******************************************
        ///     Notes / Handy references:
        ///     http://www.keylength.com/en/compare/
        ///     http://csrc.nist.gov/publications/nistpubs/800-57/sp800-57_part1_rev3_general.pdf
        /// </summary>
        private void GeneratePkcs10
            (string domainName, string companyName, string division, string city, string state,
             string countryIso2Characters, string email, RootLenght rootLength, out string csr, out string privateKey)
        {
            csr = null;
            privateKey = null;

            try
            {
                var rsaKeyPairGenerator = new RsaKeyPairGenerator();

                // Note: the numbers {3, 5, 17, 257 or 65537} as Fermat primes.
                // NIST doesn't allow a public exponent smaller than 65537, since smaller exponents are a problem if they aren't properly padded.
                // Note: the default in openssl is '65537', i.e. 0x10001.
                var genParam = new RsaKeyGenerationParameters
                    (BigInteger.ValueOf(0x10001), new SecureRandom(), (int) rootLength, 128);

                rsaKeyPairGenerator.Init(genParam);

                AsymmetricCipherKeyPair pair = rsaKeyPairGenerator.GenerateKeyPair();

                IDictionary attrs = new Hashtable();

                attrs.Add(X509Name.CN, domainName);
                attrs.Add(X509Name.O, companyName);
                attrs.Add(X509Name.L, city);
                attrs.Add(X509Name.ST, state);
                attrs.Add(X509Name.C, countryIso2Characters);

                if (division != null)
                {
                    attrs.Add(X509Name.OU, division);
                }

                if (email != null)
                {
                    attrs.Add(X509Name.EmailAddress, email);
                }

                var subject = new X509Name(new ArrayList(attrs.Keys), attrs);

                var pkcs10CertificationRequest = new Pkcs10CertificationRequest
                    (PkcsObjectIdentifiers.Sha256WithRsaEncryption.Id, subject, pair.Public, null, pair.Private);

                csr = Convert.ToBase64String(pkcs10CertificationRequest.GetEncoded());

                var pkInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(pair.Private);
                privateKey = Convert.ToBase64String(pkInfo.GetDerEncoded());
            }
            catch (Exception ex)
            {
                // Note: handles errors on the page. Redirect to error page.
                ErrorHandler(ex);
            }
        }

        private enum RootLenght
        {

            RootLength2048 = 2048,

            RootLength3072 = 3072,

            RootLength4096 = 4096,

        }

Because the documentation for BouncyCastle for .net is limited, I am unsure if the above is 'correct'. It does compile, however:

  1. Is it safe to always use BigInteger.ValueOf(0x10001)? This seems to be the default for OpenSSL so I presume this is correct. Or should this be the actual number 65537?

  2. NIST does not have a 'certainty' recommendation for 4096 keys. Is it safe to use 128 always for the three lengths in the enum? I saw this answer but it is a bit dated. Should I be switching the value depending on the length of the key?

  3. Am I missing anything?

\$\endgroup\$
1
  • \$\begingroup\$ Have you take the csr generated by this code and had it signed and then installed it as a cert? It looks solid - but slightly different from what I've done. \$\endgroup\$ Jun 3, 2016 at 15:25

1 Answer 1

5
\$\begingroup\$

ArrayList and Hashtable were superceded by List<T> and Dictionary<TKey, TValue>, respectively, in .NET 2.0.

The first parameter of the X509Name constructor, ordering, specifies the order in which the OID/string pairs will be processed. However we're using new ArrayList(attrs.Keys) for this parameter, and the documentation for Hashtable states "The order of the keys ... is unspecified", so we can't rely upon the keys being returned in the same order that we inserted them.

If the order isn't important in your case, replace ArrayList/Hashtable with List<T>/Dictionary<TKey, TValue>:

var attributes = new Dictionary<DerObjectIdentifier, string>
{
    { X509Name.CN, domainName },
    { X509Name.O, companyName },
    { X509Name.L, city },
    { X509Name.ST, state },
    { X509Name.C, countryIso2Characters }
};

if (division != null)
{
    attributes.Add(X509Name.OU, division);
}

if (email != null)
{
    attributes.Add(X509Name.EmailAddress, email);
}

var subject = new X509Name(attributes.Keys.ToList(), attributes);

If the order is important to you, then we need another solution since "The order of the keys in the Dictionary<TKey, TValue>.KeyCollection is unspecified".

The obvious solution is to use a List<KeyValuePair<DerObjectIdentifier, string>>, but this gets very verbose:

var attributes = new List<KeyValuePair<DerObjectIdentifier, string>>
{
    new KeyValuePair<DerObjectIdentifier, string>(X509Name.CN, domainName),
    ...
};

One trick I read about is to define a helper class like this

private class KeyValuePairList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>
{
    public void Add(TKey key, TValue value)
    {
        this.Add(new KeyValuePair<TKey, TValue>(key, value));
    }

    public IEnumerable<TKey> Keys
    {
        get { return this.Select(item => item.Key); }
    }

    public IEnumerable<TValue> Values
    {
        get { return this.Select(item => item.Value); }
    }
}

The Add method lets us use the same initializer syntax we used for the Dictionary.

And then you can use the X509Name(IList, IList) constructor.

var attributes = new KeyValuePairList<DerObjectIdentifier, string>
{
    { X509Name.CN, domainName },
    { X509Name.O, companyName },
    { X509Name.L, city },
    { X509Name.ST, state },
    { X509Name.C, countryIso2Characters }
};

if (division != null)
{
    attributes.Add(X509Name.OU, division);
}

if (email != null)
{
    attributes.Add(X509Name.EmailAddress, email);
}

var subject = new X509Name(attributes.Keys.ToList(), attributes.Values.ToList());
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.