Java实现Base64加密算法

使用Java实现了Base64算法。和jdk自带的Base64实现比较过,加密后的字符串完全相同,解密后也没错误。效率没测过,应该不怎么样。通过实现这个,明白了Base64的原理,是做大的收获。

public class Base {
    private static final char[] map = {
            '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', '+', '/'
    };

    /**
     * 编码
     * @param plaintextBytes 明文的比特数组
     * @return 密文的比特数组
     */
    public byte[] encode(byte[] plaintextBytes) {
        StringBuilder builder = getBinaryStr(plaintextBytes);

        String[] strings = getBinaryStrArray(builder);

        return getByteArrayByMap(strings);
    }

    /**
     * 将明文的比特数组转换为二进制数的字符串
     * @param plaintextBytes 明文的比特数组
     * @return 二进制字符串
     */
    private StringBuilder getBinaryStr(byte[] plaintextBytes) {
        StringBuilder builder = new StringBuilder();

        for (byte b : plaintextBytes) {
            String s = Integer.toUnsignedString(b, 2);

            if (b < 0) {
                s = s.substring(24, 32);
            } else {
                int num = 8 - s.length();
                s = "0".repeat(num) + s;
            }

            builder.append(s);
        }

        return builder;
    }

    /**
     * 将二进制书的字符串按6个字节一组进行分组
     * @param builder 二进制书的字符串
     * @return 字符串数组
     */
    private String[] getBinaryStrArray(StringBuilder builder) {
        int length = builder.length();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            if (i != 0 && i % 6 == 0) {
                sb.append(" ");
            }
            sb.append(builder.charAt(i));
        }
        String s = sb.toString();

        return s.split(" ");
    }

    /**
     * 将字符串数组中的元素变为整数,对照映射表,转为对应字符的比特表示
     * @param strings 二进制的字符串数组
     * @return 比特数组
     */
    private byte[] getByteArrayByMap(String[] strings) {
        List<Byte> list = new ArrayList<>();

        int len = strings.length;
        for (int i = 0; i < len; i++) {
            String s = strings[i];
            String string;
            if (i == len - 1) {
                string = s + "0".repeat(6 - s.length());
            } else {
                string = s;
            }

            char c = map[Integer.parseInt(string, 2)];

            list.add((byte) c);
            if (i == len - 1 && !s.equals(string)) {
                if (s.length() == 2) {
                    list.add((byte) '=');
                    list.add((byte) '=');
                }
                if (s.length() == 4) {
                    list.add((byte) '=');
                }
            }
        }

        int size = list.size();
        byte[] bytes = new byte[size];

        for (int i = 0; i < size; i++) {
            bytes[i] = list.get(i);
        }

        return bytes;
    }

    /**
     * 编码。<br>
     * 输入字符串,输出字符串<br>
     * 使用UTF8进行编码与解码
     * @param plaintext 明文
     * @return 密文
     */
    public String encodeToString(String plaintext) {
        Charset utf8 = StandardCharsets.UTF_8;
        byte[] plaintextBytes = plaintext.getBytes(utf8);
        byte[] encode = encode(plaintextBytes);
        return new String(encode, utf8);
    }



    // --------------------------------------解码-----------------------------------------------------------

    /**
     * 解码
     * @param ciphertextBytes 密文的比特数组
     * @return 明文的比特数组
     */
    public byte[] decode(byte[] ciphertextBytes) {
        StringBuilder builder = getByteStringWithoutEqual(ciphertextBytes);

        StringBuilder sb = new StringBuilder();
        int length = builder.length();
        for (int i = 0; i < length; i++) {
            char c = builder.charAt(i);

            if (i != 0 && i % 8 == 0) {
                sb.append(" ");
            }
            sb.append(c);
        }

        String[] strings = sb.toString().split(" ");
        List<Byte> list = new ArrayList<>();
        for (String s : strings) {
            list.add((byte) Integer.parseInt(s, 2));
        }

        int size = list.size();
        byte[] dest = new byte[size];
        for (int i = 0; i < size; i++) {
            dest[i] = list.get(i);
        }

        return dest;
    }

    private StringBuilder getByteStringWithoutEqual(byte[] ciphertextBytes) {
        int length = ciphertextBytes.length;

        if (length < 4) {
            throw new RuntimeException("密文比特数组错误!");
        }

        int num = 0;

        if (ciphertextBytes[length - 1] == 61) num++;
        if (ciphertextBytes[length - 2] == 61) num++;

        int index = length - 1 - num;

        byte ciphertextByte = ciphertextBytes[index];
        ciphertextBytes[index] = (byte) map[getIndexByByte(ciphertextByte) >> (num * 2)];

        byte[] bytes = Arrays.copyOf(ciphertextBytes, length - num);




        StringBuilder builder = new StringBuilder();

        for (int i = 0; i < bytes.length; i++) {
            byte b = bytes[i];

            int indexByByte = getIndexByByte(b);
            String s = Integer.toUnsignedString(indexByByte, 2);
            if (i < bytes.length - 1) {
                s = "0".repeat(6 - s.length()) + s;
            } else {
                s = "0".repeat(6 - num * 2 - s.length()) + s;
            }

            builder.append(s);
        }

        return builder;
    }

    private int getIndexByByte(byte b) {
        if (b >= 65 && b <= 90) {
            return (b - 65);
        }
        if (b >= 97 && b <= 122) {
            return (b - 97) + 26;
        }
        if (b >= 48 && b <= 57) {
            return (b - 48) + (26 + 26);
        }
        if (b == 43) return 62;
        if (b == 47) return 63;

        throw new RuntimeException("密文中含有Base64编码后不应该存在的字符:" + b);
    }

    /**
     * 解码。<br>
     * 输入字符串,输出字符串<br>
     * 使用UTF8进行编码与解码
     * @param ciphertext 密文
     * @return 明文
     */
    public String decodeToString(String ciphertext) {
        Charset utf8 = StandardCharsets.UTF_8;
        byte[] bytes = ciphertext.getBytes(utf8);
        byte[] decode = decode(bytes);
        return new String(decode, utf8);
    }


}

在编写的过程中出过错,有时候加密去掉最后的=的最后一位与标准实现不一样。找了很久才发现,在标准实现中最后那个二进制数是在末尾补0,我是在前面补0。我觉得前面补0更好,毕竟不改变最后一位数的大小,不知道是否有问题?

还有就是,如果自定义编码表中字符的顺序,是否可以把Base64变为对称加密算法而编码表就是密匙呢?

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值