Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API Proposal]: Make System.Security.Cryptography.Oids constants public #87270

Open
ThreeSevenths opened this issue Jun 8, 2023 · 11 comments
Open
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Security
Milestone

Comments

@ThreeSevenths
Copy link

Background and motivation

As a C# developer, I would like to have the Oids constants exposed to me so that I do not need to add my own constants for the same thing, thus reducing the size of my final output (every byte counts 😎) and reducing the chance of error.

The runtime already includes many constants as an internal class, dotnet/runtime - Oids.cs which could easily be made public instead of internal.

API Proposal

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Security.Cryptography
{
    public static partial class Oids
    {
        // Symmetric encryption algorithms
        public const string Rc2Cbc = "1.2.840.113549.3.2";
        public const string Rc4 = "1.2.840.113549.3.4";
        public const string TripleDesCbc = "1.2.840.113549.3.7";
        public const string DesCbc = "1.3.14.3.2.7";
        public const string Aes128Cbc = "2.16.840.1.101.3.4.1.2";
        public const string Aes192Cbc = "2.16.840.1.101.3.4.1.22";
        public const string Aes256Cbc = "2.16.840.1.101.3.4.1.42";

        // Asymmetric encryption algorithms
        public const string Dsa = "1.2.840.10040.4.1";
        public const string Rsa = "1.2.840.113549.1.1.1";
        public const string RsaOaep = "1.2.840.113549.1.1.7";
        public const string RsaPss = "1.2.840.113549.1.1.10";
        public const string RsaPkcs1Md5 = "1.2.840.113549.1.1.4";
        public const string RsaPkcs1Sha1 = "1.2.840.113549.1.1.5";
        public const string RsaPkcs1Sha256 = "1.2.840.113549.1.1.11";
        public const string RsaPkcs1Sha384 = "1.2.840.113549.1.1.12";
        public const string RsaPkcs1Sha512 = "1.2.840.113549.1.1.13";
        public const string RsaPkcs1Sha3_256 = "2.16.840.1.101.3.4.3.14";
        public const string RsaPkcs1Sha3_384 = "2.16.840.1.101.3.4.3.15";
        public const string RsaPkcs1Sha3_512 = "2.16.840.1.101.3.4.3.16";
        public const string Esdh = "1.2.840.113549.1.9.16.3.5";
        public const string EcDiffieHellman = "1.3.132.1.12";
        public const string DiffieHellman = "1.2.840.10046.2.1";
        public const string DiffieHellmanPkcs3 = "1.2.840.113549.1.3.1";

        // Cryptographic Attribute Types
        public const string SigningTime = "1.2.840.113549.1.9.5";
        public const string ContentType = "1.2.840.113549.1.9.3";
        public const string DocumentDescription = "1.3.6.1.4.1.311.88.2.2";
        public const string MessageDigest = "1.2.840.113549.1.9.4";
        public const string CounterSigner = "1.2.840.113549.1.9.6";
        public const string SigningCertificate = "1.2.840.113549.1.9.16.2.12";
        public const string SigningCertificateV2 = "1.2.840.113549.1.9.16.2.47";
        public const string DocumentName = "1.3.6.1.4.1.311.88.2.1";
        public const string LocalKeyId = "1.2.840.113549.1.9.21";
        public const string EnrollCertTypeExtension = "1.3.6.1.4.1.311.20.2";
        public const string UserPrincipalName = "1.3.6.1.4.1.311.20.2.3";
        public const string CertificateTemplate = "1.3.6.1.4.1.311.21.7";
        public const string ApplicationCertPolicies = "1.3.6.1.4.1.311.21.10";
        public const string AuthorityInformationAccess = "1.3.6.1.5.5.7.1.1";
        public const string OcspEndpoint = "1.3.6.1.5.5.7.48.1";
        public const string CertificateAuthorityIssuers = "1.3.6.1.5.5.7.48.2";
        public const string Pkcs9ExtensionRequest = "1.2.840.113549.1.9.14";

        // Key wrap algorithms
        public const string CmsRc2Wrap = "1.2.840.113549.1.9.16.3.7";
        public const string Cms3DesWrap = "1.2.840.113549.1.9.16.3.6";

        // PKCS7 Content Types.
        public const string Pkcs7Data = "1.2.840.113549.1.7.1";
        public const string Pkcs7Signed = "1.2.840.113549.1.7.2";
        public const string Pkcs7Enveloped = "1.2.840.113549.1.7.3";
        public const string Pkcs7SignedEnveloped = "1.2.840.113549.1.7.4";
        public const string Pkcs7Hashed = "1.2.840.113549.1.7.5";
        public const string Pkcs7Encrypted = "1.2.840.113549.1.7.6";

        public const string Md5 = "1.2.840.113549.2.5";
        public const string Sha1 = "1.3.14.3.2.26";
        public const string Sha256 = "2.16.840.1.101.3.4.2.1";
        public const string Sha384 = "2.16.840.1.101.3.4.2.2";
        public const string Sha512 = "2.16.840.1.101.3.4.2.3";
        public const string Sha3_256 = "2.16.840.1.101.3.4.2.8";
        public const string Sha3_384 = "2.16.840.1.101.3.4.2.9";
        public const string Sha3_512 = "2.16.840.1.101.3.4.2.10";

        // DSA CMS uses the combined signature+digest OID
        public const string DsaWithSha1 = "1.2.840.10040.4.3";
        public const string DsaWithSha256 = "2.16.840.1.101.3.4.3.2";
        public const string DsaWithSha384 = "2.16.840.1.101.3.4.3.3";
        public const string DsaWithSha512 = "2.16.840.1.101.3.4.3.4";

        // ECDSA CMS uses the combined signature+digest OID
        // https://tools.ietf.org/html/rfc5753#section-2.1.1
        public const string EcPrimeField = "1.2.840.10045.1.1";
        public const string EcChar2Field = "1.2.840.10045.1.2";
        public const string EcChar2TrinomialBasis = "1.2.840.10045.1.2.3.2";
        public const string EcChar2PentanomialBasis = "1.2.840.10045.1.2.3.3";
        public const string EcPublicKey = "1.2.840.10045.2.1";
        public const string ECDsaWithSha1 = "1.2.840.10045.4.1";
        public const string ECDsaWithSha256 = "1.2.840.10045.4.3.2";
        public const string ECDsaWithSha384 = "1.2.840.10045.4.3.3";
        public const string ECDsaWithSha512 = "1.2.840.10045.4.3.4";

        public const string ECDsaWithSha3_256 = "2.16.840.1.101.3.4.3.10";
        public const string ECDsaWithSha3_384 = "2.16.840.1.101.3.4.3.11";
        public const string ECDsaWithSha3_512 = "2.16.840.1.101.3.4.3.12";

        public const string Mgf1 = "1.2.840.113549.1.1.8";
        public const string PSpecified = "1.2.840.113549.1.1.9";

        // PKCS#7
        public const string NoSignature = "1.3.6.1.5.5.7.6.2";

        // X500 Names
        public const string CommonName = "2.5.4.3";
        public const string CountryOrRegionName = "2.5.4.6";
        public const string LocalityName = "2.5.4.7";
        public const string StateOrProvinceName = "2.5.4.8";
        public const string Organization = "2.5.4.10";
        public const string OrganizationalUnit = "2.5.4.11";
        public const string EmailAddress = "1.2.840.113549.1.9.1";

        // Cert Extensions
        public const string BasicConstraints = "2.5.29.10";
        public const string SubjectKeyIdentifier = "2.5.29.14";
        public const string KeyUsage = "2.5.29.15";
        public const string SubjectAltName = "2.5.29.17";
        public const string IssuerAltName = "2.5.29.18";
        public const string BasicConstraints2 = "2.5.29.19";
        public const string CrlNumber = "2.5.29.20";
        public const string CrlReasons = "2.5.29.21";
        public const string CrlDistributionPoints = "2.5.29.31";
        public const string CertPolicies = "2.5.29.32";
        public const string AnyCertPolicy = "2.5.29.32.0";
        public const string CertPolicyMappings = "2.5.29.33";
        public const string AuthorityKeyIdentifier = "2.5.29.35";
        public const string CertPolicyConstraints = "2.5.29.36";
        public const string EnhancedKeyUsage = "2.5.29.37";
        public const string InhibitAnyPolicyExtension = "2.5.29.54";

        // RFC3161 Timestamping
        public const string TstInfo = "1.2.840.113549.1.9.16.1.4";
        public const string TimeStampingPurpose = "1.3.6.1.5.5.7.3.8";

        // PKCS#12
        private const string Pkcs12Prefix = "1.2.840.113549.1.12.";
        private const string Pkcs12PbePrefix = Pkcs12Prefix + "1.";
        public const string Pkcs12PbeWithShaAnd3Key3Des = Pkcs12PbePrefix + "3";
        public const string Pkcs12PbeWithShaAnd2Key3Des = Pkcs12PbePrefix + "4";
        public const string Pkcs12PbeWithShaAnd128BitRC2 = Pkcs12PbePrefix + "5";
        public const string Pkcs12PbeWithShaAnd40BitRC2 = Pkcs12PbePrefix + "6";
        private const string Pkcs12BagTypesPrefix = Pkcs12Prefix + "10.1.";
        public const string Pkcs12KeyBag = Pkcs12BagTypesPrefix + "1";
        public const string Pkcs12ShroudedKeyBag = Pkcs12BagTypesPrefix + "2";
        public const string Pkcs12CertBag = Pkcs12BagTypesPrefix + "3";
        public const string Pkcs12CrlBag = Pkcs12BagTypesPrefix + "4";
        public const string Pkcs12SecretBag = Pkcs12BagTypesPrefix + "5";
        public const string Pkcs12SafeContentsBag = Pkcs12BagTypesPrefix + "6";
        public const string Pkcs12X509CertBagType = "1.2.840.113549.1.9.22.1";
        public const string Pkcs12SdsiCertBagType = "1.2.840.113549.1.9.22.2";

        // PKCS#5
        private const string Pkcs5Prefix = "1.2.840.113549.1.5.";
        public const string PbeWithMD5AndDESCBC = Pkcs5Prefix + "3";
        public const string PbeWithMD5AndRC2CBC = Pkcs5Prefix + "6";
        public const string PbeWithSha1AndDESCBC = Pkcs5Prefix + "10";
        public const string PbeWithSha1AndRC2CBC = Pkcs5Prefix + "11";
        public const string Pbkdf2 = Pkcs5Prefix + "12";
        public const string PasswordBasedEncryptionScheme2 = Pkcs5Prefix + "13";

        private const string RsaDsiDigestAlgorithmPrefix = "1.2.840.113549.2.";
        public const string HmacWithSha1 = RsaDsiDigestAlgorithmPrefix + "7";
        public const string HmacWithSha256 = RsaDsiDigestAlgorithmPrefix + "9";
        public const string HmacWithSha384 = RsaDsiDigestAlgorithmPrefix + "10";
        public const string HmacWithSha512 = RsaDsiDigestAlgorithmPrefix + "11";

        // Elliptic Curve curve identifiers
        public const string secp256r1 = "1.2.840.10045.3.1.7";
        public const string secp384r1 = "1.3.132.0.34";
        public const string secp521r1 = "1.3.132.0.35";

        // LDAP
        public const string DomainComponent = "0.9.2342.19200300.100.1.25";
    }
}

API Usage

// Fancy the value
var c = new MyFancyCollection<int>();
c.Fancy(42);

// Getting the values out
foreach (var v in c)
    Console.WriteLine(v);

Alternative Designs

An alternative design could include additional documentation, a big huge warning about their usage, or a tree hierarchy that maps the oids in oid hierarchy space.

Risks

There are no risks of any backward compatibility issues. There is a slight business risk that some developers that ought not to do their own certificate processing will be empowered to do so with these constants available to them.

@ThreeSevenths ThreeSevenths added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Jun 8, 2023
@ghost ghost added the untriaged New issue has not been triaged by the area owner label Jun 8, 2023
@ghost
Copy link

ghost commented Jun 8, 2023

Tagging subscribers to this area: @dotnet/area-system-security, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

Issue Details

Background and motivation

As a C# developer, I would like to have the Oids constants exposed to me so that I do not need to add my own constants for the same thing, thus reducing the size of my final output (every byte counts 😎) and reducing the chance of error.

The runtime already includes many constants as an internal class, dotnet/runtime - Oids.cs which could easily be made public instead of internal.

API Proposal

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Security.Cryptography
{
    public static partial class Oids
    {
        // Symmetric encryption algorithms
        public const string Rc2Cbc = "1.2.840.113549.3.2";
        public const string Rc4 = "1.2.840.113549.3.4";
        public const string TripleDesCbc = "1.2.840.113549.3.7";
        public const string DesCbc = "1.3.14.3.2.7";
        public const string Aes128Cbc = "2.16.840.1.101.3.4.1.2";
        public const string Aes192Cbc = "2.16.840.1.101.3.4.1.22";
        public const string Aes256Cbc = "2.16.840.1.101.3.4.1.42";

        // Asymmetric encryption algorithms
        public const string Dsa = "1.2.840.10040.4.1";
        public const string Rsa = "1.2.840.113549.1.1.1";
        public const string RsaOaep = "1.2.840.113549.1.1.7";
        public const string RsaPss = "1.2.840.113549.1.1.10";
        public const string RsaPkcs1Md5 = "1.2.840.113549.1.1.4";
        public const string RsaPkcs1Sha1 = "1.2.840.113549.1.1.5";
        public const string RsaPkcs1Sha256 = "1.2.840.113549.1.1.11";
        public const string RsaPkcs1Sha384 = "1.2.840.113549.1.1.12";
        public const string RsaPkcs1Sha512 = "1.2.840.113549.1.1.13";
        public const string RsaPkcs1Sha3_256 = "2.16.840.1.101.3.4.3.14";
        public const string RsaPkcs1Sha3_384 = "2.16.840.1.101.3.4.3.15";
        public const string RsaPkcs1Sha3_512 = "2.16.840.1.101.3.4.3.16";
        public const string Esdh = "1.2.840.113549.1.9.16.3.5";
        public const string EcDiffieHellman = "1.3.132.1.12";
        public const string DiffieHellman = "1.2.840.10046.2.1";
        public const string DiffieHellmanPkcs3 = "1.2.840.113549.1.3.1";

        // Cryptographic Attribute Types
        public const string SigningTime = "1.2.840.113549.1.9.5";
        public const string ContentType = "1.2.840.113549.1.9.3";
        public const string DocumentDescription = "1.3.6.1.4.1.311.88.2.2";
        public const string MessageDigest = "1.2.840.113549.1.9.4";
        public const string CounterSigner = "1.2.840.113549.1.9.6";
        public const string SigningCertificate = "1.2.840.113549.1.9.16.2.12";
        public const string SigningCertificateV2 = "1.2.840.113549.1.9.16.2.47";
        public const string DocumentName = "1.3.6.1.4.1.311.88.2.1";
        public const string LocalKeyId = "1.2.840.113549.1.9.21";
        public const string EnrollCertTypeExtension = "1.3.6.1.4.1.311.20.2";
        public const string UserPrincipalName = "1.3.6.1.4.1.311.20.2.3";
        public const string CertificateTemplate = "1.3.6.1.4.1.311.21.7";
        public const string ApplicationCertPolicies = "1.3.6.1.4.1.311.21.10";
        public const string AuthorityInformationAccess = "1.3.6.1.5.5.7.1.1";
        public const string OcspEndpoint = "1.3.6.1.5.5.7.48.1";
        public const string CertificateAuthorityIssuers = "1.3.6.1.5.5.7.48.2";
        public const string Pkcs9ExtensionRequest = "1.2.840.113549.1.9.14";

        // Key wrap algorithms
        public const string CmsRc2Wrap = "1.2.840.113549.1.9.16.3.7";
        public const string Cms3DesWrap = "1.2.840.113549.1.9.16.3.6";

        // PKCS7 Content Types.
        public const string Pkcs7Data = "1.2.840.113549.1.7.1";
        public const string Pkcs7Signed = "1.2.840.113549.1.7.2";
        public const string Pkcs7Enveloped = "1.2.840.113549.1.7.3";
        public const string Pkcs7SignedEnveloped = "1.2.840.113549.1.7.4";
        public const string Pkcs7Hashed = "1.2.840.113549.1.7.5";
        public const string Pkcs7Encrypted = "1.2.840.113549.1.7.6";

        public const string Md5 = "1.2.840.113549.2.5";
        public const string Sha1 = "1.3.14.3.2.26";
        public const string Sha256 = "2.16.840.1.101.3.4.2.1";
        public const string Sha384 = "2.16.840.1.101.3.4.2.2";
        public const string Sha512 = "2.16.840.1.101.3.4.2.3";
        public const string Sha3_256 = "2.16.840.1.101.3.4.2.8";
        public const string Sha3_384 = "2.16.840.1.101.3.4.2.9";
        public const string Sha3_512 = "2.16.840.1.101.3.4.2.10";

        // DSA CMS uses the combined signature+digest OID
        public const string DsaWithSha1 = "1.2.840.10040.4.3";
        public const string DsaWithSha256 = "2.16.840.1.101.3.4.3.2";
        public const string DsaWithSha384 = "2.16.840.1.101.3.4.3.3";
        public const string DsaWithSha512 = "2.16.840.1.101.3.4.3.4";

        // ECDSA CMS uses the combined signature+digest OID
        // https://tools.ietf.org/html/rfc5753#section-2.1.1
        public const string EcPrimeField = "1.2.840.10045.1.1";
        public const string EcChar2Field = "1.2.840.10045.1.2";
        public const string EcChar2TrinomialBasis = "1.2.840.10045.1.2.3.2";
        public const string EcChar2PentanomialBasis = "1.2.840.10045.1.2.3.3";
        public const string EcPublicKey = "1.2.840.10045.2.1";
        public const string ECDsaWithSha1 = "1.2.840.10045.4.1";
        public const string ECDsaWithSha256 = "1.2.840.10045.4.3.2";
        public const string ECDsaWithSha384 = "1.2.840.10045.4.3.3";
        public const string ECDsaWithSha512 = "1.2.840.10045.4.3.4";

        public const string ECDsaWithSha3_256 = "2.16.840.1.101.3.4.3.10";
        public const string ECDsaWithSha3_384 = "2.16.840.1.101.3.4.3.11";
        public const string ECDsaWithSha3_512 = "2.16.840.1.101.3.4.3.12";

        public const string Mgf1 = "1.2.840.113549.1.1.8";
        public const string PSpecified = "1.2.840.113549.1.1.9";

        // PKCS#7
        public const string NoSignature = "1.3.6.1.5.5.7.6.2";

        // X500 Names
        public const string CommonName = "2.5.4.3";
        public const string CountryOrRegionName = "2.5.4.6";
        public const string LocalityName = "2.5.4.7";
        public const string StateOrProvinceName = "2.5.4.8";
        public const string Organization = "2.5.4.10";
        public const string OrganizationalUnit = "2.5.4.11";
        public const string EmailAddress = "1.2.840.113549.1.9.1";

        // Cert Extensions
        public const string BasicConstraints = "2.5.29.10";
        public const string SubjectKeyIdentifier = "2.5.29.14";
        public const string KeyUsage = "2.5.29.15";
        public const string SubjectAltName = "2.5.29.17";
        public const string IssuerAltName = "2.5.29.18";
        public const string BasicConstraints2 = "2.5.29.19";
        public const string CrlNumber = "2.5.29.20";
        public const string CrlReasons = "2.5.29.21";
        public const string CrlDistributionPoints = "2.5.29.31";
        public const string CertPolicies = "2.5.29.32";
        public const string AnyCertPolicy = "2.5.29.32.0";
        public const string CertPolicyMappings = "2.5.29.33";
        public const string AuthorityKeyIdentifier = "2.5.29.35";
        public const string CertPolicyConstraints = "2.5.29.36";
        public const string EnhancedKeyUsage = "2.5.29.37";
        public const string InhibitAnyPolicyExtension = "2.5.29.54";

        // RFC3161 Timestamping
        public const string TstInfo = "1.2.840.113549.1.9.16.1.4";
        public const string TimeStampingPurpose = "1.3.6.1.5.5.7.3.8";

        // PKCS#12
        private const string Pkcs12Prefix = "1.2.840.113549.1.12.";
        private const string Pkcs12PbePrefix = Pkcs12Prefix + "1.";
        public const string Pkcs12PbeWithShaAnd3Key3Des = Pkcs12PbePrefix + "3";
        public const string Pkcs12PbeWithShaAnd2Key3Des = Pkcs12PbePrefix + "4";
        public const string Pkcs12PbeWithShaAnd128BitRC2 = Pkcs12PbePrefix + "5";
        public const string Pkcs12PbeWithShaAnd40BitRC2 = Pkcs12PbePrefix + "6";
        private const string Pkcs12BagTypesPrefix = Pkcs12Prefix + "10.1.";
        public const string Pkcs12KeyBag = Pkcs12BagTypesPrefix + "1";
        public const string Pkcs12ShroudedKeyBag = Pkcs12BagTypesPrefix + "2";
        public const string Pkcs12CertBag = Pkcs12BagTypesPrefix + "3";
        public const string Pkcs12CrlBag = Pkcs12BagTypesPrefix + "4";
        public const string Pkcs12SecretBag = Pkcs12BagTypesPrefix + "5";
        public const string Pkcs12SafeContentsBag = Pkcs12BagTypesPrefix + "6";
        public const string Pkcs12X509CertBagType = "1.2.840.113549.1.9.22.1";
        public const string Pkcs12SdsiCertBagType = "1.2.840.113549.1.9.22.2";

        // PKCS#5
        private const string Pkcs5Prefix = "1.2.840.113549.1.5.";
        public const string PbeWithMD5AndDESCBC = Pkcs5Prefix + "3";
        public const string PbeWithMD5AndRC2CBC = Pkcs5Prefix + "6";
        public const string PbeWithSha1AndDESCBC = Pkcs5Prefix + "10";
        public const string PbeWithSha1AndRC2CBC = Pkcs5Prefix + "11";
        public const string Pbkdf2 = Pkcs5Prefix + "12";
        public const string PasswordBasedEncryptionScheme2 = Pkcs5Prefix + "13";

        private const string RsaDsiDigestAlgorithmPrefix = "1.2.840.113549.2.";
        public const string HmacWithSha1 = RsaDsiDigestAlgorithmPrefix + "7";
        public const string HmacWithSha256 = RsaDsiDigestAlgorithmPrefix + "9";
        public const string HmacWithSha384 = RsaDsiDigestAlgorithmPrefix + "10";
        public const string HmacWithSha512 = RsaDsiDigestAlgorithmPrefix + "11";

        // Elliptic Curve curve identifiers
        public const string secp256r1 = "1.2.840.10045.3.1.7";
        public const string secp384r1 = "1.3.132.0.34";
        public const string secp521r1 = "1.3.132.0.35";

        // LDAP
        public const string DomainComponent = "0.9.2342.19200300.100.1.25";
    }
}

API Usage

// Fancy the value
var c = new MyFancyCollection<int>();
c.Fancy(42);

// Getting the values out
foreach (var v in c)
    Console.WriteLine(v);

Alternative Designs

An alternative design could include additional documentation, a big huge warning about their usage, or a tree hierarchy that maps the oids in oid hierarchy space.

Risks

There are no risks of any backward compatibility issues. There is a slight business risk that some developers that ought not to do their own certificate processing will be empowered to do so with these constants available to them.

Author: ThreeSevenths
Assignees: -
Labels:

api-suggestion, area-System.Security

Milestone: -

@vcsjones
Copy link
Member

vcsjones commented Jun 8, 2023

I've been thinking about this for a while and glad someone else proposed it 😄.

My immediate thoughts are:

  1. Do we want to expose strings, or Oid instances? I like the strings because they are cheaper than an Oid instance.

  2. We need to figure out better names for these and how to organize them. We could do nested static classes:

    public static class Oids {
        public static class HashAlgs {
            public const string SHA3_256 = "2.16.840.1.101.3.4.2.8";
        }
    }

    but that might be arbitrary. Going full Registry like IsoUtu.Country.Us.Orgaization.Gov.Csor.HashAlgs.SHA3_256 would be silly, I think.

@ThreeSevenths
Copy link
Author

  1. Do we want to expose strings, or Oid instances? I like the strings because they are cheaper than an Oid instance.

I'd agree strings are the way to go. Oid isn't heavy, but it's definitely not as light as a string. Calling .Value or adding an equality comparer could work as well. It's also less work since they are strings today. In my usage I'm doing oidinstance.Value anyway since I don't want to allocate a readonly Oid for a simple comparison.

  1. We need to figure out better names for these and how to organize them. We could do nested static classes:

I don't mind a logical hierarchy like your example. HashAlgorithms, EnhancedKeyUsages, Extensions, etc. Some permutation of a component of the friendly name might be the most useful. I agree following the 'rich' hierarchy of actual oids would be silly and difficult to navigate.

Ultimately I initially was thinking what's easy to do, and Find internal > Replace public is almost no effort however there is some opportunity here to make it easier to use. I think you still need to know what you're doing to use these constants directly, and there is lots of public API today to make this work in many situations for the vast majority of use cases.

@bartonjs
Copy link
Member

bartonjs commented Jun 8, 2023

Find internal > Replace public is almost no effort however there is some opportunity here to make it easier to use

While that may be a reasonable starting point, each and every name would have to be examined for appropriateness. Esdh doesn't seem like one we'd want to leave collapsed like that. Rsa should arguably be RsaEncryption, and maybe we want to say that in these consts RSA should be capital since it's capital elsewhere in our API surface, etc.

We need to figure out better names for these and how to organize them. We could do nested static classes:

Two problems with this (though it might end up being the best answer anyways): 1) it's definitely arbitrary... id-rsaEncryption is one of the trickier ones to place... it can be a signature algorithm or an encryption algorithm, since it's just a generic "RSA will be used here, some other detail will provide better context". 2) nested types don't work equally well in all languages, so someone should see what the experience would be in C#, F#, VB.NET, and ideally even COBOL.NET. Yeah, we used nested types for well-known EC curves, but we also felt that the number of people using them would be small. While OID-users are still somewhat small, they're a bigger small than EC-curve specifiers/verifiers, so cross-language usability matters more.

Do we want to expose strings, or Oid instances?

Yep. The answer might be both... Foo and FooOid, maybe. The string form is nicer to switch over, and can avoid allocating unnecessarily. The Oid form has the advantage that everyone can share the same reference, reducing long-term allocations. That's why we end up with both in our well-known OIDs tables. Verifiers typically want the string, creators typically want the shared Oid reference.

Another way for doing both would be to put strings as (e.g.) public const string System.Formats.Asn1.OidValues.Pkcs12CertBag = "..."; and Oids as (e.g.) public Oid System.Security.Cryptography.WellKnownOids.Pkcs12CertBag { get; } => s_pkcs12CertBag ??= InitializeOid(System.Formats.Asn1.OidValues.Pkcs12CertBag);

@vvHedgehog
Copy link

It will be great that WellKnown Oids became public.

@vcsjones
Copy link
Member

vcsjones commented Jul 6, 2023

I looked around a bit at other ecosystems to see if, or how, anyone else did this.

Java and Ruby use strings for OIDs. Go uses a typealias as []int. So no good examples there.

Rust does. Rust has a very lean stdlib. All of their cryptography is in a separate crate, including OIDs. The RustCrypto project is as close as you could get to something in-box though.

They generate a list of... every... OID.

https://github.com/RustCrypto/formats/blob/master/const-oid/src/db/gen.rs

"all of them" is probably too much for most people's needs. I was more interested in how they are organized. They are all in separate modules by the RFC or document that that defines them. Loosely, Rust modules could be static classes or namespaces.

In .NET it would be something like:

namespace System.Security.Cryptography.Oids;

public static class Fips202 {
    public static Oid SHA3_256 => MakeOid("2.16.840.1.101.3.4.2.8");
    // etc
}

public static class Rfc4519 {
    public static Oid CommonName => MakeOid("2.5.4.3");
}

There is a certain amount of this that I like, but some things going against it:

  1. Most people probably don't know what RFC or document define some of the OIDs they are looking for. Would you expect CommonName in Rfc4519, or some class called X520?
  2. Rust has some luxury in being that it's a separate crate. Particularly they can add as many as they want and not worry about crate size or app size since Rust apps are statically linked and trimmed by default.

So I don't feel that rust gets us any close to a particular answer, but it does make me think a bit more strongly that any attempt to organize them is going to feel arbitrary.

I don't feel that I am particularly closer to having a better understanding of the direction .NET should take, and there isn't a ton of prior art to look at.

I think a point worth asking though, what OIDs do people feel that they are using frequently enough that would necessitate an accelerator?

@ThreeSevenths
Copy link
Author

I think it's sufficient to start with EKUs, algos that are in common usage in .NET, and x509 related [for my use case 😀]. Ideally, I'd say you should be able to read an ASN1 stream for a bog standard cert from signtool and match the OIDs that would be present. Most of the current OIDs seem to work toward that purpose.

  1. Most people probably don't know what RFC or document define some of the OIDs they are looking for. Would you expect CommonName in Rfc4519, or some class called X520?

I've always thought a key feature for .NET and friends is our excellent tooling, IntelliSense in this case. This greatly aids most developers to be able to discover something IF they can get close to the member name, irrespective of the namespace it is under.

  1. Rust has some luxury in being that it's a separate crate. Particularly they can add as many as they want and not worry about crate size or app size since Rust apps are statically linked and trimmed by default.

I guess because they'll be used in the box, we cannot have a separate NuGet package?

@jozkee jozkee added this to the Future milestone Jul 21, 2023
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Jul 21, 2023
@alexrp
Copy link
Contributor

alexrp commented Aug 12, 2023

If this API proposal is accepted, I'd like to also see 1.3.6.1.5.5.7.3.1 (serverAuth) and 1.3.6.1.5.5.7.3.2 (clientAuth).

@ThreeSevenths
Copy link
Author

Will this be made public in .NET9?

@ergunr
Copy link

ergunr commented May 30, 2024 via email

@vcsjones
Copy link
Member

Will this be made public in .NET9?

This still has yet to be a complete API proposal and go through an API review. In particular, every name needs to be appropriate for public API.

  • What is a better name for Esdh? How do we want to name the jack-of-all-trades RSA OID? Those are just specific examples that the name and organization probably needs work. Every one needs to be well understood.
  • Is there a convention or some kind of grouping? A single class with 100 properties or fields would not look particularly good.
  • Which ones are too niche that we should not make public?
  • Which ones are missing that should be included, like some EKUs?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Security
Projects
None yet
Development

No branches or pull requests

7 participants