java-通过异或(xor)实现快速加解密的方法

java-通过异或(xor)实现快速加解密的方法

一、背景

首次接触异或加解密最早是在宽带不普及的时候,当时用的是某个在线音乐软件看MV,上网不易想着把MV下载下来,日后离线看。
可是在缓存目录把视频复制到其它目录后,发现不使用他们的播放器还无法播放。
查了下当时有对各家视频的转码程序,找个个试了下,直接转码成功。
当时也比较好奇,所以查了下相关资料。发现其实逻辑挺简单就是使用了一个固定值(暂定37吧),对流文件的每个字节执行“异或”运算。
对明文第一次与37异或后的值就是密文了,对密文再次与37异或,就能把它变回明文。
当时也没有深究原因,能用就完了呗!最近遇到个功能需要实现对大文件的快速加解密,脑海里突然蹦出来了这个算法。

二、异或定义

  • 运算规则
    • 如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
  • 更容易记忆的规则(半加运算)
    • 异或也叫半加运算,其运算法则相当于不带进位的二进制加法:
    • 二进制下用1表示真,0表示假,则异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1),这些法则与加法是相同的,只是不带进位。
    • 所以异或常被认作不进位加法。

异或不常用,几天不不碎碎念几遍规则,有个2周就把它忘干净了 _!!!。

三、异或运算法则

  • 归零律:a^a=0
  • 恒等律:a^0=a
  • 交换律: a^b = b^a
  • 结合律:abc = a(bc) = (ab)c
  • 自反律:aba = b

java中异或运算符为 ^

四、异或加密原理

  • 异或自反律:aba = b

    看完异或运算法则之后就能明白,加解密用的是异或的自反律,这是一个神奇的性质。
  • 例如:不引入第三变量,交换两个数值?
public static void main(String[] args) {
    int a = 88;
    int b = 99;
    System.out.println(MessageFormat.format("交换前\ta={0}\tb={1}",a,b));
    a = a^b;
    b = a^b;
    a = a^b;
    System.out.println(MessageFormat.format("交换后\ta={0}\tb={1}",a,b));
}
输出:
交换前	a=88	b=99
交换后	a=99	b=88
  • 内容加密

    在异或自反律(aba = b)中,如果把a看做加密的盐值,b看做明文,使用盐值a对明文b连续执行两次“异或”操作即可得到原来的明文b内容。通过该定律我们就可以实现对二进制文件的简单加解密。
    系统中存在的物理文件,都是由很多个byte组成的,我们只需按位对物理文件进行读写加密就可以实现加解密。
    盐值的长短决定了密文的破解难易。
    比如可以按1位去异或,也可以按4位去异或,等等!

五、加密算法实现

  • 读取使用InputStream类
  • 写入使用RandomAccessFile类

    RandomAccessFile是Java输入/输出流体系中功能最丰富的文件内容访问类,它提供了众多的方法来访问文件内容,它既可以读取文件内容,也可以向文件输出数据。与普通的输入/输出流不同的是,RandomAccessFile支持"随机访问"的方式,程序可以直接跳转到文件的任意地方来读写数据。

刚开始使用的是OutputStream类写内容,发现调用write方法后,文件内容变小了_!!!

  • 代码实现
    public static void main(String[] args) throws Exception {
        //你好世界!hello,world!
        String fileName = "E:\\downloads\\text.txt";
        byte salt = 10;
        int readLength = 128; //读取长度
        byte[] cipherBytes = new byte[readLength];
        //读流
        try(InputStream fr = new FileInputStream(fileName)){
            byte[] buffer = new byte[readLength];
            fr.read(buffer,0,buffer.length);
            //按位执行异或
            for(int i=0;i<buffer.length;i++){
                byte b = buffer[i];
                b ^= salt;
                //byte a = b ^ salt; 不能用这种方法,因为异或后的值是int类型
                cipherBytes[i]=b;
            }
        }
        File file = new File(fileName);
        System.out.println("执行前:file.len="+file.length());
        //写流
        try(RandomAccessFile src = new RandomAccessFile(file,"rws")) {
            src.write(cipherBytes,0,cipherBytes.length);
        }
        file = new File(fileName);
        System.out.println("执行后:file.len="+file.length());
    }
  • text.txt内容
你好世界!hello,world!
你好世界!hello,world!
你好世界!hello,world!
你好世界!hello,world!
你好世界!hello,world!
你好世界!hello,world!
你好世界!hello,world!
你好世界!hello,world!
你好世界!hello,world!
你好世界!hello,world!

总结

首先说RandomAccessFile是个很强大的类,读写文件不丢内容。不知道为啥OutputStream设计成要么覆盖要么追加的模式!
再说文件加密,一般文件都为二进制内容(除了些文本类的文件)如exe、xls、dll等,不需要对所有内容进行加密,只需要对部分执行加密即可,改变了部分内容也就相当于破坏了原有结构,也算是达到了加密效果。
而且修改内容越少,文件被损毁的风险也就越小。比如有个1G的文件,写一半断电了,这个文件几乎就废了解密不了了。
如果只修改前128位内容,修改一次性执行完成,风险就稍微小一些。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值