Cryptography中的对称密钥加解密:fernet算法探究

原创文章,欢迎转发朋友圈,转载请注明出处

cryptography是python语言中非常著名的加解密库,在算法层面提供了高层次的抽象,使用起来非常简单、直观,pythonic,同时还保留了各种不同算法的低级别接口,保留灵活性。

我们知道加密一般分为对称加密(Symmetric Key Encryption)和非对称加密(Asymmetric Key Encryption)。,各自对应多种不同的算法,每种算法又有不同的密钥位长要求,另外还涉及到不同的分组加密模式,以及末尾补齐方式。因此需要高层次的抽象,把这些参数封装起来,让我们使用时,不用关心这么多参数,只要知道这么用足够安全就够了。

对称加密又分为分组加密和序列加密,本文只讨论对称分组加密。

主流对称分组加密算法:DES、3DES、AES

主流对称分组加密模式:ECB、CBC、CFB、OFB

主流填充标准:PKCS7、ISO 10126、ANSI X.923、Zero padding

在cryptography库中,对称加密算法的抽象是fernet模块,包括了对数据的加解密以及签名验证功能,以及密钥过期机制。

该模块采用如下定义:

  • 加解密算法为AES,密钥位长128,CBC模式,填充标准PKCS7
  • 签名算法为SHA256的HMAC,密钥位长128位
  • 密钥可以设置过期时间

使用fernet加解密的例子如下:

>>> 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()
>>> ct = encryptor.update(b"a secret message") + encryptor.finalize()
>>> decryptor = cipher.decryptor()
>>> decryptor.update(ct) + decryptor.finalize()
‘a secret message‘

可见加密时除了指定算法和模式,以及生成随机的key之外,CBC模式还需要生成一个随机的初始向量iv;解密时也要提供iv。

cryptography库的fernet模块封装了对称加密的操作,提供了三个基本操作:

产生对称密钥:   generate_key

用对称密钥加密:encrypt

用对称密钥解密:decrypt

generate_key:可见只是产生了一个32位随机数,并用base64编码

  @classmethod
    def generate_key(cls):
        return base64.urlsafe_b64encode(os.urandom(32))

生成32位密钥后,前16位用来计算hmac,后16位用来加解密

  self._signing_key = key[:16]
        self._encryption_key = key[16:]
        self._backend = backend

encrypt:

1. 获取current_time,并随机生成16位的CBC初始向量iv

2. 指定padding方式为PKCS7

3. 把要加密的原始data用padding方式补齐

4. 指定用AES算法CBC模式加密

5. 加密得到ciphertext

6. 把current_time、iv、ciphertext三者合并得到一个basic_parts

  basic_parts = (
            b"\x80" + struct.pack(">Q", current_time) + iv + ciphertext
        )

7. 计算basic_parts的hmac值

8. 把basic_parts + hmac 做base64计算后返回,这就是我们最终得到的加密数据,里面包含了时间戳、iv、密文、hmac

  def encrypt(self, data):
        current_time = int(time.time())
        iv = os.urandom(16)
        return self._encrypt_from_parts(data, current_time, iv)

    def _encrypt_from_parts(self, data, current_time, iv):
        if not isinstance(data, bytes):
            raise TypeError("data must be bytes.")

        padder = padding.PKCS7(algorithms.AES.block_size).padder()
        padded_data = padder.update(data) + padder.finalize()
        encryptor = Cipher(
            algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend
        ).encryptor()
        ciphertext = encryptor.update(padded_data) + encryptor.finalize()

        basic_parts = (
            b"\x80" + struct.pack(">Q", current_time) + iv + ciphertext
        )

        h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend)
        h.update(basic_parts)
        hmac = h.finalize()
        return base64.urlsafe_b64encode(basic_parts + hmac)

decrypt:

完全于encrypt相反的操作

1. 得到current_time

2. base64解码token,得到包含时间戳、iv、密文、hmac的data

3. 根据时间戳和ttl,判断密钥是否已经失效

4. 计算hmac,并于之前的hmac进行验证,判断密钥有效性

5. 获取iv,和密文,并通过密钥解密,得到经过pad的明文

6. 通过PKCS7进行unpaid操作,得到去掉补齐的明文

7. 返回最终结果

  def decrypt(self, token, ttl=None):
        if not isinstance(token, bytes):
            raise TypeError("token must be bytes.")

        current_time = int(time.time())

        try:
            data = base64.urlsafe_b64decode(token)
        except (TypeError, binascii.Error):
            raise InvalidToken

        if not data or six.indexbytes(data, 0) != 0x80:
            raise InvalidToken

        try:
            timestamp, = struct.unpack(">Q", data[1:9])
        except struct.error:
            raise InvalidToken
        if ttl is not None:
            if timestamp + ttl < current_time:
                raise InvalidToken
        if current_time + _MAX_CLOCK_SKEW < timestamp:
            raise InvalidToken
        h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend)
        h.update(data[:-32])
        try:
            h.verify(data[-32:])
        except InvalidSignature:
            raise InvalidToken

        iv = data[9:25]
        ciphertext = data[25:-32]
        decryptor = Cipher(
            algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend
        ).decryptor()
        plaintext_padded = decryptor.update(ciphertext)
        try:
            plaintext_padded += decryptor.finalize()
        except ValueError:
            raise InvalidToken
        unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()

        unpadded = unpadder.update(plaintext_padded)
        try:
            unpadded += unpadder.finalize()
        except ValueError:
            raise InvalidToken
        return unpadded

原文地址:https://www.cnblogs.com/tian-jiang-ming/p/8313397.html

时间: 2024-10-13 16:15:34

Cryptography中的对称密钥加解密:fernet算法探究的相关文章

Java中RSA非对称密钥加解密使用示例

一.简介: RSA加密算法是最常用的非对称加密算法,CFCA在证书服务中离不了它.RSA是第一个比较完善的公开密钥算法,它既能用于加密,也能用于数字签名.这个算法经受住了多年深入的密码分析,虽然密码分析者既不能证明也不能否定RSA的安全性,但这恰恰说明该算法有一定的可信性,目前它已经成为最流行的公开密钥算法. 二.RSA的公钥.私钥的组成,以及加密.解密的公式可见于下表 三.使用方式: ①  假设A.B机器进行通信,已A机器为主: ②  A首先需要用自己的私钥为发送请求数据签名,并将公钥一同发送

HTTPS中的对称密钥加密,公开密钥加密,数字证书

HTTPS中的对称密钥加密,公开密钥加密,数字证书 密钥 我们将未加密的内容称为明文,加密之后的内容称为密文. 简单来说,要加密一段明文,可以将这段内容输入到一个加密函数中,输出密文.但这种简单的加密方式存在被人盗取到加密函数从而破解明文的危险,且加密函数一般构成复杂,一旦被盗取更换成本较高. 于是人们想出了一个办法,在加密函数中再添加一个参数,这个参数只有通信双方知道,没有参数则无法正确解密出密码.这个参数被称为密钥.对于同一个加密函数而言,密钥值的不同则加密方式也不同,得出的密文也就不同.这

JAVA中AES对称加密和解密

AES对称加密和解密 package demo.security; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Ba

程序中对密码进行加解密的C代码示例

问题的提出 在实际的软件项目中,经常涉及到对密码的处理,如用户登录密码.数据库密码.FTP密码等.为了增加软件的灵活性,一般都要求将这些密码放到一个配置文件中.但密码原文容易记录,若被软件入侵者获取,则后果不堪设想.因此我们不能直接将密码原文填入配置文件中,而要先对密码进行加密,然后将加密之后的密文填入配置文件,等程序读取配置之后再进行解密处理. 整个流程如图1所示. 图1 加解密总体流程 示例程序流程 为了演示整个加解密的流程,设计了一个演示程序,其执行流程如图2所示. 图2 示例程序流程 示

vue中使用base64进行加解密

vue进行Base64加解密 背景 项目中需要对特殊字符进行处理,避免json和数据库的特殊字符(""等)冲突,刚好学了信息安全,干脆整个加解密,wkk.. 使用步骤 打开dos,在项目根目录运行npm install --save js-base64 在组件中引入let Base64 = require('js-base64').Base64 使用 Base64.encode(明文) Base64.decode(密文) 原文地址:https://www.cnblogs.com/quj

js加解密的算法

//字符串和数字互转 var str="a" var r = str.charCodeAt(0); //97 10进制 console.log(r); var t=String.fromCharCode(r); console.log(t); //a

对称密钥与非对称密钥算法

对称密钥算法和非对称密钥算法 密码学中两种常见的密码算法为对称密码算法(单钥密码算法)和非对称密码算法(公钥密码算法). 所谓对称密钥算法是指如果一个加密算法的加密密钥和解密密钥相同,或者虽然不相同,但是可由其中的任意一个很容易的推导出另一个,即密钥是双方共享的. 非对称密钥算法是指一个加密算法的加密密钥和解密密钥是不一样的,或者说不能由其中一个密钥推导出另一个密钥.这两个密钥其中一个称为公钥,用于加密,是公开的,另一个称为私钥,用于解密,是保密的.其中由公钥计算私钥是计算上不可行的. 这两种密

SQLSERVER加密解密函数(非对称密钥 证书加密 对称密钥)

ENCRYPTBYASYMKEY() --非对称密钥 ENCRYPTBYCERT()   --证书加密 ENCRYPTBYKEY()   --对称密钥 ENCRYPTBYPASSPHRASE()  --通行短语(PassPhrase)加密 --非对称密钥包含数据库级的内部公钥和私钥,它可以用来加密和解密SQL Server数据库中的数据,它可以从外部文件或程序集中导入,也可以在SQL Server数据库中生成.它不像证书,不可以备份到文件.这意味着一旦在SQL Server中创建了它,没有非常简

Java实现RSA密钥对并在加解密、加签验签中应用的实例

一.项目结构 二.代码具体实现 1.密钥对生成的两种方式:一种生成公钥私文件,一种生成公钥私串 KeyPairGenUtil.java package com.wangjinxiang.genkey.util; import java.io.FileOutputStream; import java.io.ObjectOutputStream; import java.security.Key; import java.security.KeyPair; import java.security