3. 深入研究 UCenter API 之 加密與解密(轉載)

來源:互聯網
上載者:User

標籤:類型   輸入   計算   box   pen   tool   net   破解   特殊字元   

 

  • 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 /// 計算Md5106 /// </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         else119         { 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轉DataTime182 /// </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轉PhpTime193 /// </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 AuthCodeMethod230 {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 之 加密與解密(轉載)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.