Пример #1
0
        public static string Name(this GeneralNameType type)
        {
            switch (type)
            {
            default:
                throw new NotSupportedException();

            case GeneralNameType.OtherName:
                return(GeneralNameOtherName);

            case GeneralNameType.RFC822Name:
                return(RFC822Name);

            case GeneralNameType.DNSName:
                return(DNSName);

            case GeneralNameType.X400Address:
                return(X400Address);

            case GeneralNameType.DirectoryName:
                return(DirectoryName);

            case GeneralNameType.EdiPartyName:
                return(EdiPartyName);

            case GeneralNameType.UniformResourceIdentifier:
                return(UniformResourceIdentifier);

            case GeneralNameType.IPAddress:
                return(IPAddress);

            case GeneralNameType.RegisteredId:
                return(RegisteredId);
            }
        }
Пример #2
0
        private static string FindAltNameMatch(byte[] extensionBytes, GeneralNameType matchType, string otherOid)
        {
            // If Other, have OID, else, no OID.
            Debug.Assert(
                (otherOid == null) == (matchType != GeneralNameType.OtherName),
                $"otherOid has incorrect nullarity for matchType {matchType}");

            Debug.Assert(
                matchType == GeneralNameType.UniformResourceIdentifier ||
                matchType == GeneralNameType.DnsName ||
                matchType == GeneralNameType.Email ||
                matchType == GeneralNameType.OtherName,
                $"matchType ({matchType}) is not currently supported");

            Debug.Assert(
                otherOid == null || otherOid == Oids.UserPrincipalName,
                $"otherOid ({otherOid}) is not supported");

            AsnReader reader         = new AsnReader(extensionBytes, AsnEncodingRules.DER);
            AsnReader sequenceReader = reader.ReadSequence();

            reader.ThrowIfNotEmpty();

            while (sequenceReader.HasData)
            {
                GeneralNameAsn.Decode(sequenceReader, out GeneralNameAsn generalName);

                switch (matchType)
                {
                case GeneralNameType.OtherName:
                    // If the OtherName OID didn't match, move to the next entry.
                    if (generalName.OtherName.HasValue && generalName.OtherName.Value.TypeId == otherOid)
                    {
                        // Currently only UPN is supported, which is a UTF8 string per
                        // https://msdn.microsoft.com/en-us/library/ff842518.aspx
                        AsnReader nameReader = new AsnReader(generalName.OtherName.Value.Value, AsnEncodingRules.DER);
                        string    udnName    = nameReader.ReadCharacterString(UniversalTagNumber.UTF8String);
                        nameReader.ThrowIfNotEmpty();
                        return(udnName);
                    }
                    break;

                case GeneralNameType.Rfc822Name:
                    if (generalName.Rfc822Name != null)
                    {
                        return(generalName.Rfc822Name);
                    }
                    break;

                case GeneralNameType.DnsName:
                    if (generalName.DnsName != null)
                    {
                        return(generalName.DnsName);
                    }
                    break;

                case GeneralNameType.UniformResourceIdentifier:
                    if (generalName.Uri != null)
                    {
                        return(generalName.Uri);
                    }
                    break;
                }
            }

            return(null);
        }
Пример #3
0
        private static string FindAltNameMatch(byte[] extensionBytes, GeneralNameType matchType, string otherOid)
        {
            // If Other, have OID, else, no OID.
            Debug.Assert(
                (otherOid == null) == (matchType != GeneralNameType.OtherName),
                $"otherOid has incorrect nullarity for matchType {matchType}");

            Debug.Assert(
                matchType == GeneralNameType.UniformResourceIdentifier ||
                matchType == GeneralNameType.DnsName ||
                matchType == GeneralNameType.Email ||
                matchType == GeneralNameType.OtherName,
                $"matchType ({matchType}) is not currently supported");

            Debug.Assert(
                otherOid == null || otherOid == Oids.UserPrincipalName,
                $"otherOid ({otherOid}) is not supported");

            // SubjectAltName ::= GeneralNames
            //
            // IssuerAltName ::= GeneralNames
            //
            // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
            //
            // GeneralName ::= CHOICE {
            //   otherName                       [0]     OtherName,
            //   rfc822Name                      [1]     IA5String,
            //   dNSName                         [2]     IA5String,
            //   x400Address                     [3]     ORAddress,
            //   directoryName                   [4]     Name,
            //   ediPartyName                    [5]     EDIPartyName,
            //   uniformResourceIdentifier       [6]     IA5String,
            //   iPAddress                       [7]     OCTET STRING,
            //   registeredID                    [8]     OBJECT IDENTIFIER }
            //
            // OtherName::= SEQUENCE {
            //   type - id    OBJECT IDENTIFIER,
            //   value[0] EXPLICIT ANY DEFINED BY type - id }

            byte expectedTag = (byte)(DerSequenceReader.ContextSpecificTagFlag | (byte)matchType);

            if (matchType == GeneralNameType.OtherName)
            {
                expectedTag |= DerSequenceReader.ConstructedFlag;
            }

            DerSequenceReader altNameReader = new DerSequenceReader(extensionBytes);

            while (altNameReader.HasData)
            {
                if (altNameReader.PeekTag() != expectedTag)
                {
                    altNameReader.SkipValue();
                    continue;
                }

                switch (matchType)
                {
                case GeneralNameType.OtherName:
                {
                    DerSequenceReader otherNameReader = altNameReader.ReadSequence();
                    string            oid             = otherNameReader.ReadOidAsString();

                    if (oid == otherOid)
                    {
                        // Payload is value[0] EXPLICIT, meaning
                        // a) it'll be tagged as ContextSpecific0
                        // b) that's interpretable as a Sequence (EXPLICIT)
                        // c) the payload will then be retagged as the correct type (EXPLICIT)
                        if (otherNameReader.PeekTag() != DerSequenceReader.ContextSpecificConstructedTag0)
                        {
                            throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
                        }

                        otherNameReader = otherNameReader.ReadSequence();

                        // Currently only UPN is supported, which is a UTF8 string per
                        // https://msdn.microsoft.com/en-us/library/ff842518.aspx
                        return(otherNameReader.ReadUtf8String());
                    }

                    // If the OtherName OID didn't match, move to the next entry.
                    continue;
                }

                case GeneralNameType.Rfc822Name:
                case GeneralNameType.DnsName:
                case GeneralNameType.UniformResourceIdentifier:
                    return(altNameReader.ReadIA5String());

                default:
                    altNameReader.SkipValue();
                    continue;
                }
            }

            return(null);
        }
Пример #4
0
        private string FormatSubjectAlternativeName(byte[] rawData)
        {
            // Because SubjectAlternativeName is a commonly parsed structure, we'll
            // specifically format this one.  And we'll match the OpenSSL format, which
            // includes not localizing any of the values (or respecting the multiLine boolean)
            //
            // The intent here is to be functionally equivalent to OpenSSL GENERAL_NAME_print.

            // The end size of this string is hard to predict.
            // * dNSName values have a tag that takes four characters to represent ("DNS:")
            //   and then their payload is ASCII encoded (so one byte -> one char), so they
            //   work out to be about equal (in chars) to their DER encoded length (in bytes).
            // * iPAddress values have a tag that takes 11 characters ("IP Address:") and then
            //   grow from 4 bytes to up to 15 characters for IPv4, or 16 bytes to 47 characters
            //   for IPv6
            //
            // So use a List<string> and just Concat them all when we're done, and we reduce the
            // number of times we copy the header values (vs pointers to the header values).
            List <string> segments = new List <string>();

            try
            {
                // SubjectAltName ::= GeneralNames
                //
                // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
                //
                // GeneralName ::= CHOICE {
                //   otherName                       [0]     OtherName,
                //   rfc822Name                      [1]     IA5String,
                //   dNSName                         [2]     IA5String,
                //   x400Address                     [3]     ORAddress,
                //   directoryName                   [4]     Name,
                //   ediPartyName                    [5]     EDIPartyName,
                //   uniformResourceIdentifier       [6]     IA5String,
                //   iPAddress                       [7]     OCTET STRING,
                //   registeredID                    [8]     OBJECT IDENTIFIER }
                //
                // OtherName::= SEQUENCE {
                //   type - id    OBJECT IDENTIFIER,
                //   value[0] EXPLICIT ANY DEFINED BY type - id }
                DerSequenceReader altNameReader = new DerSequenceReader(rawData);

                while (altNameReader.HasData)
                {
                    if (segments.Count != 0)
                    {
                        segments.Add(CommaSpace);
                    }

                    byte tag = altNameReader.PeekTag();

                    if ((tag & DerSequenceReader.ContextSpecificTagFlag) == 0)
                    {
                        // All GeneralName values need the ContextSpecific flag.
                        return(null);
                    }

                    GeneralNameType nameType = (GeneralNameType)(tag & DerSequenceReader.TagNumberMask);

                    bool needsConstructedFlag = false;

                    switch (nameType)
                    {
                    case GeneralNameType.OtherName:
                    case GeneralNameType.X400Address:
                    case GeneralNameType.DirectoryName:
                    case GeneralNameType.EdiPartyName:
                        needsConstructedFlag = true;
                        break;
                    }

                    if (needsConstructedFlag &&
                        (tag & DerSequenceReader.ConstructedFlag) == 0)
                    {
                        // All of the SEQUENCE types require the constructed bit,
                        // or OpenSSL will have refused to print it.
                        return(null);
                    }

                    switch (nameType)
                    {
                    case GeneralNameType.OtherName:
                        segments.Add("othername:<unsupported>");
                        altNameReader.SkipValue();
                        break;

                    case GeneralNameType.Rfc822Name:
                        segments.Add("email:");
                        segments.Add(altNameReader.ReadIA5String());
                        break;

                    case GeneralNameType.DnsName:
                        segments.Add("DNS:");
                        segments.Add(altNameReader.ReadIA5String());
                        break;

                    case GeneralNameType.X400Address:
                        segments.Add("X400Name:<unsupported>");
                        altNameReader.SkipValue();
                        break;

                    case GeneralNameType.DirectoryName:
                        // OpenSSL supports printing one of these, but the logic lives in X509Certificates,
                        // and it isn't very common.  So we'll skip this one until someone asks for it.
                        segments.Add("DirName:<unsupported>");
                        altNameReader.SkipValue();
                        break;

                    case GeneralNameType.EdiPartyName:
                        segments.Add("EdiPartyName:<unsupported>");
                        altNameReader.SkipValue();
                        break;

                    case GeneralNameType.UniformResourceIdentifier:
                        segments.Add("URI:");
                        segments.Add(altNameReader.ReadIA5String());
                        break;

                    case GeneralNameType.IPAddress:
                        segments.Add("IP Address");

                        byte[] ipAddressBytes = altNameReader.ReadOctetString();

                        if (ipAddressBytes.Length == 4)
                        {
                            // Add the colon and dotted-decimal representation of IPv4.
                            segments.Add(
                                $":{ipAddressBytes[0]}.{ipAddressBytes[1]}.{ipAddressBytes[2]}.{ipAddressBytes[3]}");
                        }
                        else if (ipAddressBytes.Length == 16)
                        {
                            // Print the IP Address value as colon separated UInt16 hex values without leading zeroes.
                            // 20 01 0D B8 AC 10 FE 01 00 00 00 00 00 00 00 00
                            //
                            // IP Address:2001:DB8:AC10:FE01:0:0:0:0
                            for (int i = 0; i < ipAddressBytes.Length; i += 2)
                            {
                                segments.Add($":{ipAddressBytes[i] << 8 | ipAddressBytes[i + 1]:X}");
                            }
                        }
                        else
                        {
                            segments.Add(":<invalid>");
                        }

                        break;

                    case GeneralNameType.RegisteredId:
                        segments.Add("Registered ID:");
                        segments.Add(altNameReader.ReadOidAsString());
                        break;

                    default:
                        // A new extension to GeneralName could legitimately hit this,
                        // but it's correct to say that until we know what that is that
                        // the pretty-print has failed, and we should fall back to hex.
                        //
                        // But it could also simply be poorly encoded user data.
                        return(null);
                    }
                }

                return(string.Concat(segments));
            }
            catch (CryptographicException)
            {
                return(null);
            }
        }