用 Python 重寫 PHP 加密解密算法 authcode

剛剛讀了一遍 Discuz 系列產品中普遍使用的加密解密算法 authcode,受益不淺,真是設計巧妙。 php

爲了真的理解其中的想法,用 Python 改寫了一遍。  
小白程序員一枚,學藝不精,望路過的各位大俠多多指點,不吝賜教。 html

PS:在法律層面上,原算法的使用是否受限制尚不清楚。
原文:碼廄 - 用 Python 改寫 PHP 加密解密算法 authcode python

Python 代碼: 程序員

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import time
import base64
import hashlib


class AuthCode(object):

    @classmethod
    def encode(cls, string, key, expiry=0):
        """
        編碼
        @param string: 帶編碼字符串
        @param key: 密鑰
        @return:加密字符串
        """
        return cls._auth_code(string, 'ENCODE', key, expiry)

    @classmethod
    def decode(cls, string, key, expiry=0):
        """
        解碼
        @param string: 待解碼字符串
        @param key: 密鑰
        @return:原始字符串
        """
        return cls._auth_code(string, 'DECODE', key, expiry)

    @staticmethod
    def _md5(source_string):
        return hashlib.md5(source_string).hexdigest()

    @classmethod
    def _auth_code(cls, input_string, operation='DECODE', key='', expiry=3600):
        """
        編碼/解碼
        @param input_string: 原文或者密文
        @param operation: 操做(加密或者解密,默認是解密)
        @param key: 密鑰
        @param expiry: 密文有效期,單位是秒,0 表示永久有效
        @return: 處理後的原文或者通過 base64_encode 處理後的密文
        """

        # ----------------------- 獲取隨機密鑰 -----------------------

        rand_key_length = 4
        # 隨機密鑰長度 取值 0-32
        # 能夠令密文無任何規律,即使是原文和密鑰徹底相同,加密結果也會每次不一樣,增大破解難度
        # 值越大,密文變更規律越大,密文變化 = 16 的 ckey_length 次方,若是爲 0,則不產生隨機密鑰

        key = cls._md5(key)
        key_a = cls._md5(key[:16])
        key_b = cls._md5(key[16:])
        if rand_key_length:
            if operation == 'DECODE':
                key_c = input_string[:rand_key_length]
            else:
                key_c = cls._md5(str(time.time()))[-rand_key_length:]
        else:
            key_c = ''

        crypt_key = key_a + cls._md5(key_a + key_c)

        if operation == 'DECODE':
            handled_string = base64.b64decode(input_string[rand_key_length:])
        else:
            expiration_time = expiry + int(time.time) if expiry else 0
            handled_string = '%010d' % expiration_time + cls._md5(input_string + key_b)[:16] + input_string

        rand_key = list()
        for i in xrange(256):
            rand_key.append(ord(crypt_key[i % len(crypt_key)]))

        # ----------------------------------------------------------

        box = range(256)
        j = 0
        for i in xrange(256):
            j = (j + box[i] + rand_key[i]) % 256
            tmp = box[i]
            box[i] = box[j]
            box[j] = tmp

        #for i in xrange(len(box)):
        #    print str(box[i]).rjust(5),
        #    if ((i + 1) % 10) == 0:
        #        print ''

        result = ''
        a = 0
        j = 0
        for i in xrange(len(handled_string)):
            a = (a + 1) % 256
            j = (j + box[a]) % 256
            tmp = box[a]
            box[a] = box[j]
            box[j] = tmp
            result += chr(ord(handled_string[i])^(box[(box[a]+box[j])%256]))

        if operation == 'DECODE':
            if (int(result[:10]) == 0 or (int(result[:10]) - time.time() > 0)) and \
                    (result[10:26] == cls._md5(result[26:] + key_b)[:16]):
                output_string = result[26:]
            else:
                output_string = ''
        else:
            output_string = key_c + base64.b64encode(result)

        return output_string

if __name__ == '__main__':
    src = 'My name is Hu Ang, I\'m a programmer.'
    key = 'fr1e54b8t4n4m47'
    encoded_string = AuthCode.encode(src, key)
    decoded_string = AuthCode.decode(encoded_string, key)
    print 'Source String:', src
    print 'After Encode :', encoded_string
    print 'After Decode :', decoded_string
    print '----------------------------------------------'
    # 經過 PHP 方式加密獲得的一個密文,而後用 Python 解密
    # $source_string = "My name is Hu Ang.";
    # $secret_key = 'fr1e54b8t4n4m47';
    # $encoded_string = authcode($source_string, 'ENCODE', $secret_key, 0);
    php_encoded_string = '82798mEQ6ouQo1rFrbSXT5EHVjZ0gH0WuuZDXd9us/q44JAhmPwBAFZqvwXhvnjgUOJ+5aYh5ed8zNL3cjTOGBY='
    print 'Decode string encoded via php:', AuthCode.decode(php_encoded_string, key)
    # PS:Python 方式加密過的字符串經過 PHP 解析也成功了。

PHP 代碼: 算法


<?php

class AuthCode {

    public static function encode($str, $key) {
        return self::_auth_code($str, 'ENCODE', $key, 0);
    }

    public static function decode($str, $key) {
        return self::_auth_code($str, 'DECODE', $key, 0);
    }

    public static function _auth_code($string, $operation = 'DECODE', $key = '', $expiry = 3600) {
        /***
         * 隨機密鑰長度 取值 0-32;
         * 加入隨機密鑰,能夠令密文無任何規律,即使是原文和密鑰徹底相同,加密結果也會每次不一樣,增大破解難度。
         * 取值越大,密文變更規律越大,密文變化 = 16 的 $ckey_length 次方
         * 當此值爲 0 時,則不產生隨機密鑰
         */
        $ckey_length = 4;
        
        $key = md5($key);
        $keya = md5(substr($key, 0, 16));
        $keyb = md5(substr($key, 16, 16));
        $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';

        $cryptkey = $keya.md5($keya.$keyc);
        $key_length = strlen($cryptkey);

        $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
        $string_length = strlen($string);

        $result = '';
        $box = range(0, 255);

        $rndkey = array();
        for($i = 0; $i <= 255; $i++) {
            $rndkey[$i] = ord($cryptkey[$i % $key_length]);
        }

        for($j = $i = 0; $i < 256; $i++) {
            $j = ($j + $box[$i] + $rndkey[$i]) % 256;
            $tmp = $box[$i];
            $box[$i] = $box[$j];
            $box[$j] = $tmp;
        }

        for($a = $j = $i = 0; $i < $string_length; $i++) {
            $a = ($a + 1) % 256;
            $j = ($j + $box[$a]) % 256;
            $tmp = $box[$a];
            $box[$a] = $box[$j];
            $box[$j] = $tmp;
            $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
        }

        if($operation == 'DECODE') {
            if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
                return substr($result, 26);
            } else {
                return '';
            }
        } else {
            return $keyc.base64_encode($result);
        }
    }

}

$source_string = "My name is Hu Ang, I'm a programmer.";
$secret_key = 'fr1e54b8t4n4m47';
echo 'Source String: ' . $source_string . "\n";
$encoded_string = AuthCode::encode($source_string, $secret_key);
echo 'After Encode : ' . $encoded_string . "\n";
$decoded_string = AuthCode::decode($encoded_string, $secret_key);
echo 'After Decode : ' . $decoded_string . "\n";
echo "----------------------------------------------\n";
$python_encoded_string = "88fcnCU6Wb+6LPREpYrhB3NcKS3OU+V8FqQ4uUklvZR170HWlyBLtPKEtP9Ui/qZp1ZqEhF9f5k6XBDixsVgEKk=";
echo 'Decode string encoded via python: ' . AuthCode::decode($python_encoded_string, $secret_key);
相關文章
相關標籤/搜索