3. 深入研究 UCenter API 之 加密与解密(转载)
- 1. 深入研究 UCenter API 之 开篇 (转载)
- 2. 深入研究 UCenter API 之 通讯原理(转载)
- 3. 深入研究 UCenter API 之 加密与解密(转载)
- 4. 深入研究 UCenter API 之 网站搭建(转载)
- 5. 深入研究 UCenter API 之 MVC 网站下的用法(转载)
- 6. 下载地址:UCenter API For .Net 在 CodePlex 上发布啦!(转载)
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[] 进行操作
3. 深入研究 UCenter API 之 加密与解密(转载)的更多相关文章
- 深入研究 UCenter API For .NET
康盛旗下产品的搭建 来自http://www.dozer.cc/2011/02/ucenter-api-in-depth-4th/ 1.UCenter 这个当然是最基本的东西,安装起来也很简单,官方就 ...
- 循序渐进学.Net Core Web Api开发系列【16】:应用安全续-加密与解密
系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 应用安全除 ...
- Java对接拼多多开放平台API(加密上云等全流程)
前言 本文为[小小赫下士 blog]原创,搬运请保留本段,或请在醒目位置设置原文地址和原作者. 作者:小小赫下士 原文地址:Java对接拼多多开放平台API(加密上云等全流程) 本文章为企业ERP(I ...
- 关于《加密与解密》的读后感----对dump脱壳的一点思考
偶然翻了一下手机日历,原来今天是夏至啊,时间过的真快.ISCC的比赛已经持续了2个多月了,我也跟着比赛的那些题目学了2个月.......虽然过程很辛苦,但感觉还是很幸运的,能在大三的时候遇到ISCC, ...
- PHP 加密 和 解密 方法
关于Discuz的加密解密函数,相信大家都有所了解,该authcode函数可以说是对PHP界作出了重大的贡献,真的发觉discuz这个函数写的太精彩啦. 研究了一下这个算法,总的来说可以归纳为以下三点 ...
- .net中加密与解密
.Net中的加密解密 引言 在一些比较重要的应用场景中,通过网络传递数据需要进行加密以保证安全.本文将简单地介绍了加密解密的一些概念,以及相关的数字签名.证书,最后介绍了如何在.NET中对数据进行对称 ...
- C# 利用SQLite对.DB和.logdb加密和解密和SQLite创建数据库
1.最近研究了下利用SQLite为db文件简单的加密和解密 private static SQLiteConnection GetConnection() { SQLiteConnection con ...
- 如何对web.config进行加密和解密
http://blog.csdn.net/jf_jifei/article/details/6527390 在WEB网站开发过程中,如果我们将数据库连接字符串封装到.DLL文件中,将会给数据库和程序的 ...
- MVC项目实践,在三层架构下实现SportsStore-10,连接字符串的加密和解密
SportsStore是<精通ASP.NET MVC3框架(第三版)>中演示的MVC项目,在该项目中涵盖了MVC的众多方面,包括:使用DI容器.URL优化.导航.分页.购物车.订单.产品管 ...
随机推荐
- VS2015 发布常见问题
1. 发布时预编译 所示如下: 遇到的问题 使用abp时引用了System.Collections.Immutable.dll,但是项目编译一直出错, 排查: 查看项目引用,可看到System.Col ...
- 1、git基础介绍及远程/本地仓库、分支
1. Git基础介绍 基于Git进行开发时,首先需要将远程仓库代码clone到本地,即为本地仓库.后续大部分时间都是基于本地仓库上的分支进行编码,最后将本地仓库的代码合入远程仓库. 1.1. 远程仓库 ...
- Alpha 冲刺 (4/10)
目录 摘要 团队部分 个人部分 摘要 队名:小白吃 组长博客:hjj 作业博客:冲刺4 团队部分 后敬甲 过去两天完成了哪些任务 文字描述 主页部分图标的替换 -拍照按钮的设计和测试 GitHub代码 ...
- 认证 (authentication) 和授权 (authorization) 的区别
authorization 授权 authentication 身份认证 用户认证流程: 1.用户使用username和password登录 2.系统验证这个password对于该username是正 ...
- OpenStack实践系列④计算服务Nova
OpenStack实践系列④计算服务Nova 3.6 Nova控制节点的部署创建服务的凭证,完成下列步骤: 创建nova用户,并加入到service项目中,赋予admin权限 [root@node1 ...
- UML教程
1.前言 1.1 前言 本资料对UML1.5各种模型图的构成和功能进行说明,通过本资料的学习达到可以读懂UML模型图的目的.本资料不涉及模型图作成的要点等相关知识. 1.2 UML概述 1.2.1 ...
- oracle里面用sql做报表并带小计合计常用到的函数
1-- DECODE函数是Oracle PL/SQL是功能强大的函数之一,假设我们想给职员加工资,其标准是:工资在8000元以下的将加20%:工资在8000元以上的加15%,通常的做法是,先选出记录 ...
- Unix的哲学
先讲两个很老的小故事. 第一个故事. 有一家日本最大的化妆品公司,收到了用户的投诉.用户抱怨买来的肥皂盒是空的.这家公司为了防止再发生这样的事故,很辛苦地发明了一台X光检查器,能够透视每一个出货的肥皂 ...
- Java插入排序算法
直接插入排序算法 基本思想: 把n个待排序的元素看成一个有序表和一个无序表,开始时有序表中只有一个元素,无序表中有n-1个元素:排序过程即每次从无序表中取出第一个元素,将它插入到有序表中,使之成为新的 ...
- Oracle 高水位说明和释放表空间,加快表的查询速度
高水位的介绍 数据库运行了一段时间,经过一些列的删除.插入.更改操作有些表的高水位线就有可能和实际的表存储数据的情况相差特别多,为了提高检索该表的效率,建议对这些表进行收缩: 查找高水位线的表 查找表 ...