Esempio n. 1
0
        internal static GeneralName Read(DerSequenceReader reader)
        {
            var tag = reader.PeekTag();

            // Per RFC 2634 section 5.4.1 (https://tools.ietf.org/html/rfc2634#section-5.4.1)
            // only the directory name choice (#4) is allowed.
            if (tag == DerSequenceReader.ContextSpecificConstructedTag4)
            {
                var value = reader.ReadValue((DerSequenceReader.DerTag)DerSequenceReader.ContextSpecificConstructedTag4);

                if (reader.HasData)
                {
                    throw new SignatureException(Strings.InvalidAsn1);
                }

                var directoryName = new X500DistinguishedName(value);

                return(new GeneralName(directoryName));
            }

            while (reader.HasData)
            {
                reader.ValidateAndSkipDerValue();
            }

            throw new SignatureException(Strings.UnsupportedAsn1);
        }
Esempio n. 2
0
        public static void NoSupportForMultiByteTags(string caseName, string hexInput)
        {
            byte[]            bytes  = hexInput.HexToByteArray();
            DerSequenceReader reader = DerSequenceReader.CreateForPayload(bytes);

            Assert.Throws <CryptographicException>(() => reader.PeekTag());
            Assert.Throws <CryptographicException>(() => reader.SkipValue());
            Assert.Throws <CryptographicException>(() => reader.ReadNextEncodedValue());
        }
Esempio n. 3
0
        public static void InvalidLengthSpecified(string hexInput)
        {
            byte[]            bytes  = hexInput.HexToByteArray();
            DerSequenceReader reader = DerSequenceReader.CreateForPayload(bytes);

            // Doesn't throw.
            reader.PeekTag();

            // Since EatTag will have succeeded the reader needs to be reconstructed after each test.
            Assert.Throws <CryptographicException>(() => reader.SkipValue());
            reader = DerSequenceReader.CreateForPayload(bytes);

            Assert.Throws <CryptographicException>(() => reader.ReadOctetString());
            reader = DerSequenceReader.CreateForPayload(bytes);

            Assert.Throws <CryptographicException>(() => reader.ReadNextEncodedValue());
        }
        public void FindByTemplateName(string templateName)
        {
            FindCore(
                cert =>
                {
                    X509Extension ext = FindExtension(cert, Oids.EnrollCertTypeExtension);

                    if (ext != null)
                    {
                        // Try a V1 template structure, just a string:
                        string decodedName = Interop.Crypto.DerStringToManagedString(ext.RawData);

                        // If this doesn't match, maybe a V2 template will
                        if (StringComparer.OrdinalIgnoreCase.Equals(templateName, decodedName))
                        {
                            return true;
                        }
                    }

                    ext = FindExtension(cert, Oids.CertificateTemplate);

                    if (ext != null)
                    {
                        DerSequenceReader reader = new DerSequenceReader(ext.RawData);
                        // SEQUENCE (
                        //     OID oid,
                        //     INTEGER major,
                        //     INTEGER minor OPTIONAL
                        //  )

                        if (reader.PeekTag() == (byte)DerSequenceReader.DerTag.ObjectIdentifier)
                        {
                            Oid oid = reader.ReadOid();

                            if (StringComparer.Ordinal.Equals(templateName, oid.Value))
                            {
                                return true;
                            }
                        }
                    }

                    return false;
                });
        }
Esempio n. 5
0
        private static void ReadCertPolicyConstraintsExtension(X509Extension extension, CertificatePolicy policy)
        {
            DerSequenceReader reader = new DerSequenceReader(extension.RawData);

            while (reader.HasData)
            {
                // Policy Constraints context specific tag values are defined in RFC 3280 4.2.1.12,
                // and restated (unchanged) in RFC 5280 4.2.1.11.
                switch (reader.PeekTag())
                {
                    case DerSequenceReader.ContextSpecificTagFlag | 0:
                        policy.RequireExplicitPolicyDepth = reader.ReadInteger();
                        break;
                    case DerSequenceReader.ContextSpecificTagFlag | 1:
                        policy.InhibitMappingDepth = reader.ReadInteger();
                        break;
                    default:
                        if (extension.Critical)
                        {
                            // If an unknown value is read, but we're marked as critical,
                            // then we don't know what we're doing and MUST fail validation
                            // (RFC 3280).
                            // If it isn't critical then it means we're allowed to be ignorant
                            // of data defined more recently than we understand.
                            throw new CryptographicException();
                        }

                        break;
                }
            }
        }
Esempio n. 6
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);
            }
        }
        internal static DerSequenceReader SecKeyExport(
            SafeSecKeyRefHandle key,
            bool exportPrivate)
        {
            // Apple requires all private keys to be exported encrypted, but since we're trying to export
            // as parsed structures we will need to decrypt it for the user.
            const string ExportPassword = "******";

            SafeCreateHandle exportPassword = exportPrivate
                ? CoreFoundation.CFStringCreateWithCString(ExportPassword)
                : s_nullExportString;

            int ret;
            SafeCFDataHandle cfData;
            int osStatus;

            try
            {
                ret = AppleCryptoNative_SecKeyExport(
                    key,
                    exportPrivate ? 1 : 0,
                    exportPassword,
                    out cfData,
                    out osStatus);
            }
            finally
            {
                if (exportPassword != s_nullExportString)
                {
                    exportPassword.Dispose();
                }
            }

            byte[] exportedData;

            using (cfData)
            {
                if (ret == 0)
                {
                    throw CreateExceptionForOSStatus(osStatus);
                }

                if (ret != 1)
                {
                    Debug.Fail($"AppleCryptoNative_SecKeyExport returned {ret}");
                    throw new CryptographicException();
                }

                exportedData = CoreFoundation.CFGetData(cfData);
            }

            DerSequenceReader reader = new DerSequenceReader(exportedData);

            if (!exportPrivate)
            {
                return(reader);
            }

            byte tag = reader.PeekTag();

            // PKCS#8 defines two structures, PrivateKeyInfo, which starts with an integer,
            // and EncryptedPrivateKey, which starts with an encryption algorithm (DER sequence).
            if (tag == (byte)DerSequenceReader.DerTag.Integer)
            {
                return(reader);
            }

            const byte ConstructedSequence =
                DerSequenceReader.ConstructedFlag | (byte)DerSequenceReader.DerTag.Sequence;

            if (tag == ConstructedSequence)
            {
                return(ReadEncryptedPkcs8Blob(ExportPassword, reader));
            }

            Debug.Fail($"Data was neither PrivateKey or EncryptedPrivateKey: {tag:X2}");
            throw new CryptographicException();
        }
        private static DerSequenceReader ReadEncryptedPkcs8Blob(string passphrase, DerSequenceReader reader)
        {
            // EncryptedPrivateKeyInfo::= SEQUENCE {
            //    encryptionAlgorithm EncryptionAlgorithmIdentifier,
            //    encryptedData        EncryptedData }
            //
            // EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
            //
            // EncryptedData ::= OCTET STRING
            DerSequenceReader algorithmIdentifier = reader.ReadSequence();
            string            algorithmOid        = algorithmIdentifier.ReadOidAsString();

            // PBES2 (Password-Based Encryption Scheme 2)
            if (algorithmOid != OidPbes2)
            {
                Debug.Fail($"Expected PBES2 ({OidPbes2}), got {algorithmOid}");
                throw new CryptographicException();
            }

            // PBES2-params ::= SEQUENCE {
            //    keyDerivationFunc AlgorithmIdentifier { { PBES2 - KDFs} },
            //    encryptionScheme AlgorithmIdentifier { { PBES2 - Encs} }
            // }

            DerSequenceReader pbes2Params = algorithmIdentifier.ReadSequence();

            algorithmIdentifier = pbes2Params.ReadSequence();

            string kdfOid = algorithmIdentifier.ReadOidAsString();

            // PBKDF2 (Password-Based Key Derivation Function 2)
            if (kdfOid != OidPbkdf2)
            {
                Debug.Fail($"Expected PBKDF2 ({OidPbkdf2}), got {kdfOid}");
                throw new CryptographicException();
            }

            // PBKDF2-params ::= SEQUENCE {
            //   salt CHOICE {
            //     specified OCTET STRING,
            //     otherSource AlgorithmIdentifier { { PBKDF2 - SaltSources} }
            //   },
            //   iterationCount INTEGER (1..MAX),
            //   keyLength INTEGER(1..MAX) OPTIONAL,
            //   prf AlgorithmIdentifier { { PBKDF2 - PRFs} }  DEFAULT algid - hmacWithSHA1
            // }
            DerSequenceReader pbkdf2Params = algorithmIdentifier.ReadSequence();

            byte[] salt      = pbkdf2Params.ReadOctetString();
            int    iterCount = pbkdf2Params.ReadInteger();
            int    keySize   = -1;

            if (pbkdf2Params.HasData && pbkdf2Params.PeekTag() == (byte)DerSequenceReader.DerTag.Integer)
            {
                keySize = pbkdf2Params.ReadInteger();
            }

            if (pbkdf2Params.HasData)
            {
                string prfOid = pbkdf2Params.ReadOidAsString();

                // SHA-1 is the only hash algorithm our PBKDF2 supports.
                if (prfOid != OidSha1)
                {
                    Debug.Fail($"Expected SHA1 ({OidSha1}), got {prfOid}");
                    throw new CryptographicException();
                }
            }

            DerSequenceReader encryptionScheme = pbes2Params.ReadSequence();
            string            cipherOid        = encryptionScheme.ReadOidAsString();

            // DES-EDE3-CBC (TripleDES in CBC mode)
            if (cipherOid != OidTripleDesCbc)
            {
                Debug.Fail($"Expected DES-EDE3-CBC ({OidTripleDesCbc}), got {cipherOid}");
                throw new CryptographicException();
            }

            byte[] decrypted;

            using (TripleDES des3 = TripleDES.Create())
            {
                if (keySize == -1)
                {
                    foreach (KeySizes keySizes in des3.LegalKeySizes)
                    {
                        keySize = Math.Max(keySize, keySizes.MaxSize);
                    }
                }

                byte[] iv = encryptionScheme.ReadOctetString();

                using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(passphrase, salt, iterCount))
                    using (ICryptoTransform decryptor = des3.CreateDecryptor(pbkdf2.GetBytes(keySize / 8), iv))
                    {
                        byte[] encrypted = reader.ReadOctetString();
                        decrypted = decryptor.TransformFinalBlock(encrypted, 0, encrypted.Length);
                    }
            }

            DerSequenceReader pkcs8Reader = new DerSequenceReader(decrypted);

            return(pkcs8Reader);
        }