最近對(duì)加密比較感興趣,其中有一段加密算法的php的函數(shù),著實(shí)讓我花費(fèi)的了一些時(shí)間才弄明白這個(gè)坑。由此記錄下來(lái),并闡述發(fā)現(xiàn)及解決問題本質(zhì)的一點(diǎn)皮毛,當(dāng)做拋磚引玉。
function hmacsha1($key, $data) {
$blocksize = 64;
$hashfunc = 'sha1';
if (strlen($key) > $blocksize)
$key = pack('H*', $hashfunc($key));
$key = str_pad($key, $blocksize, chr(0x00));
$ipad = str_repeat(chr(0x66), $blocksize);
$opad = str_repeat(chr(0x55), $blocksize);
$hmac = pack ('H*',(此處為$key,$ipad,$opad的一些邏輯運(yùn)算))
//本文假設(shè)為$hmac = pack('H*', $hashfunc($key ^ $ipad.$key^$opad));
return bin2hex($hmac);
}
科普幾個(gè)php的函數(shù)
pack(format,args) 函數(shù)把數(shù)據(jù)裝入一個(gè)二進(jìn)制字符串
| 參數(shù) | 描述 |
|---|---|
| format | 必需。規(guī)定在包裝數(shù)據(jù)時(shí)所使用的格式。 |
| args+ | 可選。規(guī)定被包裝的一個(gè)或多個(gè)參數(shù)。 |
代碼中pack('H*', )是16進(jìn)制打包。(其它參數(shù)類型)
example1:
<?php
echo pack("C*",80,72,80);
?>
輸出:PHP
C*表示不帶有符號(hào)的字符,此處為10進(jìn)制,80,72,80對(duì)應(yīng)的ASCII碼為PHP
example2:
<?php
echo pack("H*",50,48,50);
?>
輸出:PHP
H*表示十六進(jìn)制字符串,此處為16進(jìn)制,0x50 = 80; 0x48 = 72;結(jié)果同上。
bin2hex() 函數(shù)把 ASCII 字符的字符串轉(zhuǎn)換為十六進(jìn)制值。字符串可通過(guò)使用 pack() 函數(shù)再轉(zhuǎn)換回去。unpack() 函數(shù)從二進(jìn)制字符串對(duì)數(shù)據(jù)進(jìn)行解包。
pack()和unpack()是成對(duì)的,pack加密數(shù)據(jù),unpack()解密數(shù)據(jù)。這兩個(gè)功能很強(qiáng)大,但是需要輸入一些格式化的東西。感覺bin2hex()是在unpack()上封裝了一下,使用起來(lái)更方便。
example1:
<?php
echo bin2hex("504850");
?>
輸出:PHP
example2:
<?php
echo unpack('H*',"504850");
?>
輸出:PHP
sha1為hash算法中的sha1加密
strlen($key)求字符串$key的長(zhǎng)度
str_pad() 函數(shù)把字符串填充為新的長(zhǎng)度。
| 參數(shù) | 描述 |
|---|---|
| string | 必需。規(guī)定要填充的字符串。 |
| length | 必需。規(guī)定新的字符串長(zhǎng)度。如果該值小于字符串的原始長(zhǎng)度,則不進(jìn)行任何操作 |
| pad_string | 可選。規(guī)定供填充使用的字符串。默認(rèn)是空白。 |
| pad_type | 可選。規(guī)定填充字符串的哪邊。可能的值:STR_PAD_BOTH - 填充字符串的兩側(cè)。如果不是偶數(shù),則右側(cè)獲得額外的填充。STR_PAD_LEFT - 填充字符串的左側(cè)。*STR_PAD_RIGHT - 填充字符串的右側(cè)。默認(rèn)。 |
str_repeat() 函數(shù)把字符串重復(fù)指定的次數(shù)。
chr()進(jìn)制數(shù)字字符轉(zhuǎn)化ASCII字符
example:
<?php
echo str_repeat(chr(0x66), 3);
?>
輸出:fff
ASCII中0x66--->f
至此上面的php一部分加密的算法應(yīng)該是可以看懂了,開始o(jì)c的實(shí)現(xiàn)
剛剛開始沒有想做太多的優(yōu)化,用面向?qū)ο蟮乃枷雭?lái)直接寫,
具體的實(shí)現(xiàn)部分就不貼代碼了,我直接寫出方法的定義就可以。
實(shí)現(xiàn)思路很簡(jiǎn)單,對(duì)應(yīng)的實(shí)現(xiàn)各個(gè)php中的函數(shù)就可以了.
可以新建一個(gè)Security的類
- (NSString *)pack:(NSString *)str;
- (NSString *)unpack:(NSString *)str;
- (NSString *)hashFunc:(NSString *)str;
- (NSString *)str_pad:(NSString *)str;
- (NSString *)hashfunc:(NSString *)str;
- (NSString *)str_repeat:(NSString *)str1 size:(NSIntger)size;
- (NSString *)hmacsha1:(NSString *)key data:(NSString *)data;
這個(gè)是相應(yīng)的邏輯運(yùn)算,比如同或 異或等等,具體的邏輯運(yùn)算相應(yīng)的計(jì)算。
主要是因?yàn)閛c無(wú)法直接進(jìn)行邏輯運(yùn)算
example:0x36->6; 0x6b->[;=> 6 ^ [= m;
php 底層優(yōu)化,6和[為二進(jìn)制數(shù)據(jù)00110110(6)和01011011([)的存儲(chǔ)方式,
然后進(jìn)行^運(yùn)算
01101101
00110110(6)
^
01011011([)
----------------
01101101(m)
這個(gè)方法里面為了邏輯運(yùn)算會(huì)將字符遍歷獲取char類型,然后計(jì)算
- (NSString *)str1:(NSString *)str1 str2:(NSString *)str2;
基本上實(shí)現(xiàn)上面的方法就可以大功告成了,但是總會(huì)有意向不到的事情。
用以下一個(gè)例子來(lái)說(shuō)明遇到的pack()和unpack()實(shí)現(xiàn)的問題。
php example:
<?php
for($a = 0; $a < 256; $a++) {
$b = pack('S*', $a);
$c = unpack('C*', $b);
echo "pack".$a."=".$b."******unpack".$b."=".$c[1]."\n";
}
oc example:
//這樣會(huì)crash,characterAtIndex越界了,
//是因?yàn)闀?huì)有不可見字符,str可能為nil,而nil轉(zhuǎn)為ASCII碼會(huì)出現(xiàn)問題
for (int i = 0; i < 256; i++) {
NSString *str = [NSString stringWithFormat:@"%c", (char)i];
NSLog(@"pack(%d) = %c,unpack(%c)=%d", i,(char)i, (char)i,[str characterAtIndex:0]);
}
?>
發(fā)生上面的情況著實(shí)沒有想到,原先以為各個(gè)平臺(tái)的ASCII應(yīng)該會(huì)是完全一樣的,以為ASCII轉(zhuǎn)字符和字符轉(zhuǎn)ASCII是完全一樣的,可以相互轉(zhuǎn)化而沒有任何差異的。但是事實(shí)不是,各個(gè)平臺(tái)有一些差異,比如php這個(gè)當(dāng)為不可見字符是有問號(hào)表示,然后用bin2hex()解碼還能夠轉(zhuǎn)回來(lái),底層的優(yōu)化我認(rèn)為類似指針一樣,存儲(chǔ)的數(shù)據(jù)是沒有變化的?;跀?shù)據(jù)存儲(chǔ)的二進(jìn)制數(shù)據(jù)是沒有變化的,減少不必要的數(shù)據(jù)格式化,設(shè)計(jì)了以下的方法。
- (NSMutableData *)hashFunc:(NSData *)data;
- (NSString *)str_repeat:(NSInteger)size str:(NSString *)str;
//異或函數(shù)
- (NSData *)str1:(NSData *)str1Data str2:(NSData *)str2Data;
- (NSMutableData *)appendData:(NSData *)targetData data:(NSData *)data;
- (NSString *)convertPack:(NSData *)data;
- (NSData *) stringToHexData:(NSString *)hexStr;