lib_unity_purchase/Runtime/Security/Asn1Processor/Asn1Util.cs

781 lines
27 KiB
C#
Raw Permalink Blame History

//+-------------------------------------------------------------------------------+
//| 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 <20>AS IS<49> 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
{
/// <summary>
/// Utility functions.
/// </summary>
internal class Asn1Util
{
/// <summary>
/// Check if the string is ASN.1 encoded hex string.
/// </summary>
/// <param name="dataStr">The string.</param>
/// <returns>true:Yes, false:No.</returns>
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;
}
/// <summary>
/// Format a string to have certain line length and character group length.
/// Sample result FormatString(xstr,32,2):
/// <code>07 AE 0B E7 84 5A D4 6C 6A BD DF 8F 89 88 9E F1</code>
/// </summary>
/// <param name="inStr">source string.</param>
/// <param name="lineLen">line length.</param>
/// <param name="groupLen">group length.</param>
/// <returns></returns>
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;
}
/// <summary>
/// Generate a string by duplicating <see cref="char"/> xch.
/// </summary>
/// <param name="len">duplicate times.</param>
/// <param name="xch">the duplicated character.</param>
/// <returns></returns>
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);
}
/// <summary>
/// Convert byte array to a <see cref="long"/> integer.
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static long BytesToLong(byte[] bytes)
{
long tempInt = 0;
for (int i = 0; i < bytes.Length; i++)
{
tempInt = tempInt << 8 | bytes[i];
}
return tempInt;
}
/// <summary>
/// Convert a ASCII byte array to string, also filter out the null characters.
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Convert ASCII string to byte array.
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Compare source and target byte array.
/// </summary>
/// <param name="source"></param>
/// <param name="target"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Constant hex digits array.
/// </summary>
static char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
/// <summary>
/// Convert a byte array to hex string.
/// </summary>
/// <param name="bytes">source array.</param>
/// <returns>hex string.</returns>
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);
}
/// <summary>
/// Check if the character is a valid hex digits.
/// </summary>
/// <param name="ch">source character.</param>
/// <returns>true:Valid, false:Invalid.</returns>
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;
}
/// <summary>
/// Get hex digits value.
/// </summary>
/// <param name="ch">source character.</param>
/// <returns>hex digits value.</returns>
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;
}
/// <summary>
/// Convert hex string to byte array.
/// </summary>
/// <param name="hexStr">Source hex string.</param>
/// <returns>return byte array.</returns>
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;
}
/// <summary>
/// Check if the source string is a valid hex string.
/// </summary>
/// <param name="hexStr">source string.</param>
/// <returns>true:Valid, false:Invalid.</returns>
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";
/// <summary>
/// Check if the source string is PEM formated string.
/// </summary>
/// <param name="pemStr">source string.</param>
/// <returns>true:Valid, false:Invalid.</returns>
public static bool IsPemFormated(string pemStr)
{
byte[] data = null;
try
{
data = PemToBytes(pemStr);
}
catch
{
return false;
}
return (data.Length > 0);
}
/// <summary>
/// Check if a file is PEM formated.
/// </summary>
/// <param name="fileName">source file name.</param>
/// <returns>true:Yes, false:No.</returns>
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;
}
/// <summary>
/// Convert PEM formated string into <see cref="Stream"/> and set the Stream position to 0.
/// </summary>
/// <param name="pemStr">source string.</param>
/// <returns>output stream.</returns>
public static Stream PemToStream(string pemStr)
{
byte[] bytes = PemToBytes(pemStr);
MemoryStream retval = new MemoryStream(bytes);
retval.Position = 0;
return retval;
}
/// <summary>
/// Convert PEM formated string into byte array.
/// </summary>
/// <param name="pemStr">source string.</param>
/// <returns>output byte array.</returns>
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;
}
/// <summary>
/// Convert byte array to PEM formated string.
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static string BytesToPem(byte[] data)
{
return BytesToPem(data, "");
}
/// <summary>
/// Retrieve PEM file heading.
/// </summary>
/// <param name="fileName">source file name.</param>
/// <returns>heading string.</returns>
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 "";
}
}
/// <summary>
/// Retrieve PEM heading from a PEM formated string.
/// </summary>
/// <param name="pemStr">source string.</param>
/// <returns>heading string.</returns>
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 "";
}
/// <summary>
/// Convert byte array to PEM formated string and set the heading as pemHeader.
/// </summary>
/// <param name="data">source array.</param>
/// <param name="pemHeader">PEM heading.</param>
/// <returns>PEM formated string.</returns>
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;
}
/// <summary>
/// Calculate how many bits is enough to hold ivalue.
/// </summary>
/// <param name="ivalue">source value.</param>
/// <returns>bits number.</returns>
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;
}
/// <summary>
/// Calculate how many bytes is enough to hold the value.
/// </summary>
/// <param name="value">input value.</param>
/// <returns>bytes number.</returns>
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;
}
/// <summary>
/// ASN.1 DER length encoder.
/// </summary>
/// <param name="xdata">result output stream.</param>
/// <param name="length">source length.</param>
/// <returns>result bytes.</returns>
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;
}
/// <summary>
/// ASN.1 DER length decoder.
/// </summary>
/// <param name="bt">Source stream.</param>
/// <param name="isIndefiniteLength">Output parameter.</param>
/// <returns>Output length.</returns>
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;
}
/// <summary>
/// Decode tag value to return tag name.
/// </summary>
/// <param name="tag">input tag.</param>
/// <returns>tag name.</returns>
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
/// <summary>
/// Read registry information from local machine entrys.
/// </summary>
/// <param name="path"></param>
/// <param name="name"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Write information into local machine registry entry.
/// </summary>
/// <param name="path"></param>
/// <param name="name"></param>
/// <param name="data"></param>
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
/// <summary>
/// Constructor.
/// </summary>
private Asn1Util()
{
//Private constructor.
}
}
}