//+-------------------------------------------------------------------------------+ //| Copyright (c) 2003 Liping Dai. All rights reserved. | //| Web: www.lipingshare.com | //| Email: lipingshare@yahoo.com | //| | //| Copyright and Permission Details: | //| ================================= | //| Permission is hereby granted, free of charge, to any person obtaining a copy | //| of this software and associated documentation files (the "Software"), to deal | //| in the Software without restriction, including without limitation the rights | //| to use, copy, modify, merge, publish, distribute, and/or sell copies of the | //| Software, subject to the following conditions: | //| | //| 1. Redistributions of source code must retain the above copyright notice, this| //| list of conditions and the following disclaimer. | //| | //| 2. Redistributions in binary form must reproduce the above copyright notice, | //| this list of conditions and the following disclaimer in the documentation | //| and/or other materials provided with the distribution. | //| | //| THE SOFTWARE PRODUCT IS PROVIDED �AS IS� WITHOUT WARRANTY OF ANY KIND, | //| EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | //| WARRANTIES OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR | //| A PARTICULAR PURPOSE. | //+-------------------------------------------------------------------------------+ using System; using System.IO; using System.Text; using Microsoft.Win32; namespace LipingShare.LCLib.Asn1Processor { /// /// Utility functions. /// internal class Asn1Util { /// /// Check if the string is ASN.1 encoded hex string. /// /// The string. /// true:Yes, false:No. public static bool IsAsn1EncodedHexStr(string dataStr) { bool retval = false; try { byte[] data = HexStrToBytes(dataStr); if (data.Length > 0) { Asn1Node node = new Asn1Node(); retval = node.LoadData(data); } } catch { retval = false; } return retval; } /// /// Format a string to have certain line length and character group length. /// Sample result FormatString(xstr,32,2): /// 07 AE 0B E7 84 5A D4 6C 6A BD DF 8F 89 88 9E F1 /// /// source string. /// line length. /// group length. /// public static string FormatString(string inStr, int lineLen, int groupLen) { char[] tmpCh = new char[inStr.Length * 2]; int i, c = 0, linec = 0; int gc = 0; for (i = 0; i < inStr.Length; i++) { tmpCh[c++] = inStr[i]; gc++; linec++; if (gc >= groupLen && groupLen > 0) { tmpCh[c++] = ' '; gc = 0; } if (linec >= lineLen) { tmpCh[c++] = '\r'; tmpCh[c++] = '\n'; linec = 0; } } string retval = new string(tmpCh); retval = retval.TrimEnd('\0'); retval = retval.TrimEnd('\n'); retval = retval.TrimEnd('\r'); return retval; } /// /// Generate a string by duplicating xch. /// /// duplicate times. /// the duplicated character. /// public static string GenStr(int len, char xch) { char[] ch = new char[len]; for (int i = 0; i < len; i++) { ch[i] = xch; } return new string(ch); } /// /// Convert byte array to a integer. /// /// /// public static long BytesToLong(byte[] bytes) { long tempInt = 0; for (int i = 0; i < bytes.Length; i++) { tempInt = tempInt << 8 | bytes[i]; } return tempInt; } /// /// Convert a ASCII byte array to string, also filter out the null characters. /// /// /// public static string BytesToString(byte[] bytes) { string retval = ""; if (bytes == null || bytes.Length < 1) return retval; char[] cretval = new char[bytes.Length]; for (int i = 0, j = 0; i < bytes.Length; i++) { if (bytes[i] != '\0') { cretval[j++] = (char)bytes[i]; } } retval = new string(cretval); retval = retval.TrimEnd('\0'); return retval; } /// /// Convert ASCII string to byte array. /// /// /// public static byte[] StringToBytes(string msg) { byte[] retval = new byte[msg.Length]; for (int i = 0; i < msg.Length; i++) { retval[i] = (byte)msg[i]; } return retval; } /// /// Compare source and target byte array. /// /// /// /// public static bool IsEqual(byte[] source, byte[] target) { if (source == null) return false; if (target == null) return false; if (source.Length != target.Length) return false; for (int i = 0; i < source.Length; i++) { if (source[i] != target[i]) return false; } return true; } /// /// Constant hex digits array. /// static char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; /// /// Convert a byte array to hex string. /// /// source array. /// hex string. public static string ToHexString(byte[] bytes) { if (bytes == null) return ""; char[] chars = new char[bytes.Length * 2]; int b, i; for (i = 0; i < bytes.Length; i++) { b = bytes[i]; chars[i * 2] = hexDigits[b >> 4]; chars[i * 2 + 1] = hexDigits[b & 0xF]; } return new string(chars); } /// /// Check if the character is a valid hex digits. /// /// source character. /// true:Valid, false:Invalid. public static bool IsValidHexDigits(char ch) { bool retval = false; for (int i = 0; i < hexDigits.Length; i++) { if (hexDigits[i] == ch) { retval = true; break; } } return retval; } /// /// Get hex digits value. /// /// source character. /// hex digits value. public static byte GetHexDigitsVal(char ch) { byte retval = 0; for (int i = 0; i < hexDigits.Length; i++) { if (hexDigits[i] == ch) { retval = (byte)i; break; } } return retval; } /// /// Convert hex string to byte array. /// /// Source hex string. /// return byte array. public static byte[] HexStrToBytes(string hexStr) { hexStr = hexStr.Replace(" ", ""); hexStr = hexStr.Replace("\r", ""); hexStr = hexStr.Replace("\n", ""); hexStr = hexStr.ToUpper(); if ((hexStr.Length % 2) != 0) throw new Exception("Invalid Hex string: odd length."); int i; for (i = 0; i < hexStr.Length; i++) { if (!IsValidHexDigits(hexStr[i])) { throw new Exception("Invalid Hex string: included invalid character [" + hexStr[i] + "]"); } } int bc = hexStr.Length / 2; byte[] retval = new byte[bc]; int b1, b2, b; for (i = 0; i < bc; i++) { b1 = GetHexDigitsVal(hexStr[i * 2]); b2 = GetHexDigitsVal(hexStr[i * 2 + 1]); b = ((b1 << 4) | b2); retval[i] = (byte)b; } return retval; } /// /// Check if the source string is a valid hex string. /// /// source string. /// true:Valid, false:Invalid. public static bool IsHexStr(string hexStr) { byte[] bytes = null; try { bytes = HexStrToBytes(hexStr); } catch { return false; } if (bytes == null || bytes.Length < 0) { return false; } else { return true; } } private const string PemStartStr = "-----BEGIN"; private const string PemEndStr = "-----END"; /// /// Check if the source string is PEM formated string. /// /// source string. /// true:Valid, false:Invalid. public static bool IsPemFormated(string pemStr) { byte[] data = null; try { data = PemToBytes(pemStr); } catch { return false; } return (data.Length > 0); } /// /// Check if a file is PEM formated. /// /// source file name. /// true:Yes, false:No. public static bool IsPemFormatedFile(string fileName) { bool retval = false; try { FileStream fs = new FileStream(fileName, System.IO.FileMode.Open); byte[] data = new byte[fs.Length]; fs.Read(data, 0, data.Length); fs.Close(); string dataStr = Asn1Util.BytesToString(data); retval = IsPemFormated(dataStr); } catch { retval = false; } return retval; } /// /// Convert PEM formated string into and set the Stream position to 0. /// /// source string. /// output stream. public static Stream PemToStream(string pemStr) { byte[] bytes = PemToBytes(pemStr); MemoryStream retval = new MemoryStream(bytes); retval.Position = 0; return retval; } /// /// Convert PEM formated string into byte array. /// /// source string. /// output byte array. public static byte[] PemToBytes(string pemStr) { byte[] retval = null; string[] lines = pemStr.Split('\n'); string base64Str = ""; bool started = false, ended = false; string cline = ""; for (int i = 0; i < lines.Length; i++) { cline = lines[i].ToUpper(); if (cline == "") continue; if (cline.Length > PemStartStr.Length) { if (!started && cline.Substring(0, PemStartStr.Length) == PemStartStr) { started = true; continue; } } if (cline.Length > PemEndStr.Length) { if (cline.Substring(0, PemEndStr.Length) == PemEndStr) { ended = true; break; } } if (started) { base64Str += lines[i]; } } if (!(started && ended)) { throw new Exception("'BEGIN'/'END' line is missing."); } base64Str = base64Str.Replace("\r", ""); base64Str = base64Str.Replace("\n", ""); base64Str = base64Str.Replace("\n", " "); retval = Convert.FromBase64String(base64Str); return retval; } /// /// Convert byte array to PEM formated string. /// /// /// public static string BytesToPem(byte[] data) { return BytesToPem(data, ""); } /// /// Retrieve PEM file heading. /// /// source file name. /// heading string. public static string GetPemFileHeader(string fileName) { try { FileStream fs = new FileStream(fileName, FileMode.Open); byte[] data = new byte[fs.Length]; fs.Read(data, 0, data.Length); fs.Close(); string dataStr = Asn1Util.BytesToString(data); return GetPemHeader(dataStr); } catch { return ""; } } /// /// Retrieve PEM heading from a PEM formated string. /// /// source string. /// heading string. public static string GetPemHeader(string pemStr) { string[] lines = pemStr.Split('\n'); bool started = false; string cline = ""; for (int i = 0; i < lines.Length; i++) { cline = lines[i].ToUpper().Replace("\r", ""); if (cline == "") continue; if (cline.Length > PemStartStr.Length) { if (!started && cline.Substring(0, PemStartStr.Length) == PemStartStr) { started = true; string retstr = lines[i].Substring(PemStartStr.Length, lines[i].Length - PemStartStr.Length).Replace("-----", ""); return retstr.Replace("\r", ""); } } else { continue; } } return ""; } /// /// Convert byte array to PEM formated string and set the heading as pemHeader. /// /// source array. /// PEM heading. /// PEM formated string. public static string BytesToPem(byte[] data, string pemHeader) { if (pemHeader == null || pemHeader.Length < 1) { pemHeader = "ASN.1 Editor Generated PEM File"; } string retval = ""; if (pemHeader.Length > 0 && pemHeader[0] != ' ') { pemHeader = " " + pemHeader; } retval = Convert.ToBase64String(data); retval = FormatString(retval, 64, 0); retval = "-----BEGIN" + pemHeader + "-----\r\n" + retval + "\r\n-----END" + pemHeader + "-----\r\n"; return retval; } /// /// Calculate how many bits is enough to hold ivalue. /// /// source value. /// bits number. public static int BitPrecision(ulong ivalue) { if (ivalue == 0) return 0; int l = 0, h = 8 * 4; // 4: sizeof(ulong) while (h - l > 1) { int t = (int)(l + h) / 2; if ((ivalue >> t) != 0) l = t; else h = t; } return h; } /// /// Calculate how many bytes is enough to hold the value. /// /// input value. /// bytes number. public static int BytePrecision(ulong value) { int i; for (i = 4; i > 0; --i) // 4: sizeof(ulong) if ((value >> (i - 1) * 8) != 0) break; return i; } /// /// ASN.1 DER length encoder. /// /// result output stream. /// source length. /// result bytes. public static int DERLengthEncode(Stream xdata, ulong length) { int i = 0; if (length <= 0x7f) { xdata.WriteByte((byte)length); i++; } else { xdata.WriteByte((byte)(BytePrecision(length) | 0x80)); i++; for (int j = BytePrecision((ulong)length); j > 0; --j) { xdata.WriteByte((byte)(length >> (j - 1) * 8)); i++; } } return i; } /// /// ASN.1 DER length decoder. /// /// Source stream. /// Output parameter. /// Output length. public static long DerLengthDecode(Stream bt, ref bool isIndefiniteLength) { isIndefiniteLength = false; long length = 0; byte b; b = (byte)bt.ReadByte(); if ((b & 0x80) == 0) { length = b; } else { long lengthBytes = b & 0x7f; if (lengthBytes == 0) { isIndefiniteLength = true; return -2; // Indefinite length. } length = 0; while (lengthBytes-- > 0) { if ((length >> (8 * (4 - 1))) > 0) // 4: sizeof(long) { return -1; // Length overflow. } b = (byte)bt.ReadByte(); length = (length << 8) | b; } if (length <= 0x7f) { return -1; // Indicated false node } } return length; } /// /// Decode tag value to return tag name. /// /// input tag. /// tag name. static public string GetTagName(byte tag) { string retval = ""; if ((tag & Asn1TagClasses.CLASS_MASK) != 0) { switch (tag & Asn1TagClasses.CLASS_MASK) { case Asn1TagClasses.CONTEXT_SPECIFIC: retval += "CONTEXT SPECIFIC (" + ((int)(tag & Asn1Tag.TAG_MASK)).ToString() + ")"; break; case Asn1TagClasses.APPLICATION: retval += "APPLICATION (" + ((int)(tag & Asn1Tag.TAG_MASK)).ToString() + ")"; break; case Asn1TagClasses.PRIVATE: retval += "PRIVATE (" + ((int)(tag & Asn1Tag.TAG_MASK)).ToString() + ")"; break; case Asn1TagClasses.CONSTRUCTED: retval += "CONSTRUCTED (" + ((int)(tag & Asn1Tag.TAG_MASK)).ToString() + ")"; break; case Asn1TagClasses.UNIVERSAL: retval += "UNIVERSAL (" + ((int)(tag & Asn1Tag.TAG_MASK)).ToString() + ")"; break; } } else { switch (tag & Asn1Tag.TAG_MASK) { case Asn1Tag.BOOLEAN: retval += "BOOLEAN"; break; case Asn1Tag.INTEGER: retval += "INTEGER"; break; case Asn1Tag.BIT_STRING: retval += "BIT STRING"; break; case Asn1Tag.OCTET_STRING: retval += "OCTET STRING"; break; case Asn1Tag.TAG_NULL: retval += "NULL"; break; case Asn1Tag.OBJECT_IDENTIFIER: retval += "OBJECT IDENTIFIER"; break; case Asn1Tag.OBJECT_DESCRIPTOR: retval += "OBJECT DESCRIPTOR"; break; case Asn1Tag.RELATIVE_OID: retval += "RELATIVE-OID"; break; case Asn1Tag.EXTERNAL: retval += "EXTERNAL"; break; case Asn1Tag.REAL: retval += "REAL"; break; case Asn1Tag.ENUMERATED: retval += "ENUMERATED"; break; case Asn1Tag.UTF8_STRING: retval += "UTF8 STRING"; break; case (Asn1Tag.SEQUENCE): retval += "SEQUENCE"; break; case (Asn1Tag.SET): retval += "SET"; break; case Asn1Tag.NUMERIC_STRING: retval += "NUMERIC STRING"; break; case Asn1Tag.PRINTABLE_STRING: retval += "PRINTABLE STRING"; break; case Asn1Tag.T61_STRING: retval += "T61 STRING"; break; case Asn1Tag.VIDEOTEXT_STRING: retval += "VIDEOTEXT STRING"; break; case Asn1Tag.IA5_STRING: retval += "IA5 STRING"; break; case Asn1Tag.UTC_TIME: retval += "UTC TIME"; break; case Asn1Tag.GENERALIZED_TIME: retval += "GENERALIZED TIME"; break; case Asn1Tag.GRAPHIC_STRING: retval += "GRAPHIC STRING"; break; case Asn1Tag.VISIBLE_STRING: retval += "VISIBLE STRING"; break; case Asn1Tag.GENERAL_STRING: retval += "GENERAL STRING"; break; case Asn1Tag.UNIVERSAL_STRING: retval += "UNIVERSAL STRING"; break; case Asn1Tag.BMPSTRING: retval += "BMP STRING"; break; default: retval += "UNKNOWN TAG"; break; }; } return retval; } #if UNITYIAP_DISABLED /// /// Read registry information from local machine entrys. /// /// /// /// static public object ReadRegInfo(string path, string name) { object retval = null; Microsoft.Win32.RegistryKey regKey; regKey = Registry.LocalMachine.OpenSubKey(path, false); if (regKey!=null) { retval = regKey.GetValue(name); } return retval; } /// /// Write information into local machine registry entry. /// /// /// /// static public void WriteRegInfo(string path, string name, object data) { Microsoft.Win32.RegistryKey regKey; regKey = Registry.LocalMachine.OpenSubKey(path, true); if (regKey == null) { regKey = Registry.LocalMachine.CreateSubKey(path); } if (regKey != null) { regKey.SetValue(name, data); } } #endif /// /// Constructor. /// private Asn1Util() { //Private constructor. } } }