术→技巧, 法→原理, 研发, 算法实现

加密解密算法之Base64x

钱魏Way · · 306 次浏览

在程序开发中,我们会经常使用Base64对字符串或二进制数据进行编码解码,虽然字符串在经过Base64编码后看上去很像被加密过,但是并不能成为加密解密算法,原因是Base64的编码解码过程非常的简单,且编码解码的流程的算法是公开的,起不到任何加密的效果。Base64的设计目的也并不是为了加密。

所谓Base64,就是说选出64个字符—-小写字母a-z、大写字母A-Z、数字0-9、符号”+”、”/”(再加上作为垫字的”=”,实际上是65个字符)—-作为一个基本字符集。然后,其他所有符号都转换成这个字符集中的字符。具体来说,转换方式可以分为四步。

  • 将每三个字节作为一组,一共是24个二进制位。
  • 将这24个二进制位分为四组,每个组有6个二进制位。
  • 在每组前面加两个00,扩展成32个二进制位,即四个字节。
  • 根据下表,得到扩展后的每个字节的对应符号,这就是Base64的编码值。

base64编码表

0x0  0x1  0x2  0x3  0x4  0x5  0x6  0x7  0x8  0x9  0xa  0xb  0xc  0xd  0xe  0xf  0x10  0x11  0x12  0x13  0x14  0x15  0x16  0x17  0x18  0x19  0x1a  0x1b  0x1c  0x1d  0x1e  0x1f  0x20  0x21  0x22  0x23  0x24  0x25  0x26  0x27  0x28  0x29  0x2a  0x2b  0x2c  0x2d  0x2e  0x2f  0x30  0x31  0x32  0x33  0x34  0x35  0x36  0x37  0x38  0x39  0x3a  0x3b  0x3c  0x3d  0x3e  0x3f
0    1    2    3    4    5    6    7    8    9    10   11   12   13   14   15   16    17    18    19    20    21    22    23    24    25    26    27    28    29    30    31    32    33    34    35    36    37    38    39    40    41    42    43    44    45    46    47    48    49    50    51    52    53    54    55    56    57    58    59    60    61    62    63
A    B    C    D    E    F    G    H    I    J    K    L    M    N    O    P    Q     R     S     T     U     V     W     X     Y     Z     a     b     c     d     e     f     g     h     i     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     +     /

因为,Base64将三个字节转化成四个字节,因此Base64编码后的文本,会比原文本大出三分之一左右。

从上面的编码表中,我们可以看到采用公共的Base64并不能其实简单的加密和解密,但可以通过适当的修改 Base64 来实现简单加密与解密。尽管不及专门的对称加密和非对称加密的安全性,但性能远胜于专门的加密解密过程,且可以实现可见字符的传输。适用于安全要求不高,对密文要求可见,且密文长度受限的场景。

Base64x的原理非常的简单,就是将编码表的顺序进行打乱,再进行编码,具体代码如下:

public class Base64x {
    private static final String DEFAULT_ENCODING_TABLES = "ABCDEFGHIUVWXYZ+abcdefJKLMNOPQRSTghijk016789/lmnopqrs2345tuvwxyz";
    private String encodingTables;

    public Base64x(int offset) {
        this.encodingTables = "";
        final int move = offset % DEFAULT_ENCODING_TABLES.length();
        this.encodingTables = DEFAULT_ENCODING_TABLES.substring(move) + DEFAULT_ENCODING_TABLES.substring(0, move);
    }

    public Base64x(String s, int offset) {
        this.encodingTables = "";
        final int move = offset % s.length();
        this.encodingTables = s.substring(move) + s.substring(0, move);
    }

    /**
     * encode
     *
     * coverts a byte array to a string populated with base64 digits. It steps
     * through the byte array calling a helper methode for each block of three
     * input bytes
     *
     * @param raw
     *            The byte array to encode
     * @return A string in base64 encoding
     */
    public String encode(byte[] raw) {
        StringBuffer encoded = new StringBuffer();
        for (int i = 0; i < raw.length; i += 3) {
            encoded.append(encodeBlock(raw, i));
        }
        return encoded.toString();
    }

    /**
     * encodeBlock
     *
     * creates 4 base64 digits from three bytes of input data. we use an
     * integer, block, to hold the 24 bits of input data.
     *
     * @return An array of 4 characters
     */
    protected char[] encodeBlock(byte[] raw, int offset) {
        int block = 0;
        // how much space left in input byte array
        int slack = raw.length - offset - 1;
        // if there are fewer than 3 bytes in this block, calculate end
        int end = (slack >= 2) ? 2 : slack;
        // convert signed quantities into unsigned
        for (int i = 0; i <= end; i++) {
            byte b = raw[offset + i];
            int neuter = (b < 0) ? b + 256 : b;
            block += neuter << (8 * (2 - i));
        }
        // extract the base64 digets, which are six bit quantities.
        char[] base64 = new char[4];
        for (int i = 0; i < 4; i++) {
            int sixbit = (block >>> (6 * (3 - i))) & 0x3f;
            base64[i] = getChar(sixbit);
        }
        // pad return block if needed
        if (slack < 1)
            base64[2] = '=';
        if (slack < 2)
            base64[3] = '=';
        // always returns an array of 4 characters
        return base64;
    }

    /**
     * decode
     *
     * convert a base64 string into an array of bytes.
     *
     * @param base64
     *            A String of base64 digits to decode.
     * @return A byte array containing the decoded value of the base64 input
     *         string
     */
    public byte[] decode(String base64) {
        // how many padding digits?
        int pad = 0;
        for (int i = base64.length() - 1; base64.charAt(i) == '='; i--) {
            pad++;
        }
        // we know know the lenght of the target byte array.
        int length = base64.length() * 6 / 8 - pad;
        byte[] raw = new byte[length];
        int rawIndex = 0;
        // loop through the base64 value. A correctly formed
        // base64 string always has a multiple of 4 characters.
        for (int i = 0; i < base64.length(); i += 4) {
            int block = (getValue(base64.charAt(i)) << 18) + (getValue(base64.charAt(i + 1)) << 12) + (getValue(base64.charAt(i + 2)) << 6)
                    + (getValue(base64.charAt(i + 3)));
            // based on the block, the byte array is filled with the
            // appropriate 8 bit values
            for (int j = 0; j < 3 && rawIndex + j < raw.length; j++)
                raw[rawIndex + j] = (byte) ((block >> (8 * (2 - j))) & 0xff);
            rawIndex += 3;
        }
        return raw;
    }

    /**
     * getChar
     *
     * encapsulates the translation from six bit quantity to base64 digit
     */
    protected char getChar(int sixBit) {
        return encodingTables.charAt(sixBit);
    }

    /**
     * getValue
     *
     * translates from base64 digits to their 6 bit value
     */
    protected int getValue(char c) {
        if (c == '=')
            return 0;
        return encodingTables.indexOf(c);
    }
}

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注