3. 深入研究 UCenter API 之 加密与解密(转载)

AuthCode

UCenter API 中的加密解密函数,被称为 php 领域的经典之作,也是康盛公司为 php 做的一大贡献

这个函数,可以通过一个 KEY ,生成动态的密文,并可以再通过这个 KEY 来解密

我没有研究过什么加密算法,所以对这个的基础知识也不是很了解,或许在 C# 中会有更强大的算法,但是这个函数在做 UCenter API 的时候是必需的。

也是 UCenter API php 版翻译成 C# 版本中最难的一个部分。

PHP 版详解

 1 // $string: 明文 或 密文
 2 // $operation:DECODE表示解密,其它表示加密
 3 // $key: 密匙
 4 // $expiry:密文有效期
 5 //字符串解密加密
 6 function authcode($string, $operation = ‘DECODE‘, $key = ‘‘, $expiry = 0) {
 7      // 动态密匙长度,相同的明文会生成不同密文就是依靠动态密匙
 8     $ckey_length = 4;   // 随机密钥长度 取值 0-32;
 9                 // 加入随机密钥,可以令密文无任何规律,即便是原文和密钥完全相同,加密结果也会每次不同,增大破解难度。
10                 // 取值越大,密文变动规律越大,密文变化 = 16 的 $ckey_length 次方
11                 // 当此值为 0 时,则不产生随机密钥
12     // 密匙
13     $key = md5($key ? $key : UC_KEY);
14     // 密匙a会参与加解密
15     $keya = md5(substr($key, 0, 16));
16     // 密匙b会用来做数据完整性验证
17     $keyb = md5(substr($key, 16, 16));
18     // 密匙c用于变化生成的密文
19     $keyc = $ckey_length ? ($operation == ‘DECODE‘ ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : ‘‘;
20     // 参与运算的密匙
21     $cryptkey = $keya.md5($keya.$keyc);
22     $key_length = strlen($cryptkey);
23
24     // 明文,前10位用来保存时间戳,解密时验证数据有效性,10到26位用来保存$keyb(密匙b),解密时会通过这个密匙验证数据完整性
25     // 如果是解码的话,会从第$ckey_length位开始,因为密文前$ckey_length位保存 动态密匙,以保证解密正确
26     $string = $operation == ‘DECODE‘ ? base64_decode(substr($string, $ckey_length)) : sprintf(‘%010d‘, $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
27     $string_length = strlen($string);
28
29     $result = ‘‘;
30     $box = range(0, 255);
31
32     $rndkey = array();
33     // 产生密匙簿
34     for($i = 0; $i <= 255; $i++) {
35         $rndkey[$i] = ord($cryptkey[$i % $key_length]);
36      }
37      // 用固定的算法,打乱密匙簿,增加随机性,好像很复杂,实际上对并不会增加密文的强度
38     for($j = $i = 0; $i < 256; $i++) {
39         $j = ($j + $box[$i] + $rndkey[$i]) % 256;
40         $tmp = $box[$i];
41         $box[$i] = $box[$j];
42         $box[$j] = $tmp;
43      }
44     // 核心加解密部分
45     for($a = $j = $i = 0; $i < $string_length; $i++) {
46         $a = ($a + 1) % 256;
47         $j = ($j + $box[$a]) % 256;
48         $tmp = $box[$a];
49         $box[$a] = $box[$j];
50         $box[$j] = $tmp;
51         // 从密匙簿得出密匙进行异或,再转成字符
52         $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
53      }
54
55     if($operation == ‘DECODE‘) {
56         // 验证数据有效性,请看未加密明文的格式
57         if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
58             return substr($result, 26);
59          } else {
60             return ‘‘;
61          }
62      } else {
63          // 把动态密匙保存在密文里,这也是为什么同样的明文,生产不同密文后能解密的原因
64          // 因为加密后的密文可能是一些特殊字符,复制过程可能会丢失,所以用base64编码
65         return $keyc.str_replace(‘=‘, ‘‘, base64_encode($result));
66      }
67 }

这份详解不是我写的,网上有很多,找不到原作者了

C# 版

  1 /// <summary>
  2 /// AuthCode解码&编码
  3 /// </summary>
  4 /// <param name="sourceStr">原始字符串</param>
  5 /// <param name="operation">操作类型</param>
  6 /// <param name="keyStr">API KEY</param>
  7 /// <param name="expiry">过期时间 0代表永不过期</param>
  8 /// <returns></returns>
  9 private static string AuthCode(string sourceStr, AuthCodeMethod operation, string keyStr, int expiry = 0)
 10 {
 11     var ckeyLength = 4;
 12     var source = Encode.GetBytes(sourceStr);
 13     var key = Encode.GetBytes(keyStr);
 14
 15     key = Md5(key);
 16
 17     var keya = Md5(SubBytes(key, 0, 0x10));
 18     var keyb = Md5(SubBytes(key, 0x10, 0x10));
 19     var keyc = (ckeyLength > 0)
 20                     ? ((operation == AuthCodeMethod.Decode)
 21                             ? SubBytes(source, 0, ckeyLength)
 22                             : RandomBytes(ckeyLength))
 23                     : new byte[0];
 24
 25     var cryptkey = AddBytes(keya, Md5(AddBytes(keya, keyc)));
 26     var keyLength = cryptkey.Length;
 27
 28     if (operation == AuthCodeMethod.Decode)
 29     {
 30         while (source.Length % 4 != 0)
 31         {
 32             source = AddBytes(source, Encode.GetBytes("="));
 33         }
 34         source = Convert.FromBase64String(BytesToString(SubBytes(source, ckeyLength)));
 35     }
 36     else
 37     {
 38         source =
 39             AddBytes(
 40                 (expiry != 0
 41                         ? Encode.GetBytes((expiry + PhpTimeNow()).ToString())
 42                         : Encode.GetBytes("0000000000")),
 43                 SubBytes(Md5(AddBytes(source, keyb)), 0, 0x10), source);
 44     }
 45
 46     var sourceLength = source.Length;
 47
 48     var box = new int[256];
 49     for (var k = 0; k < 256; k++)
 50     {
 51         box[k] = k;
 52     }
 53
 54     var rndkey = new int[256];
 55     for (var i = 0; i < 256; i++)
 56     {
 57         rndkey[i] = cryptkey[i % keyLength];
 58     }
 59
 60     for (int j = 0, i = 0; i < 256; i++)
 61     {
 62         j = (j + box[i] + rndkey[i]) % 256;
 63         var tmp = box[i];
 64         box[i] = box[j];
 65         box[j] = tmp;
 66     }
 67
 68     var result = new byte[sourceLength];
 69     for (int a = 0, j = 0, i = 0; i < sourceLength; i++)
 70     {
 71         a = (a + 1) % 256;
 72         j = (j + box[a]) % 256;
 73         var tmp = box[a];
 74         box[a] = box[j];
 75         box[j] = tmp;
 76
 77         result[i] = (byte)(source[i] ^ (box[(box[a] + box[j]) % 256]));
 78     }
 79
 80     if (operation == AuthCodeMethod.Decode)
 81     {
 82         var time = long.Parse(BytesToString(SubBytes(result, 0, 10)));
 83         if ((time == 0 ||
 84                 time - PhpTimeNow() > 0) &&
 85             BytesToString(SubBytes(result, 10, 16)) == BytesToString(SubBytes(Md5(AddBytes(SubBytes(result, 26), keyb)), 0, 16)))
 86         {
 87             return BytesToString(SubBytes(result, 26));
 88         }
 89         return "";
 90     }
 91     return BytesToString(keyc) + Convert.ToBase64String(result).Replace("=", "");
 92 }
 93
 94 /// <summary>
 95 /// Byte数组转字符串
 96 /// </summary>
 97 /// <param name="b">数组</param>
 98 /// <returns></returns>
 99 public static string BytesToString(byte[] b)
100 {
101     return new string(Encode.GetChars(b));
102 }
103
104 /// <summary>
105 /// 计算Md5
106 /// </summary>
107 /// <param name="b">byte数组</param>
108 /// <returns>计算好的字符串</returns>
109 public static byte[] Md5(byte[] b)
110 {
111     var cryptHandler = new MD5CryptoServiceProvider();
112     var hash = cryptHandler.ComputeHash(b);
113     var ret = "";
114     foreach (var a in hash)
115     {
116         if (a < 16)
117         { ret += "0" + a.ToString("x"); }
118         else
119         { ret += a.ToString("x"); }
120     }
121     return Encode.GetBytes(ret);
122 }
123
124 /// <summary>
125 /// Byte数组相加
126 /// </summary>
127 /// <param name="bytes">数组</param>
128 /// <returns></returns>
129 public static byte[] AddBytes(params byte[][] bytes)
130 {
131     var index = 0;
132     var length = 0;
133     foreach(var b in bytes)
134     {
135         length += b.Length;
136     }
137     var result = new byte[length];
138
139     foreach(var bs in bytes)
140     {
141         foreach (var b in bs)
142         {
143             result[index++] = b;
144         }
145     }
146     return result;
147 }
148
149 /// <summary>
150 /// Byte数组分割
151 /// </summary>
152 /// <param name="b">数组</param>
153 /// <param name="start">开始</param>
154 /// <param name="length">结束</param>
155 /// <returns></returns>
156 public static byte[] SubBytes(byte[] b, int start, int length = int.MaxValue)
157 {
158     if (start >= b.Length) return new byte[0];
159     if (start < 0) start = 0;
160     if (length < 0) length = 0;
161     if (length>b.Length || start + length > b.Length) length = b.Length - start;
162     var result = new byte[length];
163     var index = 0;
164     for(var k = start;k< start + length;k++)
165     {
166         result[index++] = b[k];
167     }
168     return result;
169 }
170
171 /// <summary>
172 /// 计算Php格式的当前时间
173 /// </summary>
174 /// <returns>Php格式的时间</returns>
175 public static long PhpTimeNow()
176 {
177     return DateTimeToPhpTime(DateTime.UtcNow);
178 }
179
180 /// <summary>
181 /// PhpTime转DataTime
182 /// </summary>
183 /// <returns></returns>
184 public static DateTime PhpTimeToDateTime(long time)
185 {
186     var timeStamp = new DateTime(1970, 1, 1); //得到1970年的时间戳
187     var t = (time + 8 * 60 * 60) * 10000000 + timeStamp.Ticks;
188     return new DateTime(t);
189 }
190
191 /// <summary>
192 /// DataTime转PhpTime
193 /// </summary>
194 /// <param name="datetime">时间</param>
195 /// <returns></returns>
196 public static long DateTimeToPhpTime(DateTime datetime)
197 {
198     var timeStamp = new DateTime(1970, 1, 1);  //得到1970年的时间戳
199     return (datetime.Ticks - timeStamp.Ticks) / 10000000;  //注意这里有时区问题,用now就要减掉8个小时
200 }
201
202 /// <summary>
203 /// 随机字符串
204 /// </summary>
205 /// <param name="lens">长度</param>
206 /// <returns></returns>
207 public static byte[] RandomBytes(int lens)
208 {
209     var chArray = new[]
210                         {
211                             ‘a‘, ‘b‘, ‘c‘, ‘d‘, ‘e‘, ‘f‘, ‘g‘, ‘h‘, ‘j‘, ‘k‘, ‘l‘, ‘m‘, ‘n‘, ‘o‘, ‘p‘, ‘q‘,
212                             ‘r‘, ‘s‘, ‘t‘, ‘u‘, ‘v‘, ‘w‘, ‘x‘, ‘y‘, ‘z‘, ‘A‘, ‘B‘, ‘C‘, ‘D‘, ‘E‘, ‘F‘, ‘G‘,
213                             ‘H‘, ‘J‘, ‘K‘, ‘L‘, ‘M‘, ‘N‘, ‘O‘, ‘P‘, ‘Q‘, ‘R‘, ‘S‘, ‘T‘, ‘U‘, ‘V‘, ‘W‘, ‘X‘,
214                             ‘Y‘, ‘Z‘, ‘0‘, ‘1‘, ‘2‘, ‘3‘, ‘4‘, ‘5‘, ‘6‘, ‘7‘, ‘8‘, ‘9‘
215                         };
216     var length = chArray.Length;
217     var result = new byte[lens];
218     var random = new Random();
219     for (var i = 0; i < lens; i++)
220     {
221         result[i] = (byte) chArray[random.Next(length)];
222     }
223     return result;
224 }
225
226 /// <summary>
227 /// 操作类型
228 /// </summary>
229 enum AuthCodeMethod
230 {
231     Encode,
232     Decode,
233 }

C# 版是一行一行按照原版本翻译的,增加了一些 C# 中没有的函数

1、string -> byte[] 的问题

在这段算法中,经常会用到 Base64 算法,C# 中的 Base64 要求输入的是 byte[] 数组

在 php 程序中,都是直接用字符串的,而且也没有问题。

那在 C# 版中自然想到了 Encoding.Default.GetBytes() 函数

但这个函数有个很奇怪的问题:

Encoding.UTF8.GetBytes(((char) 200).ToString())[0].ToString() //最后的值是多少?

运行一下后发现它不是200,因为这个函数涉及到了编码问题

所以上述的操作,如果直接对字符串操作,那会出现很多问题,因为 php 和 C# 对字符串使用的默认编码不同。

所以就改成了对 byte[] 进行操作

原文地址:http://www.cnblogs.com/webenh/p/8110423.html

时间: 2024-11-05 17:10:14

3. 深入研究 UCenter API 之 加密与解密(转载)的相关文章

【绝对原创】最新研究成果,EFS加密文件真的可以解密!各位不要放弃哈~

我是一名正在读计算机专业的博士,前一段时间花费了几个月研究EFS解密技术,终于成功了,现在总结一下,给各位一个启示~ 关于EFS加密: EFS(Encrypting File System,加密文件系统)是Windows系统特有的一个实用功能,对于NTFS卷上的文件和数据,都可以直接被操作系统加密保存,在很大程度上提高了数据的安全性.但是,如果不小心重装系统或损坏硬盘等将导致EFS加密文件无法打开,无法复制,但可以删除.EFS加密解密都是透明完成,如果用户加密了一些数据,那么其对这些数据的访问将

PHP 加密 和 解密 方法

关于Discuz的加密解密函数,相信大家都有所了解,该authcode函数可以说是对PHP界作出了重大的贡献,真的发觉discuz这个函数写的太精彩啦. 研究了一下这个算法,总的来说可以归纳为以下三点: 1,动态性,同一字符串使用相同的key,每次加密的密文都不一样,而解密方法只有一个,其实就是把解密的信息放到了密文上面. 2,时效性,可以自己加一个限期参数,以秒为单位,这个其实就是在密文里加入了有效时间. 3,统一性,加密和解密都用同一个函数,而且用了比较简单的异或算法. 由于该函数具有以上功

转:system.Security.Cryptography C# 加密和解密

以下文转自: http://www.360doc.com/content/13/0122/05/19147_261678471.shtml 总结:注册的时候经过MD5加密存进数据库,在登录的时候需要先加密输入的密码,再进行和数据库里的比对,因为同一字符串加密后是一样的,并不是无规则的:实例: string name = this.TextBox1.Text;        string pwd = System.Web.Security.FormsAuthentication.HashPassw

如何对web.config进行加密和解密

在WEB网站开发过程中,如果我们将数据库连接字符串封装到.DLL文件中,将会给数据库和程序的迁移带来麻烦,因为万一服务器地址或者数据库发生变更,那么我们就不得不修改源程序并重新将其编译.更好的解决方法是将数据库连接字符串写入到web.config配置文件中,可问题是将连接字符串写入到web.config文件中之后,任何人都能打开看到所连接的数据库名和密码,又会带来安全隐患,因此为了保证数据库的安全性,我们可以通过使用微软IDE自带的命令aspnet_regiis.exe将配置文件web.conf

加密解密技术—Web.config加密和解密

阅读目录 一:我们为什么要对web.config配置文件中的配置节加密? 二:怎么样加密和解密? 三:实例 四:运行效果 一:我们为什么要对web.config配置文件中的配置节加密? 因为在我们的项目中,有的配置节可能包含敏感信息,我们看下面的<connectionStrings/>配置节中包含了我们连接 数据库的用户名和密码以及IP地址,这要是暴露出去是很危险的,还有<identity/>配置节中包含了运行时使用的模拟账号的用户名和密 码,这些配置节都包含着敏感信息,我们不希望

OD学习笔记10:一个VB程序的加密和解密思路

前边,我们的例子中既有VC++开发的程序,也有Delphi开发的程序,今天我们给大家分析一个VB程序的加密和解密思路. Virtual BASIC是由早期DOS时代的BASIC语言发展而来的可视化编程语言. VB是由事件驱动的编程语言:就是在可视化编程环境下我们可以绘制一些窗体,按钮,编辑框等控件,然后为这些控件所可能引发的事件如按钮被单击或者被双击编写对应的处理代码. 所有的VB程序几乎都是依赖于一个外部的动态链接库.这个动态链接库的名字是:MSVBVM60.dll(可能有多个版本,但名字都差

新书预告《黑客攻防实战加密与解密》

很长一段时间,没有怎么研究技术,后面慢慢发现,自己就是弄技术的料,于是重新开始技术的研究,学习和总结,慢慢的就有了新的一套书,目前已经完成黑客攻防实战加密与解密,正在写的黑客攻防实战提权技术研究已经完成稿件的90%,预计明年年初应该可以上市. 现在的社会,房价高企,社会问题突出,很多很多现实问题,在我们改变不了现实的情况下,多做研究,多做储备,也许有一天还能宝刀未老!也能持剑杀敌!最近退了很多技术群和微信群,很多还是专家级别的,他们天天发牢骚,转发负面新闻,很是无趣,决定退掉,回归简单生活,工作

网络传输的加密与解密(不是很难理解的一个版本)

--网络传输的加密与解密--    由于互联网早期并没有考虑到数据报文安全的问题,早期的理念都是基于进行通信的.现在随着网络兴起,互联网上慢慢有很多"犯罪团体",用较低的犯罪成本通常是基于一个网络通信协议的一个小的漏洞来完成窃取.篡改.重播其他人的数据报文. 而往往计算机网络安全维护者和那些恶意攻击者玩的就是一个类似猫鼠游戏,网络安全维护者处处考虑对计算机的安全负责,而恶意攻击者处处找安全的漏洞.而对于那些恶意攻击方式大体分两种. 一种叫被动攻击,大体上来说就是窃听,攻击者获得传输信息

以PKI为基础的CA工作原理及 加密、解密过程

             以PKI为基础的CA工作原理及  加密.解密过程   PKI(Public Key Infrastructure ) 即"公钥基础设施",是一种遵循既定标准的密钥管理平台,它能够为所有网络应用提供加密和数字签名等密码服务及所必需的密钥和证书管理体系.简单来说,PKI就是利用公钥理论和技术建立的提供安全服务的基础设施.PKI技术是信息安全技术的核心,也是电子商务的关键和基础技术. 一个典型.完整.有效的PKI 应用系统至少应具有以下五个部分: 1) 认证中心CA