/// <summary> /// Перегрузка оператора инкримента /// </summary> public static BigInt32 operator ++(BigInt32 bi1) { BigInt32 result = new BigInt32(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--; } } return(result); }
/// <summary> /// Выполняет модульное деление числа, возведенного в степень exp. /// </summary> /// <param name="exp">экспонента</param> /// <param name="n">модуль</param> /// <returns></returns> public BigInt32 modPow(BigInt32 exp, BigInt32 n) { BigInt32 resultRemainder = 1; // конечный остаток BigInt32 tempRemainder = this % n; // временный остаток BigInt32 constant = new BigInt32(); //константа "мю" для алгоритма баррета int i = n.dataLength << 1; constant.data[i] = 0x00000001; constant.dataLength = i + 1; constant = constant / n; for (int pos = 0; pos < exp.dataLength; pos++) { uint mask = 0x00000001; for (int index = 0; index < 32; index++) { if ((exp.data[pos] & mask) != 0) { resultRemainder = AlgorithmOfBarrettReduction(resultRemainder * tempRemainder, n, constant); } mask <<= 1; tempRemainder = AlgorithmOfBarrettReduction(tempRemainder * tempRemainder, n, constant); } } return(resultRemainder); }
/// <summary> /// Перегрузка оператора декримента /// </summary> public static BigInt32 operator --(BigInt32 bi1) { BigInt32 result = new BigInt32(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--; } return(result); }
/// <summary> /// Перегрузка оператора сдвига вправо >> /// </summary> /// <param name="bi1"></param> /// <param name="shiftVal"></param> public static BigInt32 operator >>(BigInt32 bi1, int shiftVal) { BigInt32 result = new BigInt32(bi1); result.dataLength = shiftRight(result.data, shiftVal); if ((bi1.data[maxLength - 1] & 0x90000000) != 0) // если отрицательное { for (int i = maxLength - 1; i >= result.dataLength; i--) { result.data[i] = 0xFFFFFFFF; } uint mask = 0x90000000; 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); }
/// <summary> /// Вырабатывает ЭЦП сообщения-Text /// </summary> /// <param name="Text">Сообщение</param> /// <returns>Подпись</returns> public static BigInt32 CreateSignature(byte[] Text, BigInt32 d, BigInt32 n) { byte[] hash = MD5hash(Text); BigInt32 BI_Text = new BigInt32(hash); return(BI_Text.modPow(d, n)); }
/// <summary> /// Создание ЭЦП файла /// </summary> /// <param name="filename">Имя файла, который мы хотим подписать</param> static void CreateFilesOfSign(string filename) { Encoding enc = Encoding.Default; byte[] test_byte = File.ReadAllBytes(filename); // байтовое представление исходного файла FileStream Stream = new FileStream("SecretKey.txt", FileMode.Open, FileAccess.Read); StreamReader Reader = new StreamReader(Stream); string D = Reader.ReadLine(); string N = Reader.ReadLine(); Reader.Close(); Stream.Close(); BigInt32 d = new BigInt32(D); BigInt32 n = new BigInt32(N); BigInt32 Sign = RSA.CreateSignature(test_byte, d, n); // ЭЦП файла string FileNameIsSignature = "Sign_" + filename; Stream = new FileStream(FileNameIsSignature, FileMode.Create, FileAccess.Write); StreamWriter Writer = new StreamWriter(Stream); Writer.WriteLine(Sign); Writer.WriteLine(filename); Writer.Close(); Stream.Close(); Stream = new FileStream(FileNameIsSignature, FileMode.Append, FileAccess.Write); BinaryWriter WriterByte = new BinaryWriter(Stream); WriterByte.Write(test_byte); WriterByte.Close(); Console.WriteLine("Cоздан файл: {0}", FileNameIsSignature); }
/// <summary> /// Расширенный алгоритм Евклида (для нахождения обратного элемента по некоторому модулю) /// </summary> private static void Evklid(BigInt32 a, BigInt32 b, ref BigInt32 x, ref BigInt32 y, ref BigInt32 d) { BigInt32 q = new BigInt32(); BigInt32 r = new BigInt32(); BigInt32 x1 = new BigInt32(); BigInt32 x2 = new BigInt32(); BigInt32 y1 = new BigInt32(); BigInt32 y2 = new BigInt32(); BigInt32 one = new BigInt32(1); BigInt32 O = new BigInt32(0); if (b == O) { d = a; x = new BigInt32(1); y = new BigInt32(0); return; } x2 = new BigInt32(1); x1 = new BigInt32(0); y2 = new BigInt32(0); y1 = new BigInt32(1); while (b > O) { q = a / b; r = a % b; x = x2 - (q * x1); if (q == O || y1 == O) { y = y2; } else { y = y2 - (q * y1); } a = b; b = r; x2 = (x1); x1 = (x); y2 = (y1); y1 = (y); } d = (a); x = (x2); y = (y2); }
/// <summary> /// Перегрузка оператора сложения /// </summary> public static BigInt32 operator +(BigInt32 bi1, BigInt32 bi2) { BigInt32 result = new BigInt32(); 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--; } return(result); }
/// <summary> /// Перегрузка оператора сдвига влево << /// </summary> /// <param name="bi1"> Папаметр значения </param> /// <param name="shiftVal"> Величина сдвига</param> public static BigInt32 operator <<(BigInt32 bi1, int shiftVal) { BigInt32 result = new BigInt32(bi1); result.dataLength = shiftLeft(result.data, shiftVal); return(result); }
/// <summary> /// Возвращает минимальный BigInt32 /// </summary> public BigInt32 min(BigInt32 bi) { if (this < bi) { return(new BigInt32(this)); } else { return(new BigInt32(bi)); } }
/// <summary> /// Возвращает максимальный BigInt32 /// </summary> public BigInt32 max(BigInt32 bi) { if (this > bi) { return(new BigInt32(this)); } else { return(new BigInt32(bi)); } }
/// <summary> /// Конструктор копирования /// </summary> public BigInt32(BigInt32 bi) { data = new uint[maxLength]; dataLength = bi.dataLength; for (int i = 0; i < dataLength; i++) { data[i] = bi.data[i]; } }
/// <summary> /// Проверка цифровой подписи файла на подлинность /// </summary> /// <param name="filename"> Имя подписанного файла</param> static void VerificationSignature(string filename) { FileStream Stream = new FileStream(filename, FileMode.Open, FileAccess.Read); StreamReader Reader = new StreamReader(Stream); string Sign = Reader.ReadLine(); //подпись string Test_file = Reader.ReadLine(); //имя проверяемого файла Reader.Close(); Stream.Close(); Stream = new FileStream(filename, FileMode.Open, FileAccess.Read); BinaryReader ReaderBinary = new BinaryReader(Stream); FileInfo f = new FileInfo(filename); int fil = (int)f.Length - (Sign.Length + Test_file.Length); byte[] s1 = ReaderBinary.ReadBytes(Sign.Length + 2); // byte[] s2 = ReaderBinary.ReadBytes(Test_file.Length + 2); // byte[] test_byte = ReaderBinary.ReadBytes(fil); // байтовое представление проверяемых данные Stream = new FileStream("OpenKey.txt", FileMode.Open, FileAccess.Read); Reader = new StreamReader(Stream); string E = Reader.ReadLine(); string N = Reader.ReadLine(); Reader.Close(); Stream.Close(); Encoding enc = Encoding.Default; BigInt32 Signature = new BigInt32(Sign); //подпись BigInt32 e = new BigInt32(E); //открытый ключ BigInt32 n = new BigInt32(N); //модуль bool finish = false; finish = RSA.VerifySignature(test_byte, Signature, e, n); // проверка ЭЦП if (finish) { Console.WriteLine("Подпись верна."); string str_new = "New_" + Test_file; Stream = new FileStream(str_new, FileMode.Create, FileAccess.Write); BinaryWriter Writer = new BinaryWriter(Stream); Writer.Write(test_byte); Writer.Close(); Stream.Close(); Console.WriteLine("Создан файл: {0}", str_new); } else { Console.WriteLine("Подпись не верна"); } }
/// <summary> /// Проверка цифровой подписи Signature текста Text /// </summary> /// <param name="Text">Текст</param> /// <param name="Signature">Подпись</param> /// <param name="e">Открытый ключ</param> /// <param name="n">Модуль</param> /// <returns>true если подпись верна, false в обратном случае</returns> public static bool VerifySignature(byte[] Text, BigInt32 Signature, BigInt32 e, BigInt32 n) { BigInt32 R = new BigInt32(MD5hash(Text)); //Console.WriteLine(R); BigInt32 S = new BigInt32(Signature.modPow(e, n)); //Console.WriteLine(S); if (R == S) { return(true); } return(false); }
/// <summary> /// Алгоритм Евклида /// </summary> /// <returns> НОД </returns> public BigInt32 gcd(BigInt32 bi) { BigInt32 x = this; BigInt32 y = bi; BigInt32 g = x; while (x.dataLength > 1 || (x.dataLength == 1 && x.data[0] != 0)) { g = x; x = y % x; y = g; } return(g); }
//*************************************************************************************** //*************************************************************************************** /// <summary> /// Генерирует положительный BigInt32. С большой вероятностью простое.+ /// </summary> public static BigInt32 GetPseudoPrime(int bits, int confidence, Random rand) { BigInt32 result = new BigInt32(); bool done = false; while (!done) { result.GetRandomBits(bits, rand); result.data[0] = 0x01; // делает сгенерированное число нечетным done = result.RabinMillerTest(confidence); //проверяем на простоту } return(result); }
/// <summary> /// Статический НОД чисел а и в /// </summary> public static BigInt32 NOD(BigInt32 a, BigInt32 b) { while (a != 0 && b != 0) { if (a >= b) { a = a % b; } else { b = b % a; } } return(a + b); }
/// <summary> /// Определяет считаются ли равными объекты класса BigInt32 /// </summary> public override bool Equals(object o) { BigInt32 bi = (BigInt32)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); }
/// <summary> /// Возвращает строку из BigInt32 /// </summary> public override string ToString() { string result = ""; BigInt32 a = this; BigInt32 quotient = new BigInt32(); BigInt32 remainder = new BigInt32(); while (a.dataLength > 1 || (a.dataLength == 1 && a.data[0] != 0)) { AlgorithmOfDivideSingleByte(a, 10, quotient, remainder); result = remainder.data[0] + result; a = quotient; } return(result); }
/// <summary> /// Перегрузка оператора вычитания /// </summary> public static BigInt32 operator -(BigInt32 bi1, BigInt32 bi2) { BigInt32 result = new BigInt32(); 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; } } // взять обратный if (carryIn != 0) { for (int i = result.dataLength; i < maxLength; i++) { result.data[i] = 0xFFFFFFFF; } result.dataLength = maxLength; } while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) { result.dataLength--; } return(result); }
/// <summary> /// Представляет длинное число из строки /// </summary> /// <param name="value">Исходная строка</param> /// <param name="radix">Основание</param> public BigInt32(string value) { BigInt32 multiplier = new BigInt32(1); BigInt32 result = new BigInt32(); for (int i = value.Length - 1; i >= 0; i--) { int posVal = value[i]; if (posVal >= '0' && posVal <= '9') { posVal -= '0'; } else { posVal = 9999999; } if (value[0] == '-') { posVal = -posVal; } result = result + (multiplier * posVal); if ((i - 1) >= 0) { multiplier = multiplier * 10; } } data = new uint[maxLength]; for (int i = 0; i < result.dataLength; i++) { data[i] = result.data[i]; } dataLength = result.dataLength; }
/// <summary> /// Ищет обратный элемент к e по модулю n /// </summary> public static BigInt32 Inverse(BigInt32 е, BigInt32 n) { BigInt32 O = new BigInt32(0); BigInt32 ONE = new BigInt32(1); BigInt32 d = new BigInt32(0); BigInt32 x = new BigInt32(0); BigInt32 y = new BigInt32(0); Evklid(е, n, ref x, ref y, ref d); if (d == ONE) { if (x < O) { return(x + n); } else { return(x); } } return(O); }
/// <summary> /// Перегрузка оператора присваивания остатка /// </summary> public static BigInt32 operator %(BigInt32 bi1, BigInt32 bi2) { BigInt32 quotient = new BigInt32(); BigInt32 remainder = new BigInt32(bi1); if (bi1 < bi2) { return(remainder); } else { if (bi2.dataLength == 1) { AlgorithmOfDivideSingleByte(bi1, bi2, quotient, remainder); } else { AlgorithmOfDivideMultiByte(bi1, bi2, quotient, remainder); } return(remainder); } }
/// <summary> /// Перегрузка оператора деления /// </summary> public static BigInt32 operator /(BigInt32 bi1, BigInt32 bi2) { BigInt32 quotient = new BigInt32(); //частное BigInt32 remainder = new BigInt32(); //остаток if (bi1 < bi2) { return(quotient); //0 } else { if (bi2.dataLength == 1) { AlgorithmOfDivideSingleByte(bi1, bi2, quotient, remainder); } else { AlgorithmOfDivideMultiByte(bi1, bi2, quotient, remainder); } return(quotient); } }
/// <summary> /// Конструктор. Генерирует и проверяет ключи. /// </summary> public RSA() { Random uy = new Random(); const int KeyOFLenght = 128; // Длина p и q BigInt32 p = BigInt32.GetPseudoPrime(KeyOFLenght, 10, uy); BigInt32 q = BigInt32.GetPseudoPrime(KeyOFLenght - 1, 10, uy); //Console.WriteLine("q = {0}, p = {1}", q, p); n = p * q; //Console.WriteLine("n= {0}", n); p--; q--; BigInt32 w = p * q; // функция Эйлера e = new BigInt32(); // открытый ключ do { e.GetRandomBits(KeyOFLenght * 2, uy); }while (BigInt32.NOD(e, w) != 1 && (e > n)); d = BigInt32.Inverse(e, w); // d - закрытый ключ (обратный к е по модулю w) } // d - существует <=> НОД(e,w) = 1
/// <summary> /// Перегрузка оператора отрицания /// </summary> public static BigInt32 operator -(BigInt32 bi1) { if (bi1.dataLength == 1 && bi1.data[0] == 0) { return(new BigInt32(1)); } BigInt32 result = new BigInt32(bi1); for (int i = 0; i < maxLength; i++) { result.data[i] = (uint)(~(bi1.data[i])); } 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++; } result.dataLength = maxLength; while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) { result.dataLength--; } return(result); }
/// <summary> /// Создает файлы "OpenKey.txt" с открытым ключом и "Secret.txt" c закрытым /// </summary> static void CreateFilesOfKeys() { RSA rsa = new RSA(); BigInt32 E = new BigInt32(rsa.e); BigInt32 D = new BigInt32(rsa.d); BigInt32 N = new BigInt32(rsa.n); FileStream Stream = new FileStream("OpenKey.txt", FileMode.Create, FileAccess.Write); StreamWriter Writer = new StreamWriter(Stream); Writer.WriteLine(E); Writer.WriteLine(N); Writer.Close(); Stream.Close(); Stream = new FileStream("SecretKey.txt", FileMode.Create, FileAccess.Write); Writer = new StreamWriter(Stream); Writer.WriteLine(D); Writer.WriteLine(N); Writer.Close(); Stream.Close(); Console.WriteLine("Файлы созданы."); }
/// <summary> /// Алгоритм деления числа BigInt32 на однозначное число /// </summary> private static void AlgorithmOfDivideSingleByte(BigInt32 bi1, BigInt32 bi2, BigInt32 Quotient, BigInt32 Remainder) { uint[] result = new uint[maxLength]; int resultPos = 0; // Копируем .... for (int i = 0; i < maxLength; i++) { Remainder.data[i] = bi1.data[i]; } Remainder.dataLength = bi1.dataLength; while (Remainder.dataLength > 1 && Remainder.data[Remainder.dataLength - 1] == 0) { Remainder.dataLength--; } ulong divisor = (ulong)bi2.data[0]; int pos = Remainder.dataLength - 1; ulong dividend = (ulong)Remainder.data[pos]; if (dividend >= divisor) { ulong quotient = dividend / divisor; result[resultPos++] = (uint)quotient; Remainder.data[pos] = (uint)(dividend % divisor); } pos--; while (pos >= 0) { dividend = ((ulong)Remainder.data[pos + 1] << 32) + (ulong)Remainder.data[pos]; ulong quotient = dividend / divisor; result[resultPos++] = (uint)quotient; Remainder.data[pos + 1] = 0; Remainder.data[pos--] = (uint)(dividend % divisor); } Quotient.dataLength = resultPos; int j = 0; for (int i = Quotient.dataLength - 1; i >= 0; i--, j++) { Quotient.data[j] = result[i]; } for (; j < maxLength; j++) { Quotient.data[j] = 0; } while (Quotient.dataLength > 1 && Quotient.data[Quotient.dataLength - 1] == 0) { Quotient.dataLength--; } if (Quotient.dataLength == 0) { Quotient.dataLength = 1; } while (Remainder.dataLength > 1 && Remainder.data[Remainder.dataLength - 1] == 0) { Remainder.dataLength--; } }
/// <summary> /// Алгоритм деления двух больших чисел. /// Делитель содержит больше 1 цифры /// </summary> /// <param name="bi1">Делимое</param> /// <param name="bi2">Делитель</param> /// <param name="Quotient">Частное</param> /// <param name="Remainder">Остаток</param> private static void AlgorithmOfDivideMultiByte(BigInt32 bi1, BigInt32 bi2, BigInt32 Quotient, BigInt32 Remainder) { uint[] result = new uint[maxLength]; int remainderLen = bi1.dataLength + 1; uint[] remainder = new uint[remainderLen]; uint mask = 0x90000000; //битовая маска uint val = bi2.data[bi2.dataLength - 1]; int shift = 0, resultPos = 0; while (mask != 0 && (val & mask) == 0) { shift++; mask >>= 1; } for (int i = 0; i < bi1.dataLength; i++) { remainder[i] = bi1.data[i]; } shiftLeft(remainder, shift); bi2 = bi2 << shift; 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]; ulong q_hat = dividend / firstDivisorByte; ulong r_hat = dividend % firstDivisorByte; 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]; } BigInt32 kk = new BigInt32(dividendPart); BigInt32 ss = bi2 * (long)q_hat; while (ss > kk) { q_hat--; ss -= bi2; } BigInt32 yy = kk - ss; for (int h = 0; h < divisorLen; h++) { remainder[pos - h] = yy.data[bi2.dataLength - h]; } result[resultPos++] = (uint)q_hat; pos--; j--; } Quotient.dataLength = resultPos; int y = 0; for (int x = Quotient.dataLength - 1; x >= 0; x--, y++) { Quotient.data[y] = result[x]; } for (; y < maxLength; y++) { Quotient.data[y] = 0; } while (Quotient.dataLength > 1 && Quotient.data[Quotient.dataLength - 1] == 0) { Quotient.dataLength--; } if (Quotient.dataLength == 0) { Quotient.dataLength = 1; } Remainder.dataLength = shiftRight(remainder, shift); for (y = 0; y < Remainder.dataLength; y++) { Remainder.data[y] = remainder[y]; } for (; y < maxLength; y++) { Remainder.data[y] = 0; } }
/// <summary> /// Перегрузка оператора умножения /// </summary> public static BigInt32 operator *(BigInt32 bi1, BigInt32 bi2) { int lastPos = maxLength - 1; bool bi1Neg = false, bi2Neg = false; // берем bi1 и bi2 по модулю if ((bi1.data[lastPos] & 0x90000000) != 0) // bi1 отрицательный { bi1Neg = true; bi1 = -bi1; } if ((bi2.data[lastPos] & 0x90000000) != 0) // bi2 отрицательный { bi2Neg = true; bi2 = -bi2; } BigInt32 result = new BigInt32(); // Умножение абсолютных велечин 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; } } 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--; } // проверка переполнения if ((result.data[lastPos] & 0x90000000) != 0) { if (bi1Neg != bi2Neg && result.data[lastPos] == 0x90000000) // bi1 и bi2 разного знака { //обрабатывать особый случай, когда умножение производит // максимальное отрицательное число в 2сс 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); } } } } // Если знаки разные if (bi1Neg != bi2Neg) { return(-result); } return(result); }