Skip to content

Latest commit

 

History

History
749 lines (515 loc) · 28.9 KB

File metadata and controls

749 lines (515 loc) · 28.9 KB

十三、密码学与隐写术

本章介绍 python 中用于加密和解密信息的主要模块,如 pycrypto 和 cryptography。此外,我们还介绍了隐写术技术以及如何使用stepic模块在图像中隐藏信息。

本章将介绍以下主题:

  • pycrypto信息加解密模块
  • cryptography信息加解密模块
  • 图像信息隐藏的主要隐写技术
  • 如何使用stepic模块隐藏图像中的信息

技术要求

本章的示例和源代码可在 GitHub 存储库的chapter13文件夹中找到 https://github.com/PacktPublishing/Mastering-Python-for-Networking-and-Security

您需要在本地计算机上安装至少有 4GB 内存的 python 发行版。

用 pycrypto 加密和解密信息

在本节中,我们将回顾加密算法和用于加密和解密数据的pycrypto模块。

密码学导论

密码学可以定义为隐藏信息的实践,包括消息完整性检查、发送方/接收方身份验证和数字签名技术。

以下是四种最常见的加密算法:

  • **散列函数:**也称为单向加密,没有密钥。hash函数为明文输入输出固定长度的哈希值,理论上无法恢复明文的长度或内容。单向cryptographic功能在网站中用于以无法检索的方式存储密码。
  • **键控哈希函数:**用于构建消息认证码(MAC);Mac 旨在防止暴力攻击。因此,它们被有意设计为缓慢。
  • **对称加密:**使用可变密钥为某些文本输入输出密文,我们可以使用相同的密钥对密文进行解密。加密和解密使用相同密钥的算法称为对称密钥算法。
  • **公钥算法:**对于公钥算法,我们有两个不同的密钥:一个用于加密,另一个用于解密。这种做法使用一对密钥:一个用于加密,另一个用于解密。这项技术的用户发布他们的公钥,同时对他们的私钥保密。这使任何人都可以向他们发送一条用公钥加密的消息,只有私钥持有者才能解密。这些算法的设计使得即使攻击者知道相应的公钥,也很难找到私钥。

例如,对于散列函数,Python 提供了一些模块,例如hashlib

下面的脚本返回文件的md5校验和。

您可以在hashlib文件夹内的md5.py文件中找到以下代码:

import hashlib

def md5Checksum(filePath):
    fh = open(filePath, 'rb')
    m = hashlib.md5()
    while True:
        data = fh.read(8192)
        if not data:
            break
        m.update(data)
    return m.hexdigest()

print('The MD5 checksum is', md5Checksum('md5.py'))

上一个脚本的输出为:

The MD5 checksum is 8eec2037fe92612b9a141a45b60bec26

pycrypto 简介

说到用 Python 加密信息,我们有一些选择,但最可靠的是 PyCrypto 加密库,它支持块加密、流加密和散列计算功能。

PyCrypto模块提供了在 Python 程序中实现强加密所需的所有函数,包括哈希函数和加密算法。

例如,pycrypto支持的分组密码为:

  • AES
  • ARC2
  • 河豚
  • 铸造
  • DES
  • DES3
  • 主意
  • RC5

通常,所有这些密码都以相同的方式使用。

我们可以使用Crypto.Cipher包导入特定的密码类型:

from Crypto.Cipher import [Chiper_Type]

我们可以使用新的方法构造函数初始化密码:

new ([key], [mode], [Vector IV])

使用这种方法,只有密钥是必需的,我们必须考虑加密类型是否要求它具有特定的大小。可能的模式有MODE_ECBMODE_CBCMODE_CFBMODE_PGPMODE_OFBMODE_CTRMODE_OPENPGP

如果使用MODE_CBCMODE_CFB模式,则必须初始化第三个参数(向量 IV),这允许为密码提供初始值。某些密码可能具有可选参数,例如 AES,它可以使用block_sizekey_size参数指定块和密钥大小。

与 hashlib 相同,pycrypto也支持 hash 函数。一般哈希函数与pycrypto的用法类似:

  • 我们可以使用**Crypto.Hash包导入特定的哈希类型:from Crypto.Hash import [Hash Type]** *** 我们可以使用更新方法设置需要获取哈希的数据:update('data')*** 我们可以使用hexdigest()方法生成哈希:hexdigest()****

****下面是我们在获取文件校验和时看到的相同示例,在本例中,我们使用pycrypt而不是hashlib

您可以在pycrypto文件夹内的hash.py文件中找到以下代码:

from Crypto.Hash import MD5

def md5Checksum(filePath):
    fh = open(filePath, 'rb')
    m = MD5.new()
    while True:
        data = fh.read(8192)
        if not data:
            break
        m.update(data)
    return m.hexdigest()

print('The MD5 checksum is' + md5Checksum('hash.py'))

要加密和解密数据,我们可以使用**encrypt****decrypt**函数:

encrypt ('clear text')
decrypt ('encrypted text')

DES 算法的加解密

DES 是一种分组密码,这意味着要加密的文本是 8 的倍数,因此我在文本末尾添加了空格。当我破译它的时候,我把它们去掉了。

下面的脚本对用户和密码进行加密,最后模拟是服务器接收到这些凭据,解密并显示这些数据。

您可以在pycrypto文件夹内的Encrypt_decrypt_DES.py文件中找到以下代码:

from Crypto.Cipher import DES

# How we use DES, the blocks are 8 characters
# Fill with spaces the user until 8 characters
user =  "user    "
password = "password"

# we create the cipher with DES
cipher = DES.new('mycipher')

# encrypt username and password
cipher_user = cipher.encrypt(user)
cipher_password = cipher.encrypt(password)

# we send credentials
print("User: " + cipher_user)
print("Password: " + cipher_password)
# We simulate the server where the messages arrive encrypted.

# we decode messages and remove spaces with strip()
cipher = DES.new('mycipher')
decipher_user = cipher.decrypt(cipher_user).strip()
decipher_password = cipher.decrypt(cipher_password)
print("SERVER decipher:")
print("User: " + decipher_user)
print("Password: " + decipher_password)

该程序使用 DES 对数据进行加密,因此它要做的第一件事是导入DES模块并使用以下指令创建编码器:

cipher = DES.new('mycipher')

mycipher”参数值是加密密钥。正如您在示例程序中看到的,一旦创建了密码,加密和解密就相当简单了。

AES 加密解密算法

AES 加密需要一个强密钥。密钥越强,您的加密越强。我们的 AES 密钥需要 16、24 或 32 字节长,我们的初始化向量需要16 字节长。这将使用randomstring模块生成。

要使用 AES 等加密算法,我们可以从**Crypto.Cipher.AES**包中导入。由于 PyCrypto 块级加密 API 的级别非常低,因此它分别只接受 AES-128、AES-196 和 AES-256 的 16、24 或 32 字节长的密钥。密钥越长,加密越强。

此外,对于使用 pycrypto 的 AES 加密,您需要确保数据长度为 16 字节的倍数。如果缓冲区不是,则填充缓冲区,并在输出的开头包含数据的大小,以便接收器可以正确解密。

您可以在pycrypto文件夹内的Encrypt_decrypt_AES.py文件中找到以下代码:

# AES pycrypto package
from Crypto.Cipher import AES

# key has to be 16, 24 or 32 bytes long
encrypt_AES = AES.new('secret-key-12345', AES.MODE_CBC, 'This is an IV-12')

# Fill with spaces the user until 32 characters
message = "This is the secret message      "

ciphertext = encrypt_AES.encrypt(message)
print("Cipher text: " , ciphertext)

# key must be identical
decrypt_AES = AES.new('secret-key-12345', AES.MODE_CBC, 'This is an IV-12')
message_decrypted = decrypt_AES.decrypt(ciphertext)

print("Decrypted text: ", message_decrypted.strip())

上一个脚本的输出为:

('Cipher text: ', '\xf2\xda\x92:\xc0\xb8\xd8PX\xc1\x07\xc2\xad"\xe4\x12\x16\x1e)(\xf4\xae\xdeW\xaf_\x9d\xbd\xf4\xc3\x87\xc4') ('Decrypted text: ', 'This is the secret message')

AES 文件加密

AES 加密要求写入的每个块的大小为 16 字节的倍数。因此,我们以块的形式读取、加密和写入数据。区块大小必须是 16 的倍数。

以下脚本加密参数提供的文件。

您可以在pycrypto文件夹内的aes-file-encrypt.py文件中找到以下代码:

from Crypto.Cipher import AES
from Crypto.Hash import SHA256
import os, random, struct

def encrypt_file(key, filename):
    chunk_size = 64*1024
    output_filename = filename + '.encrypted'

    # Initialization vector
    iv = ''.join(chr(random.randint(0, 0xFF)) for i in range(16))

    #create the encryption cipher
    encryptor = AES.new(key, AES.MODE_CBC, iv)

    #Determine the size of the file
    filesize = os.path.getsize(filename)

    #Open the output file and write the size of the file. 
    #We use the struct package for the purpose.
    with open(filename, 'rb') as inputfile:
        with open(output_filename, 'wb') as outputfile:
            outputfile.write(struct.pack('<Q', filesize))
            outputfile.write(iv)

            while True:
                chunk = inputfile.read(chunk_size)
                if len(chunk) == 0:
                    break
                elif len(chunk) % 16 != 0:
                    chunk += ' ' * (16 - len(chunk) % 16)
                outputfile.write(encryptor.encrypt(chunk))

password = "password"

def getKey(password):
    hasher = SHA256.new(password)
    return hasher.digest()

encrypt_file(getKey(password), 'file.txt');

上一个脚本的输出是一个名为file.txt.encrypted的文件,其中包含与原始文件相同的内容,但信息不清晰。

前面的脚本的工作方式是,首先加载所有必需的模块并定义加密文件的函数:

from Crypto.Cipher import AES
import os, random, struct
def encrypt_file(key, filename, chunk_size=64*1024):
output_filename = filename + '.encrypted'

此外,我们还需要获得初始化向量。需要一个 16 字节的初始化向量,其生成如下:

# Initialization vector
iv = ''.join(chr(random.randint(0, 0xFF)) for i in range(16))

然后我们可以在PyCrypto模块中初始化 AES 加密方法:

encryptor = AES.new(key, AES.MODE_CBC, iv)
filesize = os.path.getsize(filename)

用 AES 解密文件

对于解密,我们需要反转前面的过程,使用 AES 对文件进行解密

您可以在pycrypto文件夹中的**aes-file-decrypt.py**文件中找到以下代码:

from Crypto.Cipher import AES
from Crypto.Hash import SHA256
import os, random, struct

def decrypt_file(key, filename):
    chunk_size = 64*1024
    output_filename = os.path.splitext(filename)[0]

    #open the encrypted file and read the file size and the initialization vector. 
    #The IV is required for creating the cipher.
    with open(filename, 'rb') as infile:
        origsize = struct.unpack('<Q', infile.read(struct.calcsize('Q')))[0]
        iv = infile.read(16)

        #create the cipher using the key and the IV.
        decryptor = AES.new(key, AES.MODE_CBC, iv)

        #We also write the decrypted data to a verification file, 
        #so we can check the results of the encryption 
        #and decryption by comparing with the original file.
        with open(output_filename, 'wb') as outfile:
            while True:
                chunk = infile.read(chunk_size)
                if len(chunk) == 0:
                    break
                outfile.write(decryptor.decrypt(chunk))
            outfile.truncate(origsize)

password = "password"

def getKey(password):
    hasher = SHA256.new(password)
    return hasher.digest()

decrypt_file(getKey(password), 'file.txt.encrypted');

用密码学加密和解密信息

在本节中,我们将回顾用于加密和解密数据的cryptography模块。Cryptography是一个较新的模块,它比pycrypto具有更好的性能和安全性。

密码学导论

pypi存储库中提供了加密技术,您可以使用pip install cryptography命令进行安装。

中 https://pypi.org/project/cryptography URL,我们可以看到此模块的最新版本。

For more information about installation and supported platforms, check out https://cryptography.io/en/latest/installation/.

密码学包括普通密码算法的高级和低级接口,如对称密码、消息摘要和密钥派生函数。例如,我们可以对fernet包使用对称加密。

fernet 包的对称加密

Fernet 是对称加密的一种实现,它保证在没有密钥的情况下无法操作或读取加密消息。

为了生成密钥,我们可以从Fernet接口使用generate_key()方法。

您可以在加密文件夹中的**encrypt_decrypt.py文件中找到以下代码:**

**```py from cryptography.fernet import Fernet

key = Fernet.generate_key() cipher_suite = Fernet(key)

print("Key "+str(cipher_suite)) message = "Secret message"

cipher_text = cipher_suite.encrypt(message) plain_text = cipher_suite.decrypt(cipher_text)

print("\n\nCipher text: "+cipher_text)

print("\n\nPlain text: "+plain_text)


这是上一个脚本的输出:

![](img/8ff216ff-69d5-4ad1-be86-befeb736603c.png)

# 在 fernet 软件包中使用密码

可以将密码与 Fernet 一起使用。为此,您需要通过密钥派生函数运行密码,例如**PBKDF2HMAC。**

**PBKDF2(基于密码的密钥派生函数 2)**通常用于从密码派生加密密钥。

More information about key derivation functions can be found at [https://cryptography.io/en/latest/hazmat/primitives/key-derivation-functions/](https://cryptography.io/en/latest/hazmat/primitives/key-derivation-functions/).

在本例中,我们使用此函数从密码生成密钥,并使用该密钥创建用于加密和解密数据的 Fernet 对象。在本例中,要加密的数据是一个简单的消息字符串。我们可以使用`verify()`方法,该方法检查从提供的密钥派生新密钥是否会生成与预期的 _ 密钥相同的密钥。

您可以在加密文件夹中的**`encrypt_decrypt_kdf.py`文件中找到以下代码:**

 **```py
import base64
import os
from cryptography.fernet import Fernet
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

password = "password"
salt = os.urandom(16)
kdf = PBKDF2HMAC(algorithm=hashes.SHA256(),length=32,salt=salt,iterations=100000,backend=default_backend())

key = kdf.derive(password)

kdf = PBKDF2HMAC(algorithm=hashes.SHA256(),length=32,salt=salt,iterations=100000,backend=default_backend())

#verify() method checks whether deriving a new key from 
#the supplied key generates the same key as the expected_key, 
#and raises an exception if they do not match.
kdf.verify(password, key)

key = base64.urlsafe_b64encode(key)
fernet = Fernet(key)
token = fernet.encrypt("Secret message")

print("Token: "+token)
print("Message: "+fernet.decrypt(token))

这是上一个脚本的输出

如果我们使用verify()方法验证密钥,并且在验证过程中检查密钥不匹配,则会启动cryptography.exceptions.InvalidKey异常:

使用密码包的对称加密

来自cryptography模块的密码包提供了一个对称加密类**cryptography.hazmat.primitives.ciphers.Cipher类。**

**密码对象将算法(如 AES)与模式(如 CBC 或 CTR)相结合。

在下面的脚本中,我们可以看到一个使用 AES 加密然后解密内容的示例。

您可以在加密文件夹中的encrypt_decrypt_AES.py文件中找到以下代码:

import os
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend

backend = default_backend()
key = os.urandom(32)
iv = os.urandom(16)
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend)

encryptor = cipher.encryptor()
print(encryptor)

message_encrypted = encryptor.update("a secret message")

print("\n\nCipher text: "+message_encrypted)
ct = message_encrypted + encryptor.finalize()

decryptor = cipher.decryptor()

print("\n\nPlain text: "+decryptor.update(ct))

这是上一个脚本的输出:

隐藏图像信息的隐写术技术

在本节中,我们将回顾隐写术技术和 stepic 作为在图像中隐藏信息的python模块。

隐写术简介

隐写术(http://en.wikipedia.org/wiki/Steganography 是密码学的一个特殊分支,它允许我们将秘密信息隐藏到公共信息中,也就是说,隐藏到明显无害的信息中。

隐藏信息的主要技术之一是使用最低有效位(LSB)。

当通过图像的每个像素时,我们得到一个由(0)到(255)的整数组成的 RGB 三元组,由于每个数字都有自己的二进制表示形式,我们将该三元组转换为其等价的二进制表示形式;例如,由(148、28、202)形成的像素是二进制的,相当于(100101001001110011001010)。

目标是编辑最低有效位,即最后一个到右边的位。在下面的 LSB 列中,我们更改了位(红色),但其余的位仍然保持不变,RGB 三元组的结果经历了一些更改,但它们是最小的。如果仔细地将它们设置为两种颜色,它们不太可能发现任何视觉上的差异,但实际上发生了变化,在改变最低有效位后,RGB 三元组与我们开始时的不同,但颜色显然是相同的。

我们可以更改信息并发送,而攻击者不会意识到有什么奇怪的东西。

一切都是 1 和 0,我们可以让 LSB 遵循我们想要的顺序,例如,如果我们想隐藏单词“Hacking”,我们必须记住每个字母(字符)都可以用一个字节表示,即“H”=01001000,因此如果我们有 3 个像素,我们可以使用 LSB 隐藏该序列。

在这幅图中,我们可以看到“H”字母以二进制和 LSB 格式表示:

因为每个像素有三个值​​这就构成了它,在每一个字母中我们只能改变一位,那么隐藏字母“H”需要三个像素,因为它的二进制表示对应于八位。上表非常直观;为了得到原始图像的三个像素,我们取出它们各自的 RGB,因为我们想在二进制中隐藏字母“H”,我们只需按“H”的顺序替换最低有效位。然后我们回去重建三个像素,只是现在我们在其中隐藏了字母,它们的值​​已经发生了变化,但肉眼看不到变化。

这样,我们不仅可以隐藏文本,还可以隐藏各种信息,因为一切都可以用二进制值表示;恢复信息的方法就是接收修改后的图像并开始读取最低有效位,因为每八位,我们就有一个字符的表示。

在下一个脚本中,我们将用 python 实现这项技术。

您可以在隐写术文件夹中的steganography_LSB.py文件中找到以下代码

首先,我们定义 get 函数,设置最低有效位(LSB)**,并设置读取图像并访问每个像素对 LSB 的extract_message()方法:

#!/usr/bin/env python

#Hide data in lsbs of an image
#python 3.x compatible

from PIL import Image

def get_pixel_pairs(iterable):
    a = iter(iterable)
    return zip(a, a)

def set_LSB(value, bit):
    if bit == '0':
        value = value & 254
    else:
        value = value | 1
    return value

def get_LSB(value):
    if value & 1 == 0:
        return '0'
    else:
        return '1'

def extract_message(image):
    c_image = Image.open(image)
    pixel_list = list(c_image.getdata())
    message = ""
    for pix1, pix2 in get_pixel_pairs(pixel_list):
        message_byte = "0b"
        for p in pix1:
            message_byte += get_LSB(p)
        for p in pix2:
            message_byte += get_LSB(p)
        if message_byte == "0b00000000":
            break
        message += chr(int(message_byte,2))
    return message

现在,我们定义了我们的hide_message方法,该方法读取图像并使用每个像素的 LSB 隐藏图像中的消息:

def hide_message(image, message, outfile):
    message += chr(0)
    c_image = Image.open(image)
    c_image = c_image.convert('RGBA')
    out = Image.new(c_image.mode, c_image.size)
    width, height = c_image.size
    pixList = list(c_image.getdata())
    newArray = []
    for i in range(len(message)):
        charInt = ord(message[i])
        cb = str(bin(charInt))[2:].zfill(8)
        pix1 = pixList[i*2]
        pix2 = pixList[(i*2)+1]
        newpix1 = []
        newpix2 = []

        for j in range(0,4):
            newpix1.append(set_LSB(pix1[j], cb[j]))
            newpix2.append(set_LSB(pix2[j], cb[j+4]))

        newArray.append(tuple(newpix1))
        newArray.append(tuple(newpix2))

    newArray.extend(pixList[len(message)*2:])
    out.putdata(newArray)
    out.save(outfile)
    return outfile

if __name__ == "__main__":

 print("Testing hide message in python_secrets.png with LSB ...")
 print(hide_message('python.png', 'Hidden message', 'python_secrets.png'))
 print("Hide test passed, testing message extraction ...")
 print(extract_message('python_secrets.png'))

Stepic 隐写术

Stepic 提供了一个Python模块和一个命令行界面,用于隐藏图像中的任意数据。它稍微修改图像中像素的颜色以存储数据。

要设置 stepic,只需使用pip install stepic命令安装即可。

Stepic 的Steganographer类是模块的主要类,在这里我们可以看到可用于编码和解码图像中数据的方法:

在下面与 python 2.x 版兼容的脚本中,我们可以看到这些函数的实现。

您可以在steganography文件夹内的**stepic.py**文件中找到以下代码:****

****```py

stepic - Python image steganography

'''Python image steganography Stepic hides arbitrary data inside PIL images. Stepic uses the Python Image Library (apt: python-imaging, web: http://www.pythonware.com/products/pil/). ''' from PIL import Image

def _validate_image(image): if image.mode not in ('RGB', 'RGBA', 'CMYK'): raise ValueError('Unsupported pixel format: ''image must be RGB, RGBA, or CMYK') if image.format == 'JPEG': raise ValueError('JPEG format incompatible with steganography')


在这部分代码中,我们可以看到与使用 LSB 对图像中的数据进行编码相关的方法。

Stepic 从顶部开始从左到右读取图像像素。每个像素由 0 到 255 之间的三个整数组成,第一个提供红色分量,第二个提供绿色分量,第三个提供蓝色分量。它一次读取三个像素,每个像素包含三个值:红色、绿色和蓝色。每组像素有九个值。一个字节的数据有八位,因此如果每种颜色都可以稍微修改,通过将最低有效位设置为零或一,这三个像素可以存储一个字节,剩下一个颜色值:

```py
def encode_imdata(imdata, data):
    '''given a sequence of pixels, returns an iterator of pixels with encoded data'''

    datalen = len(data)
    if datalen == 0:
        raise ValueError('data is empty')
    if datalen * 3 > len(imdata):
        raise ValueError('data is too large for image')

    imdata = iter(imdata)
    for i in xrange(datalen):
        pixels = [value & ~1 for value in
            imdata.next()[:3] + imdata.next()[:3] + imdata.next()[:3]]
        byte = ord(data[i])
        for j in xrange(7, -1, -1):
            pixels[j] |= byte & 1
            byte >>= 1
        if i == datalen - 1:
            pixels[-1] |= 1
            pixels = tuple(pixels)
            yield pixels[0:3]
            yield pixels[3:6]
            yield pixels[6:9]

def encode_inplace(image, data):
    '''hides data in an image'''
    _validate_image(image)
    w = image.size[0]
    (x, y) = (0, 0)
    for pixel in encode_imdata(image.getdata(), data):
        image.putpixel((x, y), pixel)
        if x == w - 1:
            x = 0
            y += 1
        else:
            x += 1

def encode(image, data):
    '''generates an image with hidden data, starting with an existing
       image and arbitrary data'''

    image = image.copy()
    encode_inplace(image, data)
    return image

在这部分代码中,我们可以看到使用 LSB 对图像中的数据进行解码的相关方法。基本上,给定图像中的像素序列,它返回图像中编码字符的迭代器:

def decode_imdata(imdata):
    '''Given a sequence of pixels, returns an iterator of characters
    encoded in the image'''

    imdata = iter(imdata)
    while True:
        pixels = list(imdata.next()[:3] + imdata.next()[:3] + imdata.next()[:3])
        byte = 0
        for c in xrange(7):
            byte |= pixels[c] & 1
            byte <<= 1
        byte |= pixels[7] & 1
        yield chr(byte)
        if pixels[-1] & 1:
            break

def decode(image):
    '''extracts data from an image'''
    _validate_image(image)
    return ''.join(decode_imdata(image.getdata()))

Stepic 使用最低有效位(http://en.wikipedia.org/wiki/Least_significant_bit 此剩余值的)表示数据的结束。编码方案不提供图像是否包含数据的线索,因此 Stepic 将始终从任何图像中提取至少一个字节,是否有人故意在那里隐藏数据。

要对其进行解码,我们可以使用以下功能:

decode_imdata(imdata)

我们可以看到,该函数与encode_imdata(imdata, data)函数相反,即从左到右、从上到下同时读取三个像素,直到读取其等于 1 的最后一个像素的最后一种颜色的最后一位。

用 stepic 隐藏图像中的数据

在下面的脚本中,我们使用PIL模块表单中的图像包读取图像。一旦我们读取了图像,我们就使用 stepic 的 encode 函数来隐藏图像中的一些文本。我们将此信息保存在第二个图像中,并使用解码功能获取隐藏文本。

您可以在steganography文件夹内的stepic_example.py文件中找到以下代码:

**```py from PIL import Image import stepic

#Open an image file in which you want to hide data image = Image.open("python.png")

#Encode some text into the source image. #This returns another Image instance, which can save to a new file

image2 = stepic.encode(image, 'This is the hidden text') image2.save('python_secrets.png','PNG')

#Use the decode() function to extract data from an image:

image2 = Image.open('python_secrets.png') s = stepic.decode(image2) data = s.decode() print("Decoded data: " + data)


# 总结

本章的目标之一是了解允许我们使用 AES 和 DES 算法加密和解密信息的`pycrypto`和`cryptography`模块。我们还研究了隐写术技术,如最低有效位,以及如何使用 stepic 模块在图像中隐藏信息。

为了结束这本书,我想强调的是,读者应该更多地了解他们认为最重要的话题。每章都涵盖了基本思想,读者可以从*进一步阅读*部分找到更多信息的参考资料。

# 问题

1.  哪种算法类型使用相同的密钥加密和解密数据?
2.  哪种算法类型使用两种不同的密钥,一种用于加密,另一种用于解密?
3.  我们可以在 pycrypto 中使用哪个包来使用加密算法,如 AES?
4.  哪种算法需要确保数据长度为 16 字节的倍数?
5.  `cryptography`模块的哪个包可以使用对称加密?

6.  使用哪种算法从密码派生加密密钥?

7.  是什么提供了用于对称加密的 fernet 包,以及用于生成密钥**的方法是什么?**
8.  哪个类别提供密码包对称加密?
9.  stepic 的哪种方法生成包含隐藏数据的图像,从现有的
    图像和任意数据开始?
10.  pycrypto 的哪个包包含一些允许单向加密的`hash`函数?

# 进一步阅读

在这些链接中,您将找到有关本章中提到的工具及其官方文档的更多信息:

`Pycryptodome`是一个基于`pypi`存储库中可用的`pycrypto`库的模块:

[https://pypi.org/project/pycryptodome/](https://pypi.org/project/pycryptodome/)

[https://github.com/Legrandin/pycryptodome](https://github.com/Legrandin/pycryptodome)

[https://www.pycryptodome.org/en/latest/](https://www.pycryptodome.org/en/latest/)

在这些链接中,我们可以看到与`Pycrypto`模块相关的其他示例:

[https://github.com/X-Vector/Crypt0x/tree/master/Crypt0x](https://github.com/X-Vector/Crypt0x/tree/master/Crypt0x)

[https://github.com/jmortega/pycon-security_criptography](https://github.com/jmortega/pycon-security_criptography)

如果您需要更深入地探索密码生成,您可以找到其他有趣的模块,如 Secrets:

[https://docs.python.org/3/library/secrets.html#module-秘密](https://docs.python.org/3/library/secrets.html#module-secrets)

`secrets`模块用于生成适用于管理数据的加密强随机数,如密码、帐户身份验证、安全令牌和相关机密。******************