예제 #1
0
    static int GetRSAPublicKeySize(byte[] kv)
    {
        AsnElt ae = AsnElt.Decode(kv);

        ae.CheckTag(AsnElt.SEQUENCE);
        ae.CheckNumSub(2);
        AsnElt ai = ae.GetSub(0);

        ai.CheckTag(AsnElt.INTEGER);
        ai.CheckPrimitive();
        byte[] v = ai.CopyValue();
        if (v.Length > 0 && v[0] >= 0x80)
        {
            throw new AsnException(
                      "Invalid RSA modulus (negative)");
        }
        int bitLen = M.BitLength(v);

        if (bitLen < 512)
        {
            throw new AsnException(string.Format(
                                       "Invalid RSA modulus ({0} bits)", bitLen));
        }
        else if ((v[v.Length - 1] & 0x01) == 0)
        {
            throw new AsnException("Invalid RSA modulus (even)");
        }
        return(bitLen);
    }
예제 #2
0
    /*
     * This method expects the DSA parameters, as an ASN.1 object.
     */
    static int GetDSAPublicKeySize(AsnElt adp)
    {
        if (adp == null)
        {
            /*
             * No parameters -- this means inheritance from
             * the CA, which we do not analyse because we do
             * not do chain building.
             */
            return(0);
        }
        adp.CheckTag(AsnElt.SEQUENCE);
        adp.CheckNumSub(3);
        foreach (AsnElt ai in adp.Sub)
        {
            ai.CheckTag(AsnElt.INTEGER);
            ai.CheckPrimitive();
        }
        byte[] v = adp.GetSub(0).CopyValue();
        if (v.Length > 0 && v[0] >= 0x80)
        {
            throw new AsnException(
                      "Invalid RSA modulus (negative)");
        }
        int bitLen = M.BitLength(v);

        /*
         * Acceptable modulus sizes for DSA have varied with
         * successive versions of FIPS 186:
         *   512 to 1024, and multiple of 64 (FIPS 186-1)
         *   1024 only (FIPS 186-2)
         *   1024, 2048 or 3072 (FIPS 186-3 and 186-4)
         *
         * Future versions might allow larger lengths. We
         * apply the following rules: acceptable sizes are
         * either multiple of 1024, or multiple of 64 in
         * the 512..1024 range.
         */
        bool goodLen;

        if (bitLen < 1024)
        {
            goodLen = (bitLen >= 512 && ((bitLen & 0x3F) == 0));
        }
        else
        {
            goodLen = ((bitLen & 0x3FF) == 0);
        }
        if (!goodLen)
        {
            throw new AsnException(string.Format(
                                       "Invalid DSA modulus ({0} bits)", bitLen));
        }
        else if ((v[v.Length - 1] & 0x01) == 0)
        {
            throw new AsnException("Invalid DSA modulus (even)");
        }
        return(bitLen);
    }
예제 #3
0
        static IPublicKey DecodePublicKeyRSA(byte[] pub)
        {
            AsnElt ae = AsnElt.Decode(pub);

            ae.CheckTag(AsnElt.SEQUENCE);
            ae.CheckNumSub(2);
            byte[] n = GetPositiveInteger(ae.GetSub(0));
            byte[] e = GetPositiveInteger(ae.GetSub(1));
            return(new RSAPublicKey(n, e));
        }
예제 #4
0
        static IPrivateKey DecodePrivateKeyPKCS8(AsnElt ak)
        {
            ak.CheckNumSub(3);
            ak.GetSub(0).CheckTag(AsnElt.INTEGER);
            long v = ak.GetSub(0).GetInteger();

            if (v != 0)
            {
                throw new AsnException(
                          "Unsupported PKCS#8 version: " + v);
            }
            AsnElt aai = ak.GetSub(1);

            aai.CheckTag(AsnElt.SEQUENCE);
            aai.CheckNumSubMin(1);
            aai.CheckNumSubMin(2);
            aai.GetSub(0).CheckTag(AsnElt.OBJECT_IDENTIFIER);
            string oid = aai.GetSub(0).GetOID();

            ak.GetSub(2).CheckTag(AsnElt.OCTET_STRING);
            byte[] rawKey = ak.GetSub(2).CopyValue();
            AsnElt ark    = AsnElt.Decode(rawKey);

            switch (oid)
            {
            case OID_RSA:
            case OID_RSA_OAEP:
            case OID_RSA_PSS:
                return(DecodePrivateKeyRSA(ark));

            /* disabled DSA
             * case OID_DSA:
             *      return DecodePrivateKeyDSA(ark);
             */
            case OID_EC:
                /*
                 * For elliptic curves, the parameters may
                 * include the curve specification.
                 */
                ECCurve curve = (aai.Sub.Length == 2)
                                ? DecodeCurve(aai.GetSub(1)) : null;
                return(DecodePrivateKeyEC(ark, curve));

            default:
                throw new AsnException(
                          "Unknown PKCS#8 key type: " + oid);
            }
        }
예제 #5
0
        /*
         * Generic parsing. If 'strictStrings' is true, then the following
         * rules are enforced:
         * -- Every SET in the sequence of RDN must have size 1.
         * -- Every name element is decoded as a string (by tag).
         *
         * If 'strictStrings' is false, then multiple elements may appear
         * in each SET, and values needs not be decodable as string (values
         * with a known OID must still be decodable).
         *
         * This constructor checks that within a single RDN, no two
         * attributes may have the same type.
         *
         * On decoding error, an AsnException is thrown.
         */
        public X500Name(AsnElt aDN, bool strictStrings)
        {
            /*
             * Note: the SEQUENCE tag MUST be present, since the
             * ASN.1 definition of Name starts with a CHOICE; thus,
             * any tag override would have to be explicit, not
             * implicit.
             */
            aDN.CheckConstructed();
            aDN.CheckTag(AsnElt.SEQUENCE);
            List <List <DNPart> > r = new List <List <DNPart> >();

            foreach (AsnElt aRDN in aDN.Sub)
            {
                aRDN.CheckConstructed();
                aRDN.CheckTag(AsnElt.SET);
                aRDN.CheckNumSubMin(1);
                int n = aRDN.Sub.Length;
                if (n != 1 && strictStrings)
                {
                    throw new AsnException(String.Format(
                                               "several ({0}) values in RDN", n));
                }
                List <DNPart> r2 = new List <DNPart>();
                r.Add(r2);
                for (int i = 0; i < n; i++)
                {
                    AsnElt aTV = aRDN.Sub[i];
                    aTV.CheckConstructed();
                    aTV.CheckTag(AsnElt.SEQUENCE);
                    aTV.CheckNumSub(2);
                    AsnElt aOID = aTV.GetSub(0);
                    aOID.CheckTag(AsnElt.OBJECT_IDENTIFIER);
                    AsnElt aVal = aTV.GetSub(1);
                    string nt   = aOID.GetOID();
                    DNPart dnp  = new DNPart(nt, aVal);
                    if (strictStrings && !dnp.IsString)
                    {
                        throw new AsnException(
                                  "RDN is not a string");
                    }
                    r2.Add(dnp);
                }
            }
            Init(r);
        }
예제 #6
0
        /*
         * Decode a public key (SubjectPublicKeyInfo).
         */
        public static IPublicKey DecodePublicKey(AsnElt ak)
        {
            ak.CheckNumSub(2);
            AlgorithmIdentifier ai = new AlgorithmIdentifier(ak.GetSub(0));
            AsnElt abs             = ak.GetSub(1);

            abs.CheckTag(AsnElt.BIT_STRING);
            byte[] pub = abs.GetBitString();
            switch (ai.OID)
            {
            case OID_RSA:
            case OID_RSA_OAEP:
            case OID_RSA_PSS:
                return(DecodePublicKeyRSA(pub));

            /* disabled DSA
             * case OID_DSA:
             *      return DecodePublicKeyDSA(pub);
             */
            case OID_EC:
                /*
                 * For elliptic curves, the parameters should
                 * include the curve specification.
                 */
                AsnElt ap = ai.Parameters;
                if (ap == null)
                {
                    throw new AsnException("No curve specified"
                                           + " for EC public key");
                }
                if (ap.TagClass != AsnElt.UNIVERSAL ||
                    ap.TagValue != AsnElt.OBJECT_IDENTIFIER)
                {
                    throw new AsnException("Unsupported type"
                                           + " of curve specification");
                }
                return(new ECPublicKey(OIDToCurve(ap.GetOID()), pub));

            default:
                throw new AsnException(
                          "Unknown public key type: " + ai.OID);
            }
        }
예제 #7
0
        static RSAPrivateKey DecodePrivateKeyRSA(AsnElt ak)
        {
            ak.CheckNumSubMin(9);
            ak.GetSub(0).CheckTag(AsnElt.INTEGER);
            long kt = ak.GetSub(0).GetInteger();

            if (kt != 0)
            {
                throw new AsnException(
                          "Unsupported RSA key type: " + kt);
            }
            ak.CheckNumSub(9);
            return(new RSAPrivateKey(
                       GetPositiveInteger(ak.GetSub(1)),
                       GetPositiveInteger(ak.GetSub(2)),
                       GetPositiveInteger(ak.GetSub(3)),
                       GetPositiveInteger(ak.GetSub(4)),
                       GetPositiveInteger(ak.GetSub(5)),
                       GetPositiveInteger(ak.GetSub(6)),
                       GetPositiveInteger(ak.GetSub(7)),
                       GetPositiveInteger(ak.GetSub(8))));
        }
예제 #8
0
    static string GetSignHashName(AlgorithmIdentifier ai)
    {
        switch (ai.OID)
        {
        /*
         * RSA PKCS#1 v1.5.
         */
        case "1.2.840.113549.1.1.2":
            return("MD2");

        case "1.2.840.113549.1.1.4":
            return("MD5");

        case "1.2.840.113549.1.1.5":
            return("SHA-1");

        case "1.2.840.113549.1.1.14":
            return("SHA-224");

        case "1.2.840.113549.1.1.11":
            return("SHA-256");

        case "1.2.840.113549.1.1.12":
            return("SHA-384");

        case "1.2.840.113549.1.1.13":
            return("SHA-512");

        /*
         * RSA PSS.
         * Parameters are:
         * RSASSA-PSS-params ::= SEQUENCE {
         *     hashAlgorithm    [0] HashAlgorithm     DEFAULT sha1,
         *     maskGenAlgorithm [1] MaskGenAlgorithm  DEFAULT mgf1SHA1,
         *     saltLength       [2] INTEGER           DEFAULT 20,
         *     trailerField     [3] TrailerField      DEFAULT tfb
         * }
         * We are only interested in the first field, which is
         * an AlgorithmIdentifier structure.
         */
        case "1.2.840.113549.1.1.10":
            AsnElt apss = ai.Parameters;
            if (apss == null)
            {
                return("SHA-1");
            }
            apss.CheckNumSubMax(4);
            if (apss.Sub.Length == 0)
            {
                return("SHA-1");
            }
            AsnElt apss0 = apss.GetSub(0);
            if (apss0.TagClass != AsnElt.CONTEXT ||
                apss0.TagValue != 0)
            {
                return("SHA-1");
            }
            apss0.CheckNumSub(1);
            AlgorithmIdentifier ahi =
                new AlgorithmIdentifier(apss0.GetSub(0));
            return(GetHashName(ahi.OID));

        /*
         * DSA (RFC 3279 and 5758).
         */
        case "1.2.840.10040.4.1":
            AsnElt adsa = ai.Parameters;
            if (ai == null)
            {
                throw new AsnException(
                          "Missing hash function for DSA");
            }
            AlgorithmIdentifier ahd = new AlgorithmIdentifier(adsa);
            return(GetHashName(ahd.OID));

        case "1.2.840.10040.4.3":
            return("SHA-1");

        case "2.16.840.1.101.3.4.3.1":
            return("SHA-224");

        case "2.16.840.1.101.3.4.3.2":
            return("SHA-256");

        /*
         * ECDSA (ANSI X9.62:2005).
         */
        case "1.2.840.10045.4.1":
            return("SHA-1");

        case "1.2.840.10045.4.3":
            AsnElt aec = ai.Parameters;
            if (ai == null)
            {
                throw new AsnException(
                          "Missing hash function for ECDSA");
            }
            AlgorithmIdentifier ahe = new AlgorithmIdentifier(aec);
            return(GetHashName(ahe.OID));

        case "1.2.840.10045.4.3.1":
            return("SHA-224");

        case "1.2.840.10045.4.3.2":
            return("SHA-256");

        case "1.2.840.10045.4.3.3":
            return("SHA-384");

        case "1.2.840.10045.4.3.4":
            return("SHA-512");

        /* TODO: GOST R 34.10-94 and GOST R 34.10-2001 */

        default:
            return("UNKNOWN");
        }
    }
예제 #9
0
    /*
     * Create an instance by decoding the provided object.
     * This constructor assumes ASN.1 DER encoding (not Base64,
     * not PEM).
     *
     * On decoding error, an AsnException is thrown.
     */
    public X509Cert(byte[] cert)
    {
        /*
         * Compute thumbprint.
         */
        thumbprint = M.DoSHA1(cert).ToUpperInvariant();

        /*
         * Outer layer decoding and extraction of the signature
         * hash algorithm.
         */
        AsnElt ac = AsnElt.Decode(cert);

        ac.CheckTag(AsnElt.SEQUENCE);
        ac.CheckNumSub(3);
        hashAlgorithm = GetSignHashName(
            new AlgorithmIdentifier(ac.GetSub(1)));

        /*
         * TBS exploration. First field is optional; if present,
         * it contains the certificate version.
         */
        AsnElt atbs = ac.GetSub(0);

        atbs.CheckNumSubMin(6);
        atbs.CheckNumSubMax(10);
        int off = 0;

        if (atbs.GetSub(0).TagValue == 0)
        {
            off++;
        }

        /*
         * Serial numer: nominally an INTEGER, we extract the
         * raw bytes, because some CA wrongly use unsigned
         * encoding.
         */
        AsnElt aserial = atbs.GetSub(off);

        aserial.CheckTag(AsnElt.INTEGER);
        byte[] sv  = aserial.CopyValue();
        int    svk = 0;

        while (svk < sv.Length && sv[svk] == 0)
        {
            svk++;
        }
        if (svk == sv.Length)
        {
            serialHex = "00";
        }
        else
        {
            StringBuilder sb = new StringBuilder();
            while (svk < sv.Length)
            {
                sb.AppendFormat("{0:X2}", sv[svk++]);
            }
            serialHex = sb.ToString();
        }

        /*
         * Issuer and subject DN.
         */
        issuerDN  = new X500Name(atbs.GetSub(off + 2));
        subjectDN = new X500Name(atbs.GetSub(off + 4));

        /*
         * Validity dates.
         */
        AsnElt adates = atbs.GetSub(off + 3);

        adates.CheckTag(AsnElt.SEQUENCE);
        adates.CheckNumSub(2);
        validFrom = adates.GetSub(0).GetTime();
        validTo   = adates.GetSub(1).GetTime();

        /*
         * Public key.
         */
        AsnElt aspki = atbs.GetSub(off + 5);

        aspki.CheckTag(AsnElt.SEQUENCE);
        aspki.CheckNumSub(2);
        AlgorithmIdentifier kt =
            new AlgorithmIdentifier(aspki.GetSub(0));
        AsnElt aktp = kt.Parameters;
        AsnElt apkv = aspki.GetSub(1);

        apkv.CheckTag(AsnElt.BIT_STRING);
        byte[] kv = apkv.GetBitString();
        curveOID = null;
        keyType  = "UNKNOWN";
        keySize  = 0;
        switch (kt.OID)
        {
        /*
         * RSA public keys should use the 'rsaEncryption' OID,
         * but some are tagged with the OAEP or the PSS OID,
         * to somehow specify that the RSA key should be used
         * only with OAEP or PSS.
         */
        case "1.2.840.113549.1.1.1":
        case "1.2.840.113549.1.1.7":
        case "1.2.840.113549.1.1.10":
            keyType = "RSA";
            keySize = GetRSAPublicKeySize(kv);
            break;

        /*
         * All DSA public keys should use that OID.
         */
        case "1.2.840.10040.4.1":
            keyType = "DSA";
            keySize = GetDSAPublicKeySize(aktp);
            break;

        /*
         * Elliptic curve keys.
         * We only support "normal" elliptic curve keys, not
         * restricted keys.
         * We only supported named curves (RFC 5480 forbids
         * explicit curve parameters).
         */
        case "1.2.840.10045.2.1":
            if (aktp == null)
            {
                break;
            }
            if (aktp.TagClass != AsnElt.UNIVERSAL ||
                aktp.TagValue != AsnElt.OBJECT_IDENTIFIER)
            {
                break;
            }
            keyType  = "EC";
            curveOID = aktp.GetOID();
            keySize  = GetCurveSize(curveOID);
            break;

            /* TODO: GOST R 34.10-94 and GOST R 34.10-2001 */
        }

        /*
         * If there are extensions, process them.
         * extract the dNSNames.
         */
        serverNames = null;
        extensions  = new SortedDictionary <string, Extension>(
            StringComparer.Ordinal);

        for (int i = off + 6; i < atbs.Sub.Length; i++)
        {
            AsnElt aexts = atbs.GetSub(i);
            if (aexts.TagClass != AsnElt.CONTEXT ||
                aexts.TagValue != 3)
            {
                continue;
            }
            aexts.CheckNumSub(1);
            aexts = aexts.GetSub(0);
            aexts.CheckTag(AsnElt.SEQUENCE);
            foreach (AsnElt aext in aexts.Sub)
            {
                aext.CheckTag(AsnElt.SEQUENCE);
                aext.CheckNumSubMin(2);
                aext.CheckNumSubMax(3);
                AsnElt aoid = aext.GetSub(0);
                aoid.CheckTag(AsnElt.OBJECT_IDENTIFIER);
                string oid = aoid.GetOID();
                AsnElt av;
                bool   critical = false;
                if (aext.Sub.Length == 2)
                {
                    av = aext.GetSub(1);
                }
                else
                {
                    AsnElt acrit = aext.GetSub(1);
                    acrit.CheckTag(AsnElt.BOOLEAN);
                    critical = acrit.GetBoolean();
                    av       = aext.GetSub(2);
                }
                av.CheckTag(AsnElt.OCTET_STRING);
                Extension ext = new Extension(
                    oid, critical, av.CopyValue());
                if (extensions.ContainsKey(oid))
                {
                    throw new AsnException(
                              "duplicate extension " + oid);
                }
                extensions[oid] = ext;
                ProcessExtension(ext);
            }
        }

        /*
         * If there was no SAN, or no dNSName in the SAN, then
         * get the Common Name from the subjectDN.
         */
        string cn = null;

        foreach (DNPart dnp in subjectDN.Parts)
        {
            if (dnp.FriendlyType == DNPart.COMMON_NAME)
            {
                if (cn != null)
                {
                    throw new AsnException(
                              "multiple CN in subject DN");
                }
                cn = dnp.Value;
            }
        }
        if (serverNames == null)
        {
            if (cn == null)
            {
                serverNames = new string[0];
            }
            else
            {
                serverNames = new string[] { cn };
            }
        }
    }
예제 #10
0
        static ECPrivateKey DecodePrivateKeyEC(AsnElt ak, ECCurve curve)
        {
            ak.CheckNumSubMin(2);
            ak.GetSub(0).CheckTag(AsnElt.INTEGER);
            ak.GetSub(1).CheckTag(AsnElt.OCTET_STRING);
            long kt = ak.GetSub(0).GetInteger();

            if (kt != 1)
            {
                throw new AsnException(
                          "Unsupported EC key type: " + kt);
            }
            byte[] x   = ak.GetSub(1).CopyValue();
            byte[] pub = null;
            int    n   = ak.Sub.Length;
            int    p   = 2;

            if (p < n)
            {
                AsnElt acc = ak.GetSub(p);
                if (acc.TagClass == AsnElt.CONTEXT &&
                    acc.TagValue == 0)
                {
                    acc.CheckNumSub(1);
                    acc = acc.GetSub(0);
                    ECCurve curve2 = DecodeCurve(acc);

                    /*
                     * Here, we support only named curves.
                     */
                    /* obsolete
                     */
                    if (curve == null)
                    {
                        curve = curve2;
                    }
                    else if (!curve.Equals(curve2))
                    {
                        throw new AsnException(string.Format(
                                                   "Inconsistent curve"
                                                   + " specification ({0} / {1})",
                                                   curve.Name, curve2.Name));
                    }

                    p++;
                }
            }
            if (p < n)
            {
                AsnElt acc = ak.GetSub(p);
                if (acc.TagClass == AsnElt.CONTEXT &&
                    acc.TagValue == 1)
                {
                    acc.CheckNumSub(1);
                    acc = acc.GetSub(0);
                    acc.CheckTag(AsnElt.BIT_STRING);
                    pub = acc.GetBitString();
                }
            }

            if (curve == null)
            {
                throw new AsnException("No curve specified for EC key");
            }
            ECPrivateKey esk = new ECPrivateKey(curve, x);

            if (pub != null)
            {
                ECPublicKey epk = new ECPublicKey(curve, pub);
                if (!epk.Equals(esk.PublicKey))
                {
                    throw new CryptoException(
                              "EC key pair public/private mismatch");
                }
            }
            return(esk);
        }