discuz 经典php加密解密函数 authcode

Eave 2015.10.19 13:00

康盛的 authcode 函数可以说对中国的PHP界作出了重大贡献。包括康盛自己的产品,以及大部分中国使用PHP的公司都用这个函数进行加密,authcode 是使用异或运算进行加密和解密。

package com.conline.util;

import java.util.Calendar;
import java.util.Random;

public class AuthCode
{
    private static final String AUTHKEY = "CAYU-DLOFU-IFENG-VENE";

    public enum authcodeMode
    {
        Encode, Decode
    };

    /**
     * 字符串加密
     * @param source
     * @return
     */
    public static String encode(String source)
    {
        return authcode(source, AUTHKEY, authcodeMode.Encode, 0);
    }

    /**
     * 字符串加密
     * @param source    原始字符串
     * @param key       密钥
     * @return
     */
    public static String encode(String source, String key)
    {
        return authcode(source, key, authcodeMode.Encode, 0);
    }

    /**
     * 字符串加密
     * @param source    原始字符串
     * @param key       密钥
     * @param expiry
     * @return
     */
    public static String encode(String source, String key, int expiry)
    {
        return authcode(source, key, authcodeMode.Encode, expiry);
    }

    /**
     * 字符串解密
     * @param source
     * @return
     */
    public static String decode(String source)
    {
        return authcode(source, AUTHKEY, authcodeMode.Decode, 0);
    }

    /**
     * 字符串解密
     * @param source     原始字符串
     * @param key        密钥
     * @return
     */
    public static String decode(String source, String key)
    {
        return authcode(source, key, authcodeMode.Decode, 0);
    }

    /**
     * 使用 变形的 rc4 编码方法对字符串进行加密或者解密
     * @param source     原始字符串
     * @param key        密钥
     * @param operation  操作 加密还是解密
     * @param expiry     加密字串过期时间
     * @return
     */
    private static String authcode(String source, String key, authcodeMode operation, int expiry)
    {
        String codeString = null;
        try
        {
            if(source != null && key != null)
            {
                int cKeyLength = 4;
                String keya, keyb, keyc, cryptkey, result;

                key = Security.md5(key);

                keya = Security.md5(cutString(key, 0, 16));

                keyb = Security.md5(cutString(key, 16, 16));

                keyc = cKeyLength > 0 ? (operation == authcodeMode.Decode ? cutString(source, 0, cKeyLength) : randomString(cKeyLength)) : "";

                cryptkey = keya + Security.md5(keya + keyc);

                if (operation == authcodeMode.Decode)
                {
                    byte[] temp;

                    temp = Base64.decode(cutString(source, cKeyLength));
                    result = new String(RC4(temp, cryptkey));
                    if (cutString(result, 10, 16).equals(cutString(Security.md5(cutString(result, 26) + keyb), 0, 16)))
                    {
                        codeString = cutString(result, 26);
                    }
                    else
                    {
                        temp = Base64.decode(cutString(source + "=", cKeyLength));
                        result = new String(RC4(temp, cryptkey));
                        if (cutString(result, 10, 16).equals(cutString(Security.md5(cutString(result, 26) + keyb), 0, 16)))
                        {
                            codeString = cutString(result, 26);
                        }
                        else
                        {
                            temp = Base64.decode(cutString(source + "==", cKeyLength));
                            result = new String(RC4(temp, cryptkey));
                            if (cutString(result, 10, 16).equals(cutString(Security.md5(cutString(result, 26) + keyb), 0, 16)))
                            {
                                codeString = cutString(result, 26);
                            }
                        }
                    }
                }
                else
                {
                    source = "0000000000" + cutString(Security.md5(source + keyb), 0, 16) + source;
                    byte[] temp = RC4(source.getBytes("UTF-8"), cryptkey);
                    codeString = keyc + Base64.encodeBytes(temp);
                }
            }
        }
        catch (Exception e)
        {
            System.err.println(e);
        }
        return codeString;
    }

    /**
     * 字符串截取
     * @param str
     * @param startIndex
     * @param length
     * @return
     */
    public static String cutString(String str, int startIndex, int length)
    {
        if(startIndex >= 0)
        {
            if (length < 0)
            {
                length = length * -1;
                if(startIndex - length < 0)
                {
                    length = startIndex;
                    startIndex = 0;
                }
                else
                {
                    startIndex = startIndex - length;
                }
            }

            if(startIndex > str.length())
            {
                return "";
            }
        }
        else
        {
            if(length < 0)
            {
                return "";
            }
            else
            {
                if(length + startIndex > 0)
                {
                    length = length + startIndex;
                    startIndex = 0;
                }
                else
                {
                    return "";
                }
            }
        }

        if (str.length() - startIndex < length)
        {
            length = str.length() - startIndex;
        }

        return str.substring(startIndex, startIndex + length);
    }

    /**
     * 字符串截取
     * @param str
     * @param startIndex
     * @return
     */
    public static String cutString(String str, int startIndex)
    {
        return cutString(str, startIndex, str.length());
    }

    /**
     * 用于 RC4 处理密码
     * @param pass    密码字串
     * @param kLen    密钥长度,一般为 256
     * @return
     */
    static private byte[] getKey(byte[] pass, int kLen)
    {
        byte[] mBox = new byte[kLen];

        for (int i = 0; i < kLen; i++)
        {
            mBox[i] = (byte) i;
        }

        int j = 0;
        for (int i = 0; i < kLen; i++)
        {

            j = (j + (int) ((mBox[i] + 256) % 256) + pass[i % pass.length]) % kLen;

            byte temp = mBox[i];
            mBox[i] = mBox[j];
            mBox[j] = temp;
        }

        return mBox;
    }

    /**
     * 生成随机字符
     * @param lens    随机字符长度
     * @return
     */
    public static String randomString(int lens)
    {
        char[] CharArray = {\'a\', \'b\', \'c\', \'d\', \'e\', \'f\', \'g\', \'h\', \'j\', \'k\', \'l\', \'m\', \'n\', \'o\', \'p\', \'q\', \'r\', \'s\', \'t\', \'u\', \'v\', \'w\', \'x\', \'y\', \'z\', \'0\', \'1\', \'2\', \'3\', \'4\', \'5\', \'6\', \'7\', \'8\', \'9\'};
        int clens = CharArray.length;
        String sCode = "";
        Random random = new Random();
        for(int i = 0; i < lens; i++)
        {
            sCode += CharArray[Math.abs(random.nextInt(clens))];
        }
        return sCode;
    }



    /**
     * RC4 原始算法
     * @param input    原始字串数组
     * @param pass     密钥
     * @return
     */
    private static byte[] RC4(byte[] input, String pass)
    {
        if(input == null || pass == null)
        {
            return null;
        }

        byte[] output = new byte[input.length];
        byte[] mBox = getKey(pass.getBytes(), 256);

        // 加密
        int i = 0;
        int j = 0;

        for(int offset = 0; offset < input.length; offset++)
        {
            i = (i + 1) % mBox.length;
            j = (j + (int) ((mBox[i] + 256) % 256)) % mBox.length;

            byte temp = mBox[i];
            mBox[i] = mBox[j];
            mBox[j] = temp;
            byte a = input[offset];

            // mBox[j] 一定比 mBox.Length 小,不需要在取模
            byte b = mBox[(toInt(mBox[i]) + toInt(mBox[j])) % mBox.length];
            output[offset] = (byte) ((int) a ^ (int) toInt(b));
        }
        return output;
    }

    public static int toInt(byte b)
    {
        return (int) ((b + 256) % 256);
    }

    public long getUnixTimestamp()
    {
        Calendar cal = Calendar.getInstance();
        return cal.getTimeInMillis() / 1000;
    }
}