DotNet加密方式解析--非对称加密
新年新气象,也希望新年可以挣大钱。不管今年年底会不会跟去年一样,满怀抱负却又壮志未酬。(不过没事,我已为各位卜上一卦,卦象显示各位都能挣钱...)。已经上班两天了,公司大部分人还在休假,而我早已上班,估计今年我就是加班狗的命。(不说了,要坚强...)
以上扯淡已毕,下面言归正传。
这次的.NET加密解析系列中,前面已经讲解了散列加密、对称加密、数字签名三种加密方式,在这篇博文种,将会主要讲解非对称加密的原理,以及非对称加密在.NET种的应用。
一.非对称加密概述:
前面讲解过对称加密,对称加密中加密和解密的密钥是相同的,但是正因为如此,这会给协商过程带来潜在的危险。所以产生了非对称加密方式。
1.非对称加密原理概述:
非对称加密算法需要两个密钥,分别是公钥和私钥。公钥和私钥是一对,如果公钥对数据进行加密,只有使用私钥才可以进行解密,反之亦然。对于非对称加密的原理有如下图:
以上是大致说明了消息利用非对称加密和解密的方式,解析来我们再来看一下如果生成密钥对。非对称加密算法包含一个“密钥生成”协议,用户可以使用该协议生成密钥对。有如下图:
在非对称加密算法中,使用两个有关的函数,一个是加密函数,使用一个公钥加密消息,加密函数只能加密数据;一个时解密函数,使用一个私钥来解密被响应公钥加密的消息。
2.非对称加密特点概述:
非对称加密算法中,采用加密函数和解密函数,加密函数只能加密函数,解密函数只能解密函数。加密函数的单向性意味着一个发送者创建的消息不能被另一个发送者阅读。非对称加密相对于对称加密来说,非对称加密的速度非常慢,而且不适用于加密大量数据,公钥加密(非对称加密)是用来为对称加密算法解决密钥协商的问题而产生的。RSA算法中指定密钥长度为最小的位数,这些位的个数使用二进制数表示密钥系数N的值。
3.非对称加密算法分类概述:
对于非对称加密算法的种类,有如下图:
RSA算法:此算法是基于数论的非对称密码*,采用分组加密方式。安全性是基于大整数因子分解的困难性,RSA算法是第一个既能用于数据加密也能用与数字签名的算法。
DSA算法(数字签名算法):次算法是基于证书有限域离散对数难题。
ECC算法(椭圆曲线密码*):椭圆曲线指的是由维尔斯特拉斯方程所确定的平面曲线。
Diffie-Hellman算法:该算法本身限于密钥交换的用途,目的在于使得两个用户安全地交换一个秘密密钥以便用与以后的报文加密。该算法依赖于计算离散对数的难度。
以上是简单介绍了一些算法,没有更加深入的介绍其算法原理,由于涉及的知识面比较广,分析起来比较的繁琐,在这里就不做讲解,如果有兴趣可以自行学习和了解。
二.DotNet非对称加密核心对象解析:
上面简单叙述了非对称加密的原理,在这里主要介绍非对称加密算法在.NET种的应用,以及实现该算法所创建的对象。在这里主要介绍RSA算法的核心对象。
1.RSA加密和解密的方式:
2.DotNet种RSA算法核心对象概述:
在.NET种对于非对称加密算法的结构体系有如下图:
3.AsymmetricAlgorithm类解析:
(1).Create():创建用于执行非对称算法的默认加密对象。
public static AsymmetricAlgorithm Create() { return AsymmetricAlgorithm.Create("System.Security.Cryptography.AsymmetricAlgorithm"); }
该方法返回新的 RSACryptoServiceProvider 实例,除非已使用 <cryptoClass> 元素更改默认设置。
public static AsymmetricAlgorithm Create(string algName) { return (AsymmetricAlgorithm) CryptoConfig.CreateFromName(algName); }
该方法返回所指定的非对称算法实现的新实例。接收参数为要使用的非对称算法实现。CryptoConfig.CreateFromName()该方法在前面的加密方式中已经做过解析,这里就不做介绍了。
(2).KeySize:获取或设置非对称算法所用密钥模块的大小(以位为单位)。
public virtual int KeySize { get { return this.KeySizeValue; } set { for (int index = 0; index < this.LegalKeySizesValue.Length; ++index) { if (this.LegalKeySizesValue[index].SkipSize == 0) { if (this.LegalKeySizesValue[index].MinSize == value) { this.KeySizeValue = value; return; } } else { int minSize = this.LegalKeySizesValue[index].MinSize; while (minSize <= this.LegalKeySizesValue[index].MaxSize) { if (minSize == value) { this.KeySizeValue = value; return; } minSize += this.LegalKeySizesValue[index].SkipSize; } } } throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidKeySize")); } }
由以上代码可以发现,该属性具有get和set两个构造器,说明该属性是可读可写的。该属性返回非对称算法所用密钥模块的大小(以位为单位)。
4.RSA类解析:
(1).FromXmlString():通过 XML 字符串中的密钥信息初始化Cryptography.RSA对象。
public override void FromXmlString(string xmlString) { if (xmlString == null) throw new ArgumentNullException("xmlString"); RSAParameters parameters = new RSAParameters(); SecurityElement topElement = new Parser(xmlString).GetTopElement(); string inputBuffer1 = topElement.SearchForTextOfLocalName("Modulus"); if (inputBuffer1 == null) { string key = "Cryptography_InvalidFromXmlString"; object[] objArray = new object[2]; int index1 = 0; string str1 = "RSA"; objArray[index1] = (object) str1; int index2 = 1; string str2 = "Modulus"; objArray[index2] = (object) str2; throw new CryptographicException(Environment.GetResourceString(key, objArray)); } parameters.Modulus = Convert.FromBase64String(Utils.DiscardWhiteSpaces(inputBuffer1)); string inputBuffer2 = topElement.SearchForTextOfLocalName("Exponent"); if (inputBuffer2 == null) { string key = "Cryptography_InvalidFromXmlString"; object[] objArray = new object[2]; int index1 = 0; string str1 = "RSA"; objArray[index1] = (object) str1; int index2 = 1; string str2 = "Exponent"; objArray[index2] = (object) str2; throw new CryptographicException(Environment.GetResourceString(key, objArray)); } parameters.Exponent = Convert.FromBase64String(Utils.DiscardWhiteSpaces(inputBuffer2)); string inputBuffer3 = topElement.SearchForTextOfLocalName("P"); if (inputBuffer3 != null) parameters.P = Convert.FromBase64String(Utils.DiscardWhiteSpaces(inputBuffer3)); string inputBuffer4 = topElement.SearchForTextOfLocalName("Q"); if (inputBuffer4 != null) parameters.Q = Convert.FromBase64String(Utils.DiscardWhiteSpaces(inputBuffer4)); string inputBuffer5 = topElement.SearchForTextOfLocalName("DP"); if (inputBuffer5 != null) parameters.DP = Convert.FromBase64String(Utils.DiscardWhiteSpaces(inputBuffer5)); string inputBuffer6 = topElement.SearchForTextOfLocalName("DQ"); if (inputBuffer6 != null) parameters.DQ = Convert.FromBase64String(Utils.DiscardWhiteSpaces(inputBuffer6)); string inputBuffer7 = topElement.SearchForTextOfLocalName("InverseQ"); if (inputBuffer7 != null) parameters.InverseQ = Convert.FromBase64String(Utils.DiscardWhiteSpaces(inputBuffer7)); string inputBuffer8 = topElement.SearchForTextOfLocalName("D"); if (inputBuffer8 != null) parameters.D = Convert.FromBase64String(Utils.DiscardWhiteSpaces(inputBuffer8)); this.ImportParameters(parameters); }
该方法是继承自AsymmetricAlgorithm类,在RSA类种被重写,该方法接收参数包含 RSA 密钥信息的 XML 字符串。SecurityElement类表示用于编码安全对象的XML对象模型。
(2).ToXmlString():创建并返回包含当前 RSA 对象的密钥的 XML 字符串。
public override string ToXmlString(bool includePrivateParameters) { RSAParameters rsaParameters = this.ExportParameters(includePrivateParameters); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("<RSAKeyValue>"); stringBuilder.Append("<Modulus>" + Convert.ToBase64String(rsaParameters.Modulus) + "</Modulus>"); stringBuilder.Append("<Exponent>" + Convert.ToBase64String(rsaParameters.Exponent) + "</Exponent>"); if (includePrivateParameters) { stringBuilder.Append("<P>" + Convert.ToBase64String(rsaParameters.P) + "</P>"); stringBuilder.Append("<Q>" + Convert.ToBase64String(rsaParameters.Q) + "</Q>"); stringBuilder.Append("<DP>" + Convert.ToBase64String(rsaParameters.DP) + "</DP>"); stringBuilder.Append("<DQ>" + Convert.ToBase64String(rsaParameters.DQ) + "</DQ>"); stringBuilder.Append("<InverseQ>" + Convert.ToBase64String(rsaParameters.InverseQ) + "</InverseQ>"); stringBuilder.Append("<D>" + Convert.ToBase64String(rsaParameters.D) + "</D>"); } stringBuilder.Append("</RSAKeyValue>"); return stringBuilder.ToString(); }
该方法同样继承自AsymmetricAlgorithm类,该方法接收一个布尔型的参数,true 表示同时包含 RSA 公钥和私钥;false 表示仅包含公钥。该方法返回包含当前 RSA 对象的密钥的 XML 字符串。RSAParameters为一个结构,表示System.Security.Cryptography.RSA算法的标准参数。
5.RSACryptoServiceProvider类解析:
(1).Encrypt():使用 RSA算法对数据进行加密。
[SecuritySafeCritical] public byte[] Encrypt(byte[] rgb, bool fOAEP) { if (rgb == null) throw new ArgumentNullException("rgb"); this.GetKeyPair(); byte[] o = (byte[]) null; RSACryptoServiceProvider.EncryptKey(this._safeKeyHandle, rgb, rgb.Length, fOAEP, JitHelpers.GetObjectHandleOnStack<byte[]>(ref o)); return o; }
该方法接受两个参数,要加密的数据。fOAEP如果为 true,则使用 OAEP 填充(仅在运行 Microsoft Windows XP 或更高版本的计算机上可用)执行直接的 RSA 加密;否则,如果为 false,则使用 PKCS#1 1.5 版填充。该方法返回一个已加密的数据,为一个字节数组。
(2).Decrypt():使用 RSA算法对数据进行解密。
[SecuritySafeCritical] public byte[] Decrypt(byte[] rgb, bool fOAEP) { if (rgb == null) throw new ArgumentNullException("rgb"); this.GetKeyPair(); if (rgb.Length > this.KeySize / 8) { string key = "Cryptography_Padding_DecDataTooBig"; object[] objArray = new object[1]; int index = 0; // ISSUE: variable of a boxed type __Boxed<int> local = (ValueType) (this.KeySize / 8); objArray[index] = (object) local; throw new CryptographicException(Environment.GetResourceString(key, objArray)); } if (!this.CspKeyContainerInfo.RandomlyGenerated && !CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) { KeyContainerPermission containerPermission = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags); KeyContainerPermissionAccessEntry accessEntry = new KeyContainerPermissionAccessEntry(this._parameters, KeyContainerPermissionFlags.Decrypt); containerPermission.AccessEntries.Add(accessEntry); containerPermission.Demand(); } byte[] o = (byte[]) null; RSACryptoServiceProvider.DecryptKey(this._safeKeyHandle, rgb, rgb.Length, fOAEP, JitHelpers.GetObjectHandleOnStack<byte[]>(ref o)); return o; }
该方法接受两个参数,rgb要解密的数据。fOAEP如果为 true,则使用 OAEP 填充(仅在运行 Microsoft Windows XP 或更高版本的计算机上可用)执行直接的 <see cref="T:System.Security.Cryptography.RSA"/> 解密;否则,如果为 false,则使用 PKCS#1 1.5 版填充。该方法返回 已解密的数据,它是加密前的原始纯文本。
三.应用实例:
1.RsaHelper类:
using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; namespace BasicMmethodExtensionClass.EncryptHelper { /// <summary> /// 非对称RSA加密类 /// 需要BigInteger类来辅助 /// </summary> public static class RsaHelper { /// <summary> /// RSA的容器 可以解密的源字符串长度为 DWKEYSIZE/8-11 /// </summary> public const int Dwkeysize = 1024; /// <summary> /// RSA加密的密匙结构 公钥和私匙 /// </summary> public struct RsaKey { public string PublicKey { get; set; } public string PrivateKey { get; set; } } /// <summary> /// 得到RSA的解谜的密匙对 /// </summary> /// <returns></returns> public static RsaKey GetRasKey() { RSACryptoServiceProvider.UseMachineKeyStore = true; //声明一个指定大小的RSA容器 RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(Dwkeysize); //取得RSA容易里的各种参数 RSAParameters p = rsaProvider.ExportParameters(true); return new RsaKey { PublicKey = ComponentKey(p.Exponent, p.Modulus), PrivateKey = ComponentKey(p.D, p.Modulus) }; } /// <summary> /// 检查明文的有效性 DWKEYSIZE/8-11 长度之内为有效 中英文都算一个字符 /// </summary> /// <param name="source"></param> /// <returns></returns> public static bool CheckSourceValidate(string source) { return (Dwkeysize / 8 - 11) >= source.Length; } /// <summary> /// 组合成密匙字符串 /// </summary> /// <param name="b1"></param> /// <param name="b2"></param> /// <returns></returns> private static string ComponentKey(byte[] b1, byte[] b2) { var list = new List<byte> { (byte) b1.Length }; list.AddRange(b1); list.AddRange(b2); var b = list.ToArray<byte>(); return Convert.ToBase64String(b); } /// <summary> /// 解析密匙 /// </summary> /// <param name="key">密匙</param> /// <param name="b1">RSA的相应参数1</param> /// <param name="b2">RSA的相应参数2</param> private static void ResolveKey(string key, out byte[] b1, out byte[] b2) { //从base64字符串 解析成原来的字节数组 byte[] b = Convert.FromBase64String(key); //初始化参数的数组长度 b1 = new byte[b[0]]; b2 = new byte[b.Length - b[0] - 1]; //将相应位置是值放进相应的数组 for (int n = 1, i = 0, j = 0; n < b.Length; n++) { if (n <= b[0]) { b1[i++] = b[n]; } else { b2[j++] = b[n]; } } } /// <summary> /// 字符串加密 /// </summary> /// <param name="source">源字符串 明文</param> /// <param name="key">密匙</param> /// <returns>加密遇到错误将会返回原字符串</returns> public static string EncryptString(string source, string key) { string encryptString; try { if (!CheckSourceValidate(source)) { throw new Exception("明文太长"); } //解析这个密钥 byte[] d; byte[] n; ResolveKey(key, out d, out n); var biN = new BigInteger(n); var biD = new BigInteger(d); encryptString = EncryptString(source, biD, biN); } catch { encryptString = source; } return encryptString; } /// <summary> /// 字符串解密 /// </summary> /// <param name="encryptString">密文</param> /// <param name="key">密钥</param> /// <returns>遇到解密失败将会返回原字符串</returns> public static string DecryptString(string encryptString, string key) { string source; try { //解析这个密钥 byte[] e; byte[] n; ResolveKey(key, out e, out n); var biE = new BigInteger(e); var biN = new BigInteger(n); source = DecryptString(encryptString, biE, biN); } catch { source = encryptString; } return source; } /// <summary> /// 用指定的密匙加密 /// </summary> /// <param name="source">明文</param> /// <param name="d">可以是RSACryptoServiceProvider生成的D</param> /// <param name="n">可以是RSACryptoServiceProvider生成的Modulus</param> /// <returns>返回密文</returns> private static string EncryptString(string source, BigInteger d, BigInteger n) { var len = source.Length; int len1; if ((len % 128) == 0) len1 = len / 128; else len1 = len / 128 + 1; var result = new StringBuilder(); for (var i = 0; i < len1; i++) { var blockLen = len >= 128 ? 128 : len; var block = source.Substring(i * 128, blockLen); byte[] oText = Encoding.UTF8.GetBytes(block); var biText = new BigInteger(oText); var biEnText = biText.modPow(d, n); var temp = biEnText.ToHexString(); result.Append(temp).Append("@"); len -= blockLen; } return result.ToString().TrimEnd('@'); } /// <summary> /// 用指定的密匙加密 /// </summary> /// <param name="encryptString"></param> /// <param name="e">可以是RSACryptoServiceProvider生成的Exponent</param> /// <param name="n">可以是RSACryptoServiceProvider生成的Modulus</param> /// <returns>返回明文</returns> private static string DecryptString(string encryptString, BigInteger e, BigInteger n) { var result = new StringBuilder(); var strarr1 = encryptString.Split(new[] { '@' }, StringSplitOptions.RemoveEmptyEntries); foreach (var block in strarr1) { var biText = new BigInteger(block, 16); var biEnText = biText.modPow(e, n); var temp = Encoding.UTF8.GetString(biEnText.getBytes()); result.Append(temp); } return result.ToString(); } } }
2.BigInteger类:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace BasicMmethodExtensionClass.EncryptHelper { public class BigInteger { // maximum length of the BigInteger in uint (4 bytes) // change this to suit the required level of precision. private const int maxLength = 70; // primes smaller than 2000 to test the generated prime number public static readonly int[] primesBelow2000 = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999 }; private uint[] data; // stores bytes from the Big Integer public int DataLength; // number of actual chars used //*********************************************************************** // Constructor (Default value for BigInteger is 0 //*********************************************************************** public BigInteger() { data = new uint[maxLength]; DataLength = 1; } //*********************************************************************** // Constructor (Default value provided by long) //*********************************************************************** public BigInteger(long value) { data = new uint[maxLength]; long tempVal = value; // copy bytes from long to BigInteger without any assumption of // the length of the long datatype DataLength = 0; while (value != 0 && DataLength < maxLength) { data[DataLength] = (uint)(value & 0xFFFFFFFF); value >>= 32; DataLength++; } if (tempVal > 0) // overflow check for +ve value { if (value != 0 || (data[maxLength - 1] & 0x80000000) != 0) throw (new ArithmeticException("Positive overflow in constructor.")); } else if (tempVal < 0) // underflow check for -ve value { if (value != -1 || (data[DataLength - 1] & 0x80000000) == 0) throw (new ArithmeticException("Negative underflow in constructor.")); } if (DataLength == 0) DataLength = 1; } //*********************************************************************** // Constructor (Default value provided by ulong) //*********************************************************************** public BigInteger(ulong value) { data = new uint[maxLength]; // copy bytes from ulong to BigInteger without any assumption of // the length of the ulong datatype DataLength = 0; while (value != 0 && DataLength < maxLength) { data[DataLength] = (uint)(value & 0xFFFFFFFF); value >>= 32; DataLength++; } if (value != 0 || (data[maxLength - 1] & 0x80000000) != 0) throw (new ArithmeticException("Positive overflow in constructor.")); if (DataLength == 0) DataLength = 1; } //*********************************************************************** // Constructor (Default value provided by BigInteger) //*********************************************************************** public BigInteger(BigInteger bi) { data = new uint[maxLength]; DataLength = bi.DataLength; for (int i = 0; i < DataLength; i++) data[i] = bi.data[i]; } //*********************************************************************** // Constructor (Default value provided by a string of digits of the // specified base) // // Example (base 10) // ----------------- // To initialize "a" with the default value of 1234 in base 10 // BigInteger a = new BigInteger("1234", 10) // // To initialize "a" with the default value of -1234 // BigInteger a = new BigInteger("-1234", 10) // // Example (base 16) // ----------------- // To initialize "a" with the default value of 0x1D4F in base 16 // BigInteger a = new BigInteger("1D4F", 16) // // To initialize "a" with the default value of -0x1D4F // BigInteger a = new BigInteger("-1D4F", 16) // // Note that string values are specified in the <sign><magnitude> // format. // //*********************************************************************** public BigInteger(string value, int radix) { BigInteger multiplier = new BigInteger(1); BigInteger result = new BigInteger(); value = (value.ToUpper()).Trim(); int limit = 0; if (value[0] == '-') limit = 1; for (int i = value.Length - 1; i >= limit; i--) { int posVal = (int)value[i]; if (posVal >= '0' && posVal <= '9') posVal -= '0'; else if (posVal >= 'A' && posVal <= 'Z') posVal = (posVal - 'A') + 10; else posVal = 9999999; // arbitrary large if (posVal >= radix) throw (new ArithmeticException("Invalid string in constructor.")); else { if (value[0] == '-') posVal = -posVal; result = result + (multiplier * posVal); if ((i - 1) >= limit) multiplier = multiplier * radix; } } if (value[0] == '-') // negative values { if ((result.data[maxLength - 1] & 0x80000000) == 0) throw (new ArithmeticException("Negative underflow in constructor.")); } else // positive values { if ((result.data[maxLength - 1] & 0x80000000) != 0) throw (new ArithmeticException("Positive overflow in constructor.")); } data = new uint[maxLength]; for (int i = 0; i < result.DataLength; i++) data[i] = result.data[i]; DataLength = result.DataLength; } //*********************************************************************** // Constructor (Default value provided by an array of bytes) // // The lowest index of the input byte array (i.e [0]) should contain the // most significant byte of the number, and the highest index should // contain the least significant byte. // // E.g. // To initialize "a" with the default value of 0x1D4F in base 16 // byte[] temp = { 0x1D, 0x4F }; // BigInteger a = new BigInteger(temp) // // Note that this method of initialization does not allow the // sign to be specified. // //*********************************************************************** public BigInteger(byte[] inData) { DataLength = inData.Length >> 2; int leftOver = inData.Length & 0x3; if (leftOver != 0) // length not multiples of 4 DataLength++; if (DataLength > maxLength) throw (new ArithmeticException("Byte overflow in constructor.")); data = new uint[maxLength]; for (int i = inData.Length - 1, j = 0; i >= 3; i -= 4, j++) { data[j] = (uint)((inData[i - 3] << 24) + (inData[i - 2] << 16) + (inData[i - 1] << 8) + inData[i]); } if (leftOver == 1) data[DataLength - 1] = (uint)inData[0]; else if (leftOver == 2) data[DataLength - 1] = (uint)((inData[0] << 8) + inData[1]); else if (leftOver == 3) data[DataLength - 1] = (uint)((inData[0] << 16) + (inData[1] << 8) + inData[2]); while (DataLength > 1 && data[DataLength - 1] == 0) DataLength--; //Console.WriteLine("Len = " + dataLength); } //*********************************************************************** // Constructor (Default value provided by an array of bytes of the // specified length.) //*********************************************************************** public BigInteger(byte[] inData, int inLen) { DataLength = inLen >> 2; int leftOver = inLen & 0x3; if (leftOver != 0) // length not multiples of 4 DataLength++; if (DataLength > maxLength || inLen > inData.Length) throw (new ArithmeticException("Byte overflow in constructor.")); data = new uint[maxLength]; for (int i = inLen - 1, j = 0; i >= 3; i -= 4, j++) { data[j] = (uint)((inData[i - 3] << 24) + (inData[i - 2] << 16) + (inData[i - 1] << 8) + inData[i]); } if (leftOver == 1) data[DataLength - 1] = (uint)inData[0]; else if (leftOver == 2) data[DataLength - 1] = (uint)((inData[0] << 8) + inData[1]); else if (leftOver == 3) data[DataLength - 1] = (uint)((inData[0] << 16) + (inData[1] << 8) + inData[2]); if (DataLength == 0) DataLength = 1; while (DataLength > 1 && data[DataLength - 1] == 0) DataLength--; //Console.WriteLine("Len = " + dataLength); } //*********************************************************************** // Constructor (Default value provided by an array of unsigned integers) //********************************************************************* public BigInteger(uint[] inData) { DataLength = inData.Length; if (DataLength > maxLength) throw (new ArithmeticException("Byte overflow in constructor.")); data = new uint[maxLength]; for (int i = DataLength - 1, j = 0; i >= 0; i--, j++) data[j] = inData[i]; while (DataLength > 1 && data[DataLength - 1] == 0) DataLength--; //Console.WriteLine("Len = " + dataLength); } //*********************************************************************** // Overloading of the typecast operator. // For BigInteger bi = 10; //*********************************************************************** public static implicit operator BigInteger(long value) { return (new BigInteger(value)); } public static implicit operator BigInteger(ulong value) { return (new BigInteger(value)); } public static implicit operator BigInteger(int value) { return (new BigInteger((long)value)); } public static implicit operator BigInteger(uint value) { return (new BigInteger((ulong)value)); } //*********************************************************************** // Overloading of addition operator //*********************************************************************** public static BigInteger operator +(BigInteger bi1, BigInteger bi2) { BigInteger result = new BigInteger(); result.DataLength = (bi1.DataLength > bi2.DataLength) ? bi1.DataLength : bi2.DataLength; long carry = 0; for (int i = 0; i < result.DataLength; i++) { long sum = (long)bi1.data[i] + (long)bi2.data[i] + carry; carry = sum >> 32; result.data[i] = (uint)(sum & 0xFFFFFFFF); } if (carry != 0 && result.DataLength < maxLength) { result.data[result.DataLength] = (uint)(carry); result.DataLength++; } while (result.DataLength > 1 && result.data[result.DataLength - 1] == 0) result.DataLength--; // overflow check int lastPos = maxLength - 1; if ((bi1.data[lastPos] & 0x80000000) == (bi2.data[lastPos] & 0x80000000) && (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) { throw (new ArithmeticException()); } return result; } //*********************************************************************** // Overloading of the unary ++ operator //*********************************************************************** public static BigInteger operator ++(BigInteger bi1) { BigInteger result = new BigInteger(bi1); long val, carry = 1; int index = 0; while (carry != 0 && index < maxLength) { val = (long)(result.data[index]); val++; result.data[index] = (uint)(val & 0xFFFFFFFF); carry = val >> 32; index++; } if (index > result.DataLength) result.DataLength = index; else { while (result.DataLength > 1 && result.data[result.DataLength - 1] == 0) result.DataLength--; } // overflow check int lastPos = maxLength - 1; // overflow if initial value was +ve but ++ caused a sign // change to negative. if ((bi1.data[lastPos] & 0x80000000) == 0 && (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) { throw (new ArithmeticException("Overflow in ++.")); } return result; } //*********************************************************************** // Overloading of subtraction operator //*********************************************************************** public static BigInteger operator -(BigInteger bi1, BigInteger bi2) { BigInteger result = new BigInteger(); result.DataLength = (bi1.DataLength > bi2.DataLength) ? bi1.DataLength : bi2.DataLength; long carryIn = 0; for (int i = 0; i < result.DataLength; i++) { long diff; diff = (long)bi1.data[i] - (long)bi2.data[i] - carryIn; result.data[i] = (uint)(diff & 0xFFFFFFFF); if (diff < 0) carryIn = 1; else carryIn = 0; } // roll over to negative if (carryIn != 0) { for (int i = result.DataLength; i < maxLength; i++) result.data[i] = 0xFFFFFFFF; result.DataLength = maxLength; } // fixed in v1.03 to give correct datalength for a - (-b) while (result.DataLength > 1 && result.data[result.DataLength - 1] == 0) result.DataLength--; // overflow check int lastPos = maxLength - 1; if ((bi1.data[lastPos] & 0x80000000) != (bi2.data[lastPos] & 0x80000000) && (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) { throw (new ArithmeticException()); } return result; } //*********************************************************************** // Overloading of the unary -- operator //*********************************************************************** public static BigInteger operator --(BigInteger bi1) { BigInteger result = new BigInteger(bi1); long val; bool carryIn = true; int index = 0; while (carryIn && index < maxLength) { val = (long)(result.data[index]); val--; result.data[index] = (uint)(val & 0xFFFFFFFF); if (val >= 0) carryIn = false; index++; } if (index > result.DataLength) result.DataLength = index; while (result.DataLength > 1 && result.data[result.DataLength - 1] == 0) result.DataLength--; // overflow check int lastPos = maxLength - 1; // overflow if initial value was -ve but -- caused a sign // change to positive. if ((bi1.data[lastPos] & 0x80000000) != 0 && (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) { throw (new ArithmeticException("Underflow in --.")); } return result; } //*********************************************************************** // Overloading of multiplication operator //*********************************************************************** public static BigInteger operator *(BigInteger bi1, BigInteger bi2) { int lastPos = maxLength - 1; bool bi1Neg = false, bi2Neg = false; // take the absolute value of the inputs try { if ((bi1.data[lastPos] & 0x80000000) != 0) // bi1 negative { bi1Neg = true; bi1 = -bi1; } if ((bi2.data[lastPos] & 0x80000000) != 0) // bi2 negative { bi2Neg = true; bi2 = -bi2; } } catch (Exception) { } BigInteger result = new BigInteger(); // multiply the absolute values try { for (int i = 0; i < bi1.DataLength; i++) { if (bi1.data[i] == 0) continue; ulong mcarry = 0; for (int j = 0, k = i; j < bi2.DataLength; j++, k++) { // k = i + j ulong val = ((ulong)bi1.data[i] * (ulong)bi2.data[j]) + (ulong)result.data[k] + mcarry; result.data[k] = (uint)(val & 0xFFFFFFFF); mcarry = (val >> 32); } if (mcarry != 0) result.data[i + bi2.DataLength] = (uint)mcarry; } } catch (Exception) { throw (new ArithmeticException("Multiplication overflow.")); } result.DataLength = bi1.DataLength + bi2.DataLength; if (result.DataLength > maxLength) result.DataLength = maxLength; while (result.DataLength > 1 && result.data[result.DataLength - 1] == 0) result.DataLength--; // overflow check (result is -ve) if ((result.data[lastPos] & 0x80000000) != 0) { if (bi1Neg != bi2Neg && result.data[lastPos] == 0x80000000) // different sign { // handle the special case where multiplication produces // a max negative number in 2's complement. if (result.DataLength == 1) return result; else { bool isMaxNeg = true; for (int i = 0; i < result.DataLength - 1 && isMaxNeg; i++) { if (result.data[i] != 0) isMaxNeg = false; } if (isMaxNeg) return result; } } throw (new ArithmeticException("Multiplication overflow.")); } // if input has different signs, then result is -ve if (bi1Neg != bi2Neg) return -result; return result; } //*********************************************************************** // Overloading of unary << operators //*********************************************************************** public static BigInteger operator <<(BigInteger bi1, int shiftVal) { BigInteger result = new BigInteger(bi1); result.DataLength = shiftLeft(result.data, shiftVal); return result; } // least significant bits at lower part of buffer private static int shiftLeft(uint[] buffer, int shiftVal) { int shiftAmount = 32; int bufLen = buffer.Length; while (bufLen > 1 && buffer[bufLen - 1] == 0) bufLen--; for (int count = shiftVal; count > 0;) { if (count < shiftAmount) shiftAmount = count; //Console.WriteLine("shiftAmount = {0}", shiftAmount); ulong carry = 0; for (int i = 0; i < bufLen; i++) { ulong val = ((ulong)buffer[i]) << shiftAmount; val |= carry; buffer[i] = (uint)(val & 0xFFFFFFFF); carry = val >> 32; } if (carry != 0) { if (bufLen + 1 <= buffer.Length) { buffer[bufLen] = (uint)carry; bufLen++; } } count -= shiftAmount; } return bufLen; } //*********************************************************************** // Overloading of unary >> operators //*********************************************************************** public static BigInteger operator >>(BigInteger bi1, int shiftVal) { BigInteger result = new BigInteger(bi1); result.DataLength = shiftRight(result.data, shiftVal); if ((bi1.data[maxLength - 1] & 0x80000000) != 0) // negative { for (int i = maxLength - 1; i >= result.DataLength; i--) result.data[i] = 0xFFFFFFFF; uint mask = 0x80000000; for (int i = 0; i < 32; i++) { if ((result.data[result.DataLength - 1] & mask) != 0) break; result.data[result.DataLength - 1] |= mask; mask >>= 1; } result.DataLength = maxLength; } return result; } private static int shiftRight(uint[] buffer, int shiftVal) { int shiftAmount = 32; int invShift = 0; int bufLen = buffer.Length; while (bufLen > 1 && buffer[bufLen - 1] == 0) bufLen--; //Console.WriteLine("bufLen = " + bufLen + " buffer.Length = " + buffer.Length); for (int count = shiftVal; count > 0;) { if (count < shiftAmount) { shiftAmount = count; invShift = 32 - shiftAmount; } //Console.WriteLine("shiftAmount = {0}", shiftAmount); ulong carry = 0; for (int i = bufLen - 1; i >= 0; i--) { ulong val = ((ulong)buffer[i]) >> shiftAmount; val |= carry; carry = ((ulong)buffer[i]) << invShift; buffer[i] = (uint)(val); } count -= shiftAmount; } while (bufLen > 1 && buffer[bufLen - 1] == 0) bufLen--; return bufLen; } //*********************************************************************** // Overloading of the NOT operator (1's complement) //*********************************************************************** public static BigInteger operator ~(BigInteger bi1) { BigInteger result = new BigInteger(bi1); for (int i = 0; i < maxLength; i++) result.data[i] = (uint)(~(bi1.data[i])); result.DataLength = maxLength; while (result.DataLength > 1 && result.data[result.DataLength - 1] == 0) result.DataLength--; return result; } //*********************************************************************** // Overloading of the NEGATE operator (2's complement) //*********************************************************************** public static BigInteger operator -(BigInteger bi1) { // handle neg of zero separately since it'll cause an overflow // if we proceed. if (bi1.DataLength == 1 && bi1.data[0] == 0) return (new BigInteger()); BigInteger result = new BigInteger(bi1); // 1's complement for (int i = 0; i < maxLength; i++) result.data[i] = (uint)(~(bi1.data[i])); // add one to result of 1's complement long val, carry = 1; int index = 0; while (carry != 0 && index < maxLength) { val = (long)(result.data[index]); val++; result.data[index] = (uint)(val & 0xFFFFFFFF); carry = val >> 32; index++; } if ((bi1.data[maxLength - 1] & 0x80000000) == (result.data[maxLength - 1] & 0x80000000)) throw (new ArithmeticException("Overflow in negation.\n")); result.DataLength = maxLength; while (result.DataLength > 1 && result.data[result.DataLength - 1] == 0) result.DataLength--; return result; } //*********************************************************************** // Overloading of equality operator //*********************************************************************** public static bool operator ==(BigInteger bi1, BigInteger bi2) { return bi1.Equals(bi2); } public static bool operator !=(BigInteger bi1, BigInteger bi2) { return !(bi1.Equals(bi2)); } public override bool Equals(object o) { BigInteger bi = (BigInteger)o; if (this.DataLength != bi.DataLength) return false; for (int i = 0; i < this.DataLength; i++) { if (this.data[i] != bi.data[i]) return false; } return true; } public override int GetHashCode() { return this.ToString().GetHashCode(); } //*********************************************************************** // Overloading of inequality operator //*********************************************************************** public static bool operator >(BigInteger bi1, BigInteger bi2) { int pos = maxLength - 1; // bi1 is negative, bi2 is positive if ((bi1.data[pos] & 0x80000000) != 0 && (bi2.data[pos] & 0x80000000) == 0) return false; // bi1 is positive, bi2 is negative else if ((bi1.data[pos] & 0x80000000) == 0 && (bi2.data[pos] & 0x80000000) != 0) return true; // same sign int len = (bi1.DataLength > bi2.DataLength) ? bi1.DataLength : bi2.DataLength; for (pos = len - 1; pos >= 0 && bi1.data[pos] == bi2.data[pos]; pos--) ; if (pos >= 0) { if (bi1.data[pos] > bi2.data[pos]) return true; return false; } return false; } public static bool operator <(BigInteger bi1, BigInteger bi2) { int pos = maxLength - 1; // bi1 is negative, bi2 is positive if ((bi1.data[pos] & 0x80000000) != 0 && (bi2.data[pos] & 0x80000000) == 0) return true; // bi1 is positive, bi2 is negative else if ((bi1.data[pos] & 0x80000000) == 0 && (bi2.data[pos] & 0x80000000) != 0) return false; // same sign int len = (bi1.DataLength > bi2.DataLength) ? bi1.DataLength : bi2.DataLength; for (pos = len - 1; pos >= 0 && bi1.data[pos] == bi2.data[pos]; pos--) ; if (pos >= 0) { if (bi1.data[pos] < bi2.data[pos]) return true; return false; } return false; } public static bool operator >=(BigInteger bi1, BigInteger bi2) { return (bi1 == bi2 || bi1 > bi2); } public static bool operator <=(BigInteger bi1, BigInteger bi2) { return (bi1 == bi2 || bi1 < bi2); } //*********************************************************************** // Private function that supports the division of two numbers with // a divisor that has more than 1 digit. // // Algorithm taken from [1] //*********************************************************************** private static void multiByteDivide(BigInteger bi1, BigInteger bi2, BigInteger outQuotient, BigInteger outRemainder) { uint[] result = new uint[maxLength]; int remainderLen = bi1.DataLength + 1; uint[] remainder = new uint[remainderLen]; uint mask = 0x80000000; uint val = bi2.data[bi2.DataLength - 1]; int shift = 0, resultPos = 0; while (mask != 0 && (val & mask) == 0) { shift++; mask >>= 1; } //Console.WriteLine("shift = {0}", shift); //Console.WriteLine("Before bi1 Len = {0}, bi2 Len = {1}", bi1.dataLength, bi2.dataLength); for (int i = 0; i < bi1.DataLength; i++) remainder[i] = bi1.data[i]; shiftLeft(remainder, shift); bi2 = bi2 << shift; /* Console.WriteLine("bi1 Len = {0}, bi2 Len = {1}", bi1.dataLength, bi2.dataLength); Console.WriteLine("dividend = " + bi1 + "\ndivisor = " + bi2); for(int q = remainderLen - 1; q >= 0; q--) Console.Write("{0:x2}", remainder[q]); Console.WriteLine(); */ int j = remainderLen - bi2.DataLength; int pos = remainderLen - 1; ulong firstDivisorByte = bi2.data[bi2.DataLength - 1]; ulong secondDivisorByte = bi2.data[bi2.DataLength - 2]; int divisorLen = bi2.DataLength + 1; uint[] dividendPart = new uint[divisorLen]; while (j > 0) { ulong dividend = ((ulong)remainder[pos] << 32) + (ulong)remainder[pos - 1]; //Console.WriteLine("dividend = {0}", dividend); ulong q_hat = dividend / firstDivisorByte; ulong r_hat = dividend % firstDivisorByte; //Console.WriteLine("q_hat = {0:X}, r_hat = {1:X}", q_hat, r_hat); bool done = false; while (!done) { done = true; if (q_hat == 0x100000000 || (q_hat * secondDivisorByte) > ((r_hat << 32) + remainder[pos - 2])) { q_hat--; r_hat += firstDivisorByte; if (r_hat < 0x100000000) done = false; } } for (int h = 0; h < divisorLen; h++) dividendPart[h] = remainder[pos - h]; BigInteger kk = new BigInteger(dividendPart); BigInteger ss = bi2 * (long)q_hat; //Console.WriteLine("ss before = " + ss); while (ss > kk) { q_hat--; ss -= bi2; //Console.WriteLine(ss); } BigInteger yy = kk - ss; //Console.WriteLine("ss = " + ss); //Console.WriteLine("kk = " + kk); //Console.WriteLine("yy = " + yy); for (int h = 0; h < divisorLen; h++) remainder[pos - h] = yy.data[bi2.DataLength - h]; /* Console.WriteLine("dividend = "); for(int q = remainderLen - 1; q >= 0; q--) Console.Write("{0:x2}", remainder[q]); Console.WriteLine("\n************ q_hat = {0:X}\n", q_hat); */ result[resultPos++] = (uint)q_hat; pos--; j--; } outQuotient.DataLength = resultPos; int y = 0; for (int x = outQuotient.DataLength - 1; x >= 0; x--, y++) outQuotient.data[y] = result[x]; for (; y < maxLength; y++) outQuotient.data[y] = 0; while (outQuotient.DataLength > 1 && outQuotient.data[outQuotient.DataLength - 1] == 0) outQuotient.DataLength--; if (outQuotient.DataLength == 0) outQuotient.DataLength = 1; outRemainder.DataLength = shiftRight(remainder, shift); for (y = 0; y < outRemainder.DataLength; y++) outRemainder.data[y] = remainder[y]; for (; y < maxLength; y++) outRemainder.data[y] = 0; } //*********************************************************************** // Private function that supports the division of two numbers with // a divisor that has only 1 digit. //*********************************************************************** private static void singleByteDivide(BigInteger bi1, BigInteger bi2, BigInteger outQuotient, BigInteger outRemainder) { uint[] result = new uint[maxLength]; int resultPos = 0; // copy dividend to reminder for (int i = 0; i < maxLength; i++) outRemainder.data[i] = bi1.data[i]; outRemainder.DataLength = bi1.DataLength; while (outRemainder.DataLength > 1 && outRemainder.data[outRemainder.DataLength - 1] == 0) outRemainder.DataLength--; ulong divisor = (ulong)bi2.data[0]; int pos = outRemainder.DataLength - 1; ulong dividend = (ulong)outRemainder.data[pos]; //Console.WriteLine("divisor = " + divisor + " dividend = " + dividend); //Console.WriteLine("divisor = " + bi2 + "\ndividend = " + bi1); if (dividend >= divisor) { ulong quotient = dividend / divisor; result[resultPos++] = (uint)quotient; outRemainder.data[pos] = (uint)(dividend % divisor); } pos--; while (pos >= 0) { //Console.WriteLine(pos); dividend = ((ulong)outRemainder.data[pos + 1] << 32) + (ulong)outRemainder.data[pos]; ulong quotient = dividend / divisor; result[resultPos++] = (uint)quotient; outRemainder.data[pos + 1] = 0; outRemainder.data[pos--] = (uint)(dividend % divisor); //Console.WriteLine(">>>> " + bi1); } outQuotient.DataLength = resultPos; int j = 0; for (int i = outQuotient.DataLength - 1; i >= 0; i--, j++) outQuotient.data[j] = result[i]; for (; j < maxLength; j++) outQuotient.data[j] = 0; while (outQuotient.DataLength > 1 && outQuotient.data[outQuotient.DataLength - 1] == 0) outQuotient.DataLength--; if (outQuotien
相关文章:
-
-
using System.Xml;using System.IO;using System; namespace Framework.Common{... [阅读全文]
-
本系列将分析ASP.NET Core运行原理 "【ASP.NET Core】运行原理(1):创建WebHost" "【ASP.NET Core】运行... [阅读全文]
-
前两天在博客园看到 .NET开发一个微信跳一跳辅助程序, 原来可以通过C#连接手机操作。正好朋友圈有人分享“冲顶大会”。冲顶大会是一个在线答题APP... [阅读全文]
-
使用方式:调用API "GetSerialPort"即可获得设备列表,示意如下: 效果如图: 如果报错: managemento... [阅读全文]
-
现在比较流行C#与C++融合:C#做GUI,开发效率高,C++做运算,运行效率高,二者兼得。 但是C++与C#必然存在数据交互,C#与C++dll的... [阅读全文]
-
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
上一篇: Vue.js 源码分析(十五) 指令篇 v-bind指令详解
下一篇: 鱼胶的功效和定义是什么
发表评论