Фрагмент для ознакомления
bin";// создаем поток на запись или дозапись (объект BinaryWriter)if (File.Exists(FileName)) {using (BinaryWriter writer = new BinaryWriter(File.Open(FileName, FileMode.Append, FileAccess.Write))){// записываемвфайлстрокуwriter.Write(newUser.login);// записываемвфайлхешwriter.Write(newUser.password.Hash);// записываемвфайлсольwriter.Write(newUser.password.Salt);writer.Close(); } }else {using (BinaryWriter writer = new BinaryWriter(File.Open(FileName, FileMode.OpenOrCreate))){// записываемвфайлстрокуwriter.Write(newUser.login);// записываемвфайлхешwriter.Write(newUser.password.Hash);// записываемвфайлсольwriter.Write(newUser.password.Salt); } }MessageBox.Show("Пользовательсоздан!");this.Close();}else { MessageBox.Show("Не введен логин или пароли не совпали");} } }}using System;using System.Collections.Generic;using System.Linq;using System.Security.Cryptography;using System.Text;using System.Threading.Tasks;namespace HousingFund{publicclassPassword {privatebyte[] inSalt; // синхропосылкаprivatebyte[] inHash; // хэшпароля//следующие четыре поля возвращают значения хеша пароля и синхропосылку //в строке из цифр в кодировке Base64 и побайтовоinternalstring Salt {get { return Convert.ToBase64String(inSalt); } }internalstring Hash {get { return Convert.ToBase64String(inHash); } }internalbyte[] RawSalt {get { return (byte[])inSalt.Clone(); } }internalbyte[] RawHash {get { return (byte[])inHash.Clone(); } }internalPassword(string salt, string hash){//Конструктор принимает две строки: синхропосылку и хеш//преобразует их в строку из цифр в кодировке Base64//и сохраняет в закрытых полях классаinSalt = Convert.FromBase64String(salt);inHash = Convert.FromBase64String(hash); }internalPassword(byte[] salt, byte[] hash){/*Конструктор, принимающий массивы байт, * создает копии входных массивов*/inSalt = (byte[])salt.Clone();inHash = (byte[])hash.Clone(); }internalPassword(char[] clearText) {/*Конструкторсоднимаргументомгенерируетслучайную синхропосылку и вычисляет хэш предоставленного пароля, используя внутренний методHashPassword()*/inSalt = GenerateRandom(8);inHash = HashPassword(clearText);}/*Метод Verify сначала вычисляет хэш предоставленного пароля, используя все тот же внутренний метод HashPassword, после чего сравнивает байты сохраненного хэша с байтами только что вычисленного хэша. Совпадение всех до одного байтов хэша означает, что пароль верный.*/internalbool Verify(char[] clearText) {byte[] hash = HashPassword(clearText);if (hash.Length == inHash.Length) {for (int i = 0; i < hash.Length; i++) {if (hash[i] != inHash[i])returnfalse; }returntrue; }returnfalse;}/*Статический метод Generate просто генерирует массив случайных байтов и преобразует его в base64-строку. */privatestaticchar[] Generate(){char[] random = newchar[12];// генерируем 9 случайных байтов; этого достаточно, чтобы// получить 12 случайных символов из набора base64byte[] rnd = GenerateRandom(9);// конвертируем случайные байты в base64Convert.ToBase64CharArray(rnd, 0, rnd.Length, random, 0);// очищаемрабочиймассивArray.Clear(rnd, 0, rnd.Length);return random; }/* Метод записывает синхропосылку и пароль в поток на основе массива байтов, а затем вычисляет хэш содержимого потока. В качестве хэш-функции используется алгоритм SHA-256. */privatebyte[] HashPassword(char[] clearText){Encodingutf8 = Encoding.UTF8;byte[] hash;// создаем рабочий массив достаточного размера, чтобы вместитьbyte[] data = newbyte[inSalt.Length + utf8.GetMaxByteCount(clearText.Length)];try {// копируем inSalt врабочиймассивArray.Copy(inSalt, 0, data, 0, inSalt.Length);// копируем пароль в рабочий массив, преобразуя его в UTF-8int byteCount = utf8.GetBytes(clearText, 0, clearText.Length,data, inSalt.Length);// хэшируем данные массиваusing (System.Security.Cryptography.HashAlgorithm alg = new SHA256Managed())hash = alg.ComputeHash(data, 0, inSalt.Length + byteCount);}finally {// очищаем рабочий массив в конце работы, чтобы избежать// утечки открытого пароля Array.Clear(data, 0, data.Length); }return hash; }/*Метод GenerateRandom генерирует массив указанной длины,состоящийизслучайныхбайтов. */privatestaticbyte[] GenerateRandom(int size) {byte[] random = newbyte[size];RandomNumberGenerator.Create().GetBytes(random);return random;} }}using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace HousingFund{publicstaticclassStringExtensions {//////Extends the String
class with this ToFixedString
method./////////The prefered fixed string size///Thechar
to append///publicstatic String ToFixedString(this String value, int length, char appendChar = ' ') {int currlen = value.Length;int needed = length == currlen ? 0 : (length - currlen);return needed == 0 ? value : (needed > 0 ? value + newstring(' ', needed) :newstring(newstring(value.ToCharArray().Reverse().ToArray()).Substring(needed * -1, value.Length - (needed * -1)).ToCharArray().Reverse().ToArray())); } }}using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Data.SqlClient;using System.Drawing;using System.Globalization;using System.Linq;using System.Text;using System.Text.RegularExpressions;using System.Threading.Tasks;using System.Windows.Forms;namespace HousingFund{publicpartialclassfrmAddData : Form {publicfrmAddData() {InitializeComponent(); }privatevoid frmAddData_Load(object sender, EventArgs e) {this.panel1.Visible = true; }privateint AddDataSQL(string address, int cntFlats, double square, int yearBuild, int floors) {// выражение SQL длядобавленияданныхstring sqlExpression = "INSERT INTO [Houses] ([addressHouse],[cntFlats],[squareFlats],[yearBuild],[floors]) VALUES (@address,@cntFlats,@square,@yearBuild,@floors)"; SqlConnection cn = newSqlConnection(frmAutorization.ConnStr);try {cn.Open(); SqlCommand command = newSqlCommand(sqlExpression, cn);// создаемпараметрдляадреса SqlParameter addressParam = newSqlParameter("@address", address);// добавляемпараметрккомандеcommand.Parameters.Add(addressParam);// создаемпараметрдляколичестваквартирSqlParameter cntFlatsParam = newSqlParameter("@cntFlats", cntFlats);// добавляемпараметрккомандеcommand.Parameters.Add(cntFlatsParam);// создаем параметр для площади квартирSqlParameter squareParam = newSqlParameter("@square", square);// добавляемпараметрккомандеcommand.Parameters.Add(squareParam);// создаем параметр для года постройкиSqlParameter yearParam = newSqlParameter("@yearBuild", yearBuild);// добавляемпараметрккомандеcommand.Parameters.Add(yearParam);// создаем параметр для этажностиSqlParameter floorsParam = newSqlParameter("@floors", floors);// добавляем параметр к командеcommand.Parameters.Add(floorsParam);int number = command.ExecuteNonQuery();return number; }catch (Exception ex) {MessageBox.Show(ex.Message);return 0; } }privatevoid btnAddData_Click(object sender, EventArgs e) { Housing objectHousing = newHousing();string sqlExpression;if (txtAddress.Text.Length != 0 && txtCntFlats.Text.Length != 0 && txtSquareFlats.Text.Length != 0 && txtYearBuild.Text.Length != 0 && txtFloors.Text.Length != 0) { objectHousing.addressHouse = txtAddress.Text;int k = Convert.ToInt32(txtCntFlats.Text);if (k == 0) { MessageBox.Show("Количество квартир должно быть больше 0!");return; }else { objectHousing.cntFlats= k; }// Задаемдесятичнуюточку IFormatProvider formatter = new NumberFormatInfo { NumberDecimalSeparator = "."};//приводим число в техтбокс к формату с точкой (даже если введена запятая)string tmpS = txtSquareFlats.Text.Replace(",", ".");//получаемплощадьчисломdouble p = double.Parse(tmpS, formatter);if (p == 0) { MessageBox.Show("Жилая площадь не может быть нулевой!");return; }else { objectHousing.squareFlats = p; } k = Convert.ToInt32(txtYearBuild.Text);MessageBox.Show(DateTime.Now.Year.ToString());if (k < 1500 || k > DateTime.Now.Year) {MessageBox.Show("Год постройки не может быть больше текущего года и меньше 1500 года!");return; }else { objectHousing.yearBuild = k; } k = Convert.ToInt32(txtFloors.Text);if (k < 1 && k > 100) { MessageBox.Show("Этажность может быть от 1 до 100!");return; }else { objectHousing.floors = k; }MessageBox.Show($"Добавленообъектов: {AddDataSQL(objectHousing.addressHouse, objectHousing.cntFlats, objectHousing.squareFlats, objectHousing.yearBuild, objectHousing.floors)}"); txtAddress.Text = ""; txtCntFlats.Text = ""; txtSquareFlats.Text = ""; txtYearBuild.Text = ""; txtFloors.Text = ""; }else {MessageBox.Show("Заполните все поля!");} }privatevoid txtCntFlats_KeyPress(object sender, KeyPressEventArgs e) {//вводтолькоцифри backspaceif (!Char.IsDigit(e.KeyChar) && e.KeyChar != Convert.ToChar(8)) { e.Handled = true; } }privatevoid txtYearBuild_KeyPress(object sender, KeyPressEventArgs e) {//вводтолькоцифри backspaceif (!Char.IsDigit(e.KeyChar) && e.KeyChar != Convert.ToChar(8)) { e.Handled = true; } }privatevoid txtFloors_KeyPress(object sender, KeyPressEventArgs e) {//вводтолькоцифри backspaceif (!Char.IsDigit(e.KeyChar) && e.KeyChar != Convert.ToChar(8)) { e.Handled = true; } }privatevoid txtSquareFlats_TextChanged(object sender, EventArgs e){//заменяет точку на запятую, вне зависимости от того, что введеноstring s = Regex.Replace(((TextBox)sender).Text, @"[^\d.,]", ""); ((TextBox)sender).Text = s; }privatevoid txtSquareFlats_KeyPress(object sender, KeyPressEventArgs e){//ввод только цифр, точки, запятой и backspaceif (!Char.IsDigit(e.KeyChar) && (e.KeyChar != ',') && (e.KeyChar != '.') && e.KeyChar != Convert.ToChar(8)) { e.Handled = true; } }privatevoid btnExit_Click(object sender, EventArgs e) {this.Close(); } }}using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.IO;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows.Forms;namespace HousingFund{publicpartialclassEditBox : Form {publicEditBox() {InitializeComponent(); }privatevoid btnCancel_Click(object sender, EventArgs e) {this.Close(); }privatevoid btnOk_Click(object sender, EventArgs e) {if (txtLogin.Text.Length != 0 && txtPassword1.Text == txtPassword2.Text){// переменные для считываемых данныхstring FileName = "users.bin";string logIn;string hashIn;string saltIn;int i = -1; FileStream fs_read = newFileStream(FileName, FileMode.Open); {using (BinaryReader reader = new BinaryReader(fs_read)) {long count = fs_read.Length / 84;for (int pos = 0; pos < count; ++pos) { reader.BaseStream.Position = 84 * pos;//перешливпозициюlogIn = reader.ReadString();//считалилогинhashIn = reader.ReadString();//считалихешsaltIn = reader.ReadString();//считалисольif (logIn == txtLogin.Text.ToFixedString(25)) { i = pos; } }reader.Close(); } fs_read.Close(); }if (i < 0) {MessageBox.Show("Неверныйлогин!");}else {//задаем пароль как массив фиксированной длиныchar[] charPass = txtPassword1.Text.ToFixedString(25).ToCharArray(); SystemUser newUser = new SystemUser {//выравниваемлогинlogin = txtLogin.Text.ToFixedString(25),password = new Password(charPass) };using (var stream = File.Open(FileName, FileMode.Open)) {using (var writer = new BinaryWriter(stream)) {writer.Seek(i * 84, SeekOrigin.Begin);writer.Write(newUser.login);writer.Write(newUser.password.Hash);writer.Write(newUser.password.Salt);writer.Close(); }stream.Close(); }MessageBox.Show("Парольизменен!");this.Close(); } }else {MessageBox.Show("Невведенлогинилипаролинесовпали");} } }}using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Data.SqlClient;using System.Drawing;using System.Globalization;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows.Forms;namespace HousingFund{publicpartialclassfrmEditData : Form {publicfrmEditData() {InitializeComponent(); }privatevoid btnExit_Click(object sender, EventArgs e) {this.Close(); }privateint UpdateDataSQL(string id, string address, int cntFlats, double square, int yearBuild, int floors) {// выражение SQL длядобавленияданныхstring sqlExpression = "UPDATE [Houses] SET [addressHouse]=@address,[cntFlats]=@cntFlats,[squareFlats]=@square,[yearBuild]=@yearBuild,[floors]=@floors WHERE idHouse=@id"; SqlConnection cn = newSqlConnection(frmAutorization.ConnStr);try {cn.Open(); SqlCommand command = newSqlCommand(sqlExpression, cn); SqlParameter idParam = newSqlParameter("@id", Convert.ToInt32(frmMain.id));// добавляемпараметрккомандеcommand.Parameters.Add(idParam);// создаемпараметрдляадресаSqlParameter addressParam = newSqlParameter("@address", address);// добавляемпараметрккомандеcommand.Parameters.Add(addressParam);// создаемпараметрдляколичестваквартирSqlParameter cntFlatsParam = newSqlParameter("@cntFlats", cntFlats);// добавляемпараметрккомандеcommand.Parameters.Add(cntFlatsParam);// создаем параметр для площади квартирSqlParameter squareParam = newSqlParameter("@square", square);// добавляемпараметрккомандеcommand.Parameters.Add(squareParam);// создаем параметр для года постройкиSqlParameter yearParam = newSqlParameter("@yearBuild", yearBuild);// добавляемпараметрккомандеcommand.Parameters.Add(yearParam);// создаем параметр для этажностиSqlParameter floorsParam = newSqlParameter("@floors", floors);// добавляем параметр к командеcommand.Parameters.Add(floorsParam);int number = command.ExecuteNonQuery();return number; }catch (Exception ex) {MessageBox.Show(ex.Message);return 0; } }privatevoid btnAddData_Click(object sender, EventArgs e) {//update sqlif (txtAddress.Text.Length != 0 && txtCntFlats.Text.Length != 0 && txtSquareFlats.Text.Length != 0 && txtYearBuild.Text.Length != 0 && txtFloors.Text.Length != 0) { frmMain.objectHousing.addressHouse = txtAddress.Text;int k = Convert.ToInt32(txtCntFlats.Text);if (k == 0) { MessageBox.Show("Количество квартир должно быть больше 0!");return; }else { frmMain.objectHousing.cntFlats = k; }// Задаемдесятичнуюточку IFormatProvider formatter = new NumberFormatInfo { NumberDecimalSeparator = "."};//приводим число в техтбокс к формату с точкой (даже если введена запятая)string tmpS = txtSquareFlats.Text.Replace(",", ".");//получаемплощадьчисломdouble p = double.Parse(tmpS, formatter);if (p == 0) { MessageBox.Show("Жилая площадь не может быть нулевой!");return; }else { frmMain.objectHousing.squareFlats = p; } k = Convert.ToInt32(txtYearBuild.Text);if (k < 1500 && k > DateTime.Now.Year){ MessageBox.Show("Год постройки не может быть больше текущего года и меньше 1500 года!");return; }else { frmMain.objectHousing.yearBuild = k; } k = Convert.ToInt32(txtFloors.Text);if (k < 1 && k > 100) { MessageBox.Show("Этажность может быть от 1 до 100!");return; }else { frmMain.objectHousing.floors = k; }MessageBox.Show($"Обновленообъектов: {UpdateDataSQL(frmMain.id,frmMain.objectHousing.addressHouse, frmMain.objectHousing.cntFlats, frmMain.objectHousing.squareFlats, frmMain.objectHousing.yearBuild, frmMain.objectHousing.floors)}"); txtAddress.Text = ""; txtCntFlats.Text = ""; txtSquareFlats.Text = ""; txtYearBuild.Text = ""; txtFloors.Text = ""; }else {MessageBox.Show("Заполните все поля!");} }privatevoid frmEditData_Load(object sender, EventArgs e) {this.panel1.Visible = true; txtAddress.Text = frmMain.objectHousing.addressHouse; txtCntFlats.Text= frmMain.objectHousing.cntFlats.ToString(); txtSquareFlats.Text = frmMain.objectHousing.squareFlats.ToString(); txtYearBuild.Text = frmMain.objectHousing.yearBuild.ToString(); txtFloors.Text = frmMain.objectHousing.floors.ToString(); } }}using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.IO;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows.Forms;namespace HousingFund{publicpartialclassViewTextFile : Form {publicViewTextFile() {InitializeComponent(); }privatevoid ViewTextFile_Load(object sender, EventArgs e) {string path = "output.txt";// асинхронноечтениеusing (StreamReader reader = new StreamReader(path)) {string line;while ((line = reader.ReadLine()) != null) {rtxtBoxView.AppendText(line);rtxtBoxView.AppendText("\n");} } } }}
1.C# – хэширование данных с использованием соли. [Электронный ресурс] – Режим доступа: https://www.nookery.ru/c-hashing-data-using-salt/
2.Бинарные файлы. BinaryWriter и BinaryReader. [Электронный ресурс] – Режим доступа: https://metanit.com/sharp/tutorial/5.6.php
3.Документация по C# [Электрон. ресурс]. – Режим доступа: https://docs.microsoft.com/ru-ru/dotnet/csharp/
4.Казанский А.А. Программирование на Visual C# 2013: учеб. пособие для СПО / А.А. Казанский.- М.: Издательство Юрайт, 2019. – 191 с.
5.Методы хранения паролей. [Электронный ресурс]. – Режим доступа: http://www.cyberguru.ru/algorithms/algorithms-theory/password-storing-methods.html?showall=1
6.Павловская Т. А. С#. Программирование на языке высокого уровня. Учебник для вузов. – СПб.: Питер, 2014. – 432 с: ил.
7.Практика программирования на С# для Windows и Web в Microsoft Visual Studio. [Электронный ресурс]. – Режим доступа: http://wladm.narod.ru/C_Sharp/index.html
8.Тюкачев Н. А., Хлебостроев В. Г. C#. Основы программирования: Учебное пособие. – 3_е изд., стер. – СПб.: Издательство «Лань», 2018. – 272 с.
9.Фленов М. Е. Библия C#. – 4-е изд., перераб. и доп. – СПб.: БХВ-Петербург, 2019. – 512 с.