public static KeyInfo GetType(byte[] data) { if (data == null) throw new ArgumentNullException("data"); KeyInfo ki = KeyInfo.Unknown; try { ASN1 top = new ASN1(data); if ((top.Tag == 0x30) && (top.Count > 0)) { ASN1 firstLevel = top[0]; switch (firstLevel.Tag) { case 0x02: ki = KeyInfo.PrivateKey; break; case 0x30: ki = KeyInfo.EncryptedPrivateKey; break; } } } catch { throw new CryptographicException("invalid ASN.1 data"); } return ki; }
public static ASN1 FromInt32(Int32 value) { byte[] integer = BitConverterLE.GetBytes(value); Array.Reverse(integer); int x = 0; while ((x < integer.Length) && (integer[x] == 0x00)) x++; ASN1 asn1 = new ASN1(0x02); switch (x) { case 0: asn1.Value = integer; break; case 4: asn1.Value = new byte[1]; break; default: byte[] smallerInt = new byte[4 - x]; Buffer.BlockCopy(integer, x, smallerInt, 0, smallerInt.Length); asn1.Value = smallerInt; break; } return asn1; }
public ASN1 Add(ASN1 asn1) { if (asn1 != null) { if (elist == null) elist = new ArrayList(); elist.Add(asn1); } return asn1; }
// Convert a binary encoded OID to human readable string representation of // an OID (IETF style). Based on DUMPASN1.C from Peter Gutmann. public static string ToOid(ASN1 asn1) { if (asn1 == null) throw new ArgumentNullException("asn1"); byte[] aOID = asn1.Value; StringBuilder sb = new StringBuilder(); // Pick apart the OID byte x = (byte)(aOID[0] / 40); byte y = (byte)(aOID[0] % 40); if (x > 2) { // Handle special case for large y if x = 2 y += (byte)((x - 2) * 40); x = 2; } sb.Append(x.ToString(CultureInfo.InvariantCulture)); sb.Append("."); sb.Append(y.ToString(CultureInfo.InvariantCulture)); ulong val = 0; for (x = 1; x < aOID.Length; x++) { val = ((val << 7) | ((byte)(aOID[x] & 0x7F))); if (!((aOID[x] & 0x80) == 0x80)) { sb.Append("."); sb.Append(val.ToString(CultureInfo.InvariantCulture)); val = 0; } } return sb.ToString(); }
public static int ToInt32(ASN1 asn1) { if (asn1 == null) throw new ArgumentNullException("asn1"); if (asn1.Tag != 0x02) throw new FormatException("Only integer can be converted"); int x = 0; for (int i = 0; i < asn1.Value.Length; i++) x = (x << 8) + asn1.Value[i]; return x; }
public static DateTime ToDateTime(ASN1 time) { if (time == null) throw new ArgumentNullException("time"); string t = Encoding.ASCII.GetString(time.Value); // to support both UTCTime and GeneralizedTime (and not so common format) string mask = null; int year; switch (t.Length) { case 11: // illegal format, still it's supported for compatibility mask = "yyMMddHHmmZ"; break; case 13: // RFC3280: 4.1.2.5.1 UTCTime year = Convert.ToInt16(t.Substring(0, 2), CultureInfo.InvariantCulture); // Where YY is greater than or equal to 50, the // year SHALL be interpreted as 19YY; and // Where YY is less than 50, the year SHALL be // interpreted as 20YY. if (year >= 50) t = "19" + t; else t = "20" + t; mask = "yyyyMMddHHmmssZ"; break; case 15: mask = "yyyyMMddHHmmssZ"; // GeneralizedTime break; case 17: // another illegal format (990630000000+1000), again supported for compatibility year = Convert.ToInt16(t.Substring(0, 2), CultureInfo.InvariantCulture); string century = (year >= 50) ? "19" : "20"; // ASN.1 (see ITU X.680 section 43.3) deals with offset differently than .NET char sign = (t[12] == '+') ? '-' : '+'; t = String.Format("{0}{1}{2}{3}{4}:{5}{6}", century, t.Substring(0, 12), sign, t[13], t[14], t[15], t[16]); mask = "yyyyMMddHHmmsszzz"; break; } return DateTime.ParseExact(t, mask, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); }
public byte[] GetBytes() { ASN1 privateKeyAlgorithm = new ASN1(0x30); privateKeyAlgorithm.Add(ASN1Convert.FromOid(_algorithm)); privateKeyAlgorithm.Add(new ASN1(0x05)); // ASN.1 NULL ASN1 pki = new ASN1(0x30); pki.Add(new ASN1(0x02, new byte[1] { (byte)_version })); pki.Add(privateKeyAlgorithm); pki.Add(new ASN1(0x04, _key)); if (_list.Count > 0) { ASN1 attributes = new ASN1(0xA0); foreach (ASN1 attribute in _list) { attributes.Add(attribute); } pki.Add(attributes); } return pki.GetBytes(); }
// methods private void Decode(byte[] data) { ASN1 privateKeyInfo = new ASN1(data); if (privateKeyInfo.Tag != 0x30) throw new CryptographicException("invalid PrivateKeyInfo"); ASN1 version = privateKeyInfo[0]; if (version.Tag != 0x02) throw new CryptographicException("invalid version"); _version = version.Value[0]; ASN1 privateKeyAlgorithm = privateKeyInfo[1]; if (privateKeyAlgorithm.Tag != 0x30) throw new CryptographicException("invalid algorithm"); ASN1 algorithm = privateKeyAlgorithm[0]; if (algorithm.Tag != 0x06) throw new CryptographicException("missing algorithm OID"); _algorithm = ASN1Convert.ToOid(algorithm); ASN1 privateKey = privateKeyInfo[2]; _key = privateKey.Value; // attributes [0] IMPLICIT Attributes OPTIONAL if (privateKeyInfo.Count > 3) { ASN1 attributes = privateKeyInfo[3]; for (int i = 0; i < attributes.Count; i++) { _list.Add(attributes[i]); } } }
/* * RSAPrivateKey ::= SEQUENCE { * version Version, * modulus INTEGER, -- n * publicExponent INTEGER, -- e * privateExponent INTEGER, -- d * prime1 INTEGER, -- p * prime2 INTEGER, -- q * exponent1 INTEGER, -- d mod (p-1) * exponent2 INTEGER, -- d mod (q-1) * coefficient INTEGER, -- (inverse of q) mod p * otherPrimeInfos OtherPrimeInfos OPTIONAL * } */ public static byte[] Encode(RSA rsa) { RSAParameters param = rsa.ExportParameters(true); ASN1 rsaPrivateKey = new ASN1(0x30); rsaPrivateKey.Add(new ASN1(0x02, new byte[1] { 0x00 })); rsaPrivateKey.Add(ASN1Convert.FromUnsignedBigInteger(param.Modulus)); rsaPrivateKey.Add(ASN1Convert.FromUnsignedBigInteger(param.Exponent)); rsaPrivateKey.Add(ASN1Convert.FromUnsignedBigInteger(param.D)); rsaPrivateKey.Add(ASN1Convert.FromUnsignedBigInteger(param.P)); rsaPrivateKey.Add(ASN1Convert.FromUnsignedBigInteger(param.Q)); rsaPrivateKey.Add(ASN1Convert.FromUnsignedBigInteger(param.DP)); rsaPrivateKey.Add(ASN1Convert.FromUnsignedBigInteger(param.DQ)); rsaPrivateKey.Add(ASN1Convert.FromUnsignedBigInteger(param.InverseQ)); return rsaPrivateKey.GetBytes(); }
/* * RSAPrivateKey ::= SEQUENCE { * version Version, * modulus INTEGER, -- n * publicExponent INTEGER, -- e * privateExponent INTEGER, -- d * prime1 INTEGER, -- p * prime2 INTEGER, -- q * exponent1 INTEGER, -- d mod (p-1) * exponent2 INTEGER, -- d mod (q-1) * coefficient INTEGER, -- (inverse of q) mod p * otherPrimeInfos OtherPrimeInfos OPTIONAL * } */ public static RSA DecodeRSA(byte[] keypair) { ASN1 privateKey = new ASN1(keypair); if (privateKey.Tag != 0x30) throw new CryptographicException("invalid private key format"); ASN1 version = privateKey[0]; if (version.Tag != 0x02) throw new CryptographicException("missing version"); if (privateKey.Count < 9) throw new CryptographicException("not enough key parameters"); RSAParameters param = new RSAParameters(); // note: MUST remove leading 0 - else MS wont import the key param.Modulus = RemoveLeadingZero(privateKey[1].Value); int keysize = param.Modulus.Length; int keysize2 = (keysize >> 1); // half-size // size must be normalized - else MS wont import the key param.D = Normalize(privateKey[3].Value, keysize); param.DP = Normalize(privateKey[6].Value, keysize2); param.DQ = Normalize(privateKey[7].Value, keysize2); param.Exponent = RemoveLeadingZero(privateKey[2].Value); param.InverseQ = Normalize(privateKey[8].Value, keysize2); param.P = Normalize(privateKey[4].Value, keysize2); param.Q = Normalize(privateKey[5].Value, keysize2); RSA rsa = null; try { rsa = RSA.Create(); rsa.ImportParameters(param); } catch (CryptographicException) { #if MONOTOUCH // there's no machine-wide store available for iOS so we can drop the dependency on // CspParameters (which drops other things, like XML key persistance, unless used elsewhere) throw; #else // this may cause problem when this code is run under // the SYSTEM identity on Windows (e.g. ASP.NET). See // http://bugzilla.ximian.com/show_bug.cgi?id=77559 CspParameters csp = new CspParameters(); csp.Flags = CspProviderFlags.UseMachineKeyStore; rsa = new RSACryptoServiceProvider(csp); rsa.ImportParameters(param); #endif } return rsa; }
// DSA only encode it's X private key inside an ASN.1 INTEGER (Hint: Tag == 0x02) // which isn't enough for rebuilding the keypair. The other parameters // can be found (98% of the time) in the X.509 certificate associated // with the private key or (2% of the time) the parameters are in it's // issuer X.509 certificate (not supported in the .NET framework). public static DSA DecodeDSA(byte[] privateKey, DSAParameters dsaParameters) { ASN1 pvk = new ASN1(privateKey); if (pvk.Tag != 0x02) throw new CryptographicException("invalid private key format"); // X is ALWAYS 20 bytes (no matter if the key length is 512 or 1024 bits) dsaParameters.X = Normalize(pvk.Value, 20); DSA dsa = DSA.Create(); dsa.ImportParameters(dsaParameters); return dsa; }
// methods private void Decode(byte[] data) { ASN1 encryptedPrivateKeyInfo = new ASN1(data); if (encryptedPrivateKeyInfo.Tag != 0x30) throw new CryptographicException("invalid EncryptedPrivateKeyInfo"); ASN1 encryptionAlgorithm = encryptedPrivateKeyInfo[0]; if (encryptionAlgorithm.Tag != 0x30) throw new CryptographicException("invalid encryptionAlgorithm"); ASN1 algorithm = encryptionAlgorithm[0]; if (algorithm.Tag != 0x06) throw new CryptographicException("invalid algorithm"); _algorithm = ASN1Convert.ToOid(algorithm); // parameters ANY DEFINED BY algorithm OPTIONAL if (encryptionAlgorithm.Count > 1) { ASN1 parameters = encryptionAlgorithm[1]; if (parameters.Tag != 0x30) throw new CryptographicException("invalid parameters"); ASN1 salt = parameters[0]; if (salt.Tag != 0x04) throw new CryptographicException("invalid salt"); _salt = salt.Value; ASN1 iterationCount = parameters[1]; if (iterationCount.Tag != 0x02) throw new CryptographicException("invalid iterationCount"); _iterations = ASN1Convert.ToInt32(iterationCount); } ASN1 encryptedData = encryptedPrivateKeyInfo[1]; if (encryptedData.Tag != 0x04) throw new CryptographicException("invalid EncryptedData"); _data = encryptedData.Value; }
// Note: PKCS#8 doesn't define how to generate the key required for encryption // so you're on your own. Just don't try to copy the big guys too much ;) // Netscape: http://www.cs.auckland.ac.nz/~pgut001/pubs/netscape.txt // Microsoft: http://www.cs.auckland.ac.nz/~pgut001/pubs/breakms.txt public byte[] GetBytes() { if (_algorithm == null) throw new CryptographicException("No algorithm OID specified"); ASN1 encryptionAlgorithm = new ASN1(0x30); encryptionAlgorithm.Add(ASN1Convert.FromOid(_algorithm)); // parameters ANY DEFINED BY algorithm OPTIONAL if ((_iterations > 0) || (_salt != null)) { ASN1 salt = new ASN1(0x04, _salt); ASN1 iterations = ASN1Convert.FromInt32(_iterations); ASN1 parameters = new ASN1(0x30); parameters.Add(salt); parameters.Add(iterations); encryptionAlgorithm.Add(parameters); } // encapsulates EncryptedData into an OCTET STRING ASN1 encryptedData = new ASN1(0x04, _data); ASN1 encryptedPrivateKeyInfo = new ASN1(0x30); encryptedPrivateKeyInfo.Add(encryptionAlgorithm); encryptedPrivateKeyInfo.Add(encryptedData); return encryptedPrivateKeyInfo.GetBytes(); }