MD5算法原理简要介绍并采用C#应用在桌面应用系统的用户登录与注册中
接上文,本文简要介绍一下MD5加密算法的原理,并采用C#实现MD5算法的加密与解密过程,将这一实现过程应用在我自己开发的桌面办公应用-工资管理系统 中的用户注册与登录模块,有需要进一步了解的博友可以在下面留言或者可以加我QQ:1974544863(记得备注:"桌面办公应用"),我可以与您一同交流其中实现的原理。
MD5,是message-digest(即“信息-摘要”)的简称,是一种加密算法,具有不可逆性,简要地说:“MD5是以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值”。具体的原理可以参考这篇csdn博客 :MD5算法原理。
我虽然喜欢算法,但在开发软件过程中,考虑到软件的开发周期,就没深入研究MD5的具体原理,而只是从中抽取了核心的关键点:明文,密钥key,密钥。 从这点入手,去网上搜索几篇采用C#实现MD5算法的代码,之后导入自己的项目工程中,一步一步加breakpoint,单步debug,最后实现了自己想要的效果!
下面介绍采用C#实现MD5算法的代码,正如上面提到的,核心关键点有明文,密钥key,密钥这三点,所以代码也就主要有三个功能方法MD5Encrypt(string pToEncrypt, string sKey),GenerateKey(),MD5Decrypt(string pToDecrypt, string sKey)。下面是代码(在代码有部分进行了注释,就不再进行详解,若想知晓每一句代码的原理,请你参考上面提到的那篇参考博客MD5算法原理)。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Security.Cryptography; using System.IO; namespace SMS.cmmMessage { class MD5EncryptAndDecrypt { // 创建Key public string GenerateKey() { DESCryptoServiceProvider desCrypto = (DESCryptoServiceProvider)DESCryptoServiceProvider.Create(); //将产生的tempKey采用ASCII进行编码 return ASCIIEncoding.ASCII.GetString(desCrypto.Key); } ///MD5加密 public string MD5Encrypt(string pToEncrypt, string sKey) { DESCryptoServiceProvider des = new DESCryptoServiceProvider(); byte[] inputByteArray = Encoding.Default.GetBytes(pToEncrypt); des.Key = ASCIIEncoding.ASCII.GetBytes(sKey); des.IV = ASCIIEncoding.ASCII.GetBytes(sKey); MemoryStream ms = new MemoryStream(); CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write); cs.Write(inputByteArray, 0, inputByteArray.Length); cs.FlushFinalBlock(); StringBuilder ret = new StringBuilder(); foreach (byte b in ms.ToArray()) { ret.AppendFormat("{0:X2}", b); } ret.ToString(); return ret.ToString(); } ///MD5解密 public string MD5Decrypt(string pToDecrypt, string sKey) { DESCryptoServiceProvider des = new DESCryptoServiceProvider(); byte[] inputByteArray = new byte[pToDecrypt.Length / 2]; for (int x = 0; x < pToDecrypt.Length / 2; x++) { int i = (Convert.ToInt32(pToDecrypt.Substring(x * 2, 2), 16)); inputByteArray[x] = (byte)i; } des.Key = ASCIIEncoding.ASCII.GetBytes(sKey); des.IV = ASCIIEncoding.ASCII.GetBytes(sKey); MemoryStream ms = new MemoryStream(); CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write); cs.Write(inputByteArray, 0, inputByteArray.Length); cs.FlushFinalBlock(); StringBuilder ret = new StringBuilder(); return System.Text.Encoding.Default.GetString(ms.ToArray()); } } }值得说明的是,我这个实现的是:产生的密文是16位的,当然你也可以改改成为32位的,或者网上搜搜!
算法的研究如果只是停留在研究的层次,或者研究只是为了书写一大堆学术论文的话,我个人觉得没多大意思。像我一直比较崇拜的MSRA,就真的是将研究的成果应用到诸多产品中,我觉得那才有意思!(可惜自己能力有限啊,不然真的想去MSRA跟那堆牛人共事!)。
因为自己最近正好在自己开发一套企业使用的工资管理系统,其中涉及到用户注册于登录模块,对于投身应用软件开发的人而言,势必需要经常与“安全”打交道,因为,正如上文提到的“有应用系统就会有用户,有用户就会涉及到登录注册,涉及到登录注册则势必与密码挂钩,而有密码则会涉及到安全,想到了安全则自然需要想到加密”。正是这样的想法驱动,我将C#实现的MD5加密解密算法应用了我开发的桌面办公应用系统-工资管理系统中。
实现的过程,核心的地方有两点:一点是三个核心关键:明文,密钥key,密钥;另一点是在key上再拼接自定义的一段字符串!
下面以我自主开发的工资管理系统为例,下面是注册界面与代码:我们只关心用户的登录密码,其他的用户信息的处理以及含义在后续博文中会陆续介绍!
//加密解密工具 MD5EncryptAndDecrypt md5 = new MD5EncryptAndDecrypt();在保存时候,即注册用户时候,保存用户的登录密码那一段代码是这样的:
//加密密码与密钥 String loginKeyTemp=md5.GenerateKey();//产生临时密钥 String loginPasswordEncrypt=md5.MD5Encrypt(empLoginPassword,loginKeyTemp);//产生密文 String loginKeyEncrypt=loginKeyTemp+CommonMessage.lastKeyStr;//产生真正存储的密钥之后,数据库中的用户表中关于登录密码的需要有两个字段:loginPassword,loginKey,其中loginPassword存储的是用户明文密码加密后的密文,loginKey存储的是真正的密钥(当然了,真正有用的是那个临时的密钥:需要截断自定义的那段拼接的字符串)。
所以在登录时候,代码是这样的
//登录事件 private void buttonLogin_Click(object sender, EventArgs e) { try { string userName = textBoxUserName.Text.Trim(); string userPwd = textBoxPwd.Text.Trim(); if (userName == "" || userPwd == "") { MessageBox.Show("用户名或密码不能为空!", "友情提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } else { String[] loginFieldNames = new String[]{ "loginPassword","loginKey" }; String tempSQL = " from tb_employee where loginName='" + userName + "'"; String strSql = cmmUtils.organizeSqlStatementWithFields(loginFieldNames, tempSQL); Console.WriteLine(strSql); String[] dataResults = operate.GetDatasFromSelectedTable(strSql.ToString(), loginFieldNames); //不存在该用户名 if (dataResults==null) { MessageBox.Show("该用户名不存在!", "友情提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { String loginPasswordEncrypt = dataResults[0];//用户名对应的登录密码 String loginKeyTemp = dataResults[1];//用户名对应的加了拼接字符串的登录密钥 String loginKeyReal = loginKeyTemp.Substring(0, loginKeyTemp.IndexOf(CommonMessage.lastKeyStr));//截断后才是真正的密钥 String loginPasswordDecrypt = md5.MD5Decrypt(loginPasswordEncrypt, loginKeyReal);//解密密文 if (!userPwd.Equals(loginPasswordDecrypt)) { MessageBox.Show("用户密码错误!", "友情提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { //MessageBox.Show("登陆成功!", "友情提示", MessageBoxButtons.OK, MessageBoxIcon.Information); this.Hide(); <span style="white-space:pre"> </span> //密码匹配正确即成功登录:记录当前的用户名与用户密码 CommonMessage.userName = userName; CommonMessage.userPassword = userPwd; String[] selFieldNames = new String[]{//主要用于获取当前用户的用户权限 "powerName" }; String tempPowerSQL = " from tb_employee,tb_powerType where tb_employee.powerId=tb_powerType.powerId and tb_employee.loginName='" + userName + "'"; String strPowerSql = cmmUtils.organizeSqlStatementWithFields(selFieldNames, tempPowerSQL); String[] dataPowerResults = operate.GetDatasFromSelectedTable(strPowerSql.ToString(), selFieldNames); CommonMessage.userPower = dataPowerResults[0]; frmMain fMain = new frmMain(); fMain.ShowDialog(); } } } } catch (System.Exception ex) { MessageBox.Show(ex.Message + "\n请与管理员联系!", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information); } }