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)); }
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); } }
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); }
/* * 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); }
/* * 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); }
/* * 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); } }
/* * Create an instance over the provided ASN.1 element. If * 'checkTag' is true, then the outer tag will be checked to * match the universal tag for SEQUENCE. Set 'checkTag' to * false if the tag was already checked, or if it has been * overwritten with an implicit tag. */ internal AlgorithmIdentifier(AsnElt ai, bool checkTag) { if (checkTag) { ai.CheckTag(AsnElt.SEQUENCE); } ai.CheckNumSubMin(1); ai.CheckNumSubMax(2); AsnElt ao = ai.GetSub(0); ao.CheckTag(AsnElt.OBJECT_IDENTIFIER); oid = ao.GetOID(); if (ai.Sub.Length >= 2) { parameters = ai.GetSub(1); } else { parameters = null; } }
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)))); }
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"); } }
/* * 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 }; } } }
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); }
/* * Decode the provided private key. This method accepts both * PKCS#8 and the "internal" format; the source object may be * raw DER, Base64-encoded DER, or PEM. The key type is * automatically detected. */ public static IPrivateKey DecodePrivateKey(byte[] enc) { string pemType; enc = AsnIO.FindBER(enc, false, out pemType); if (enc == null) { throw new AsnException("Not an encoded object"); } AsnElt ak = AsnElt.Decode(enc); ak.CheckConstructed(); if (pemType != null) { switch (pemType) { case "RSA PRIVATE KEY": return(DecodePrivateKeyRSA(ak)); /* disabled DSA * case "DSA PRIVATE KEY": * return DecodePrivateKeyDSA(ak); */ case "EC PRIVATE KEY": return(DecodePrivateKeyEC(ak)); case "PRIVATE KEY": return(DecodePrivateKeyPKCS8(ak)); default: throw new AsnException( "Unknown PEM object: " + pemType); } } if (ak.Sub.Length == 3 && ak.GetSub(0).TagValue == AsnElt.INTEGER && ak.GetSub(1).TagValue == AsnElt.SEQUENCE && ak.GetSub(2).TagValue == AsnElt.OCTET_STRING) { return(DecodePrivateKeyPKCS8(ak)); } if (ak.Sub.Length >= 9) { bool mayBeRSA = true; for (int i = 0; i < 9; i++) { if (ak.GetSub(i).TagValue != AsnElt.INTEGER) { mayBeRSA = false; break; } } if (mayBeRSA) { return(DecodePrivateKeyRSA(ak)); } } /* disabled DSA * if (ak.Sub.Length >= 6) { * bool mayBeDSA = true; * for (int i = 0; i < 6; i ++) { * if (ak.GetSub(i).TagValue != AsnElt.INTEGER) { * mayBeDSA = false; * break; * } * } * if (mayBeDSA) { * return DecodePrivateKeyDSA(ak); * } * } */ if (ak.Sub.Length >= 2 && ak.GetSub(0).TagValue == AsnElt.INTEGER && ak.GetSub(1).TagValue == AsnElt.OCTET_STRING) { return(DecodePrivateKeyEC(ak)); } throw new AsnException("Unrecognized private key format"); }