#8 端对端加密通信软件

2023-01-29

相关文章:

端对端加密(End-to-end encryption,缩写为 E2EE)指的是,只有对话双方能够看到通信内容,其他第三方都无法解密,属于最安全的通信方式。
注意:按照国内相关法律,提供、使用无法监管的加密通讯工具有法律风险。

目前,支持端对端加密的通信软件,主要有下面这些:

  • Signal,据说是最安全的通信工具
  • Wire
  • Riot(Matrix 协议的实现)
  • Line
  • iMessage
  • Keybase
  • WhatsApp
  • Telegram
    默认没有启用端对端加密,只有打开"秘密对话"功能才会启用。
  • Status 去中心化,Matrix 加密通信协议
  • CoyIM 洋葱网络项目开发的匿名通信工具,Tor + TLS + XMPP + OTR(Off-The-Record)端对端加密

参考资料与拓展阅读

#7 scrypt 加密

2021-10-03

在 Python 标准库 hashlib 中见过 scrypt,说是 3.6 引入。
然后,这两天看到的资讯说是 Django 4 将加入了 scrypt 做密码哈希,据说安全性比之前的 PBKDF2 更好。
PS: 由于需要 OpenSSL 1.1+ 的支持,以及会消耗更多的内存,所以不是默认选项。

#5 JWT 认证

2021-09-20

JWT 介绍

一句话介绍 JWT:就是通过非对称加密算法签名的方式,将部分用户信息存在客户端。

全名 JSON Web Token,简称 JWT。
内容 = 头 Headers (JSON) + 负载 Payload (JSON) + 签名 Signature
分别 Base64 编码之后,用 . 连接。

示例

JSON 实现

import base64

def jsonify(obj):
    return json.dumps(obj, ensure_ascii=False, separators=',:')

def base64urlEncoding(s):
    return base64.urlsafe_b64encode(s).rstrip(b'=').decode('ascii')

def sign(key, msg):
    return base64urlEncoding(hmac.new(key, msg, hashlib.sha256).digest())

# def verify(key, msg, sig):
#     return key.verify(msg, base64.urlsafe_b64decode(sig))

def e(obj):
    return base64urlEncoding(jsonify(header).encode('utf-8'))

def encode(key, header, payload):
    s = e(header) + '.' + e(payload)
    return s + '.' + sign(key, s)

def decode(token):
    header_str, payload_str, sig = token.split('.')
    header = json.loads(base64.urlsafe_b64decode(header_str).decode('utf-8'))
    payload = json.loads(base64.urlsafe_b64decode(payload_str).decode('utf-8'))
    sig = base64.urlsafe_b64decode(sig)

header = {
  "alg": "HS256",
  "typ": "JWT"
}
payload = {
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

signature = HMAC_SHA256(
    secret,
    base64urlEncoding(header) + '.' +
    base64urlEncoding(payload)
)
const token = base64urlEncoding(header) + '.' + base64urlEncoding(payload) + '.' + base64urlEncoding(signature)

标准字段

Claim 字段

Code Name Description
iss Issuer ..
sub Subject ..
aud Audience ..
exp Expiration Time ..
nbf Not Before ..
iat Issued At ..
jti JWT ID ..

Header 字段

Code Name Description
typ Type ..
cty Content Type ..
alg Algorithm ..
kid Key ID ..
x5c X.509 Certificate ..
x5u X.509 URL ..
x5t X.509 Thumbprint ..
jku JSON Web Key URL ..
jwk JSON Web Key ..
x5t#S256 X.509 SHA-256 Thumbprint ..

关于 JWT 的看法

  1. 相比 Session,减少了 DB/Redis 操作。
  2. 如果需要注销功能(踢下线),可以加一个黑名单,验证服务定时拉取这个黑名单
  3. 服务降级的时候,这一步可以去掉
  4. 相比加密 Cookie,不用依赖 HTTP Cookie,更加灵活,适用更多场景。
  5. 应该尽可能保证 Token 不要太长
  6. 顶多 4、5 个字段就够了
  7. JWT 和 加密 Cookie 一样,如果需要存储的信息太多,还是会让请求有点臃肿
  8. 大部分时候只需要验证身份,没有太多信息存储的需求,JWT
  9. 可以在网关那边统一校验
  10. 跨域

#4 PyCryptodome

2020-02-05

和 PyCrypto 的关系

PyCrypto 是 Python 界最知名的加密模块,它提供了一系列的加密算法,包括对称加密、非对称加密、哈希算法、签名算法等。
不过有一个很大的问题:上一个版本 2.6.1 发布于 2013-10-18,已经很多年没有维护了。

PyCryptodome 是 PyCrypto 的分叉,该项目在统一套代码的基础上提供了两种包:pycryptodomepycryptodomex

  1. 前者保持对 PyCrypto 的兼容,所有的代码都在 Crypto 名称下,
  2. 后者丢掉了历史包袱,放弃对 PyCrypto 的兼容,所有代码都在 Cryptodome 名称下。

  3. https://pypi.org/project/pycryptodome/

  4. https://pypi.org/project/pycryptodomex/
  5. https://www.pycryptodome.org/en/latest/
  6. https://github.com/Legrandin/pycryptodome/

其他加密模块

示例


#3 Python 加密解密

2017-05-04

#2 Djanog 密码算法:PBKDF2

2015-04-06

实现

在 Python 2.7.8 (July 2, 2014),或 Python 3.4 (March 17, 2014) 以上版本,标准库 hashlib 有 pbkdf2_hmac:

import base64
import hashlib
import random
import secrets

def pbkdf2(password, salt, iterations, dklen=0, digest=None):
    if digest is None:
        digest = hashlib.sha256
    if not dklen:
        dklen = None
    password = password.encode('utf-8')
    salt = salt.encode('utf-8')
    return hashlib.pbkdf2_hmac(
        digest().name, password, salt, iterations, dklen)

### pbkdf2_sha256 ###

ALGORITHM = 'pbkdf2_sha256'
ITERATIONS = 30000
DIGEST = hashlib.sha256

def encode(password, salt, iterations=None):
    assert password is not None
    assert salt and '$' not in salt
    if not iterations:
        iterations = ITERATIONS
    hash = pbkdf2(password, salt, iterations, digest=DIGEST)
    hash = base64.b64encode(hash).decode('ascii').strip()
    return "%s$%d$%s$%s" % (ALGORITHM, iterations, salt, hash)

def decode(encoded):
    algorithm, iterations, salt, hash = encoded.split('$', 3)
    assert algorithm == ALGORITHM
    return {
        'algorithm': algorithm,
        'hash': hash,
        'iterations': int(iterations),
        'salt': salt,
    }

def verify(password, encoded):
    algorithm, iterations, salt, hash = encoded.split('$', 3)
    assert algorithm == ALGORITHM
    encoded_2 = encode(password, salt, int(iterations))
    return secrets.compare_digest(encoded.encode('utf-8'),
                                  encoded_2.encode('utf-8'))

salt_length = 32
# salt = bytes([random.randint(0, 255) for i in range(salt_length + 20)])
salt = random.randbytes(salt_length + 20)
salt = salt.replace(b'$', b'').decode('latin')[:salt_length]
# print(repr(salt))
a = encode('123456', salt)
print(repr(a))
print(decode(a))
# 'pbkdf2_sha256$30000$x\x82¨\x07Ó[Ám¬9V¬\x13·sÉ\x1eEܱ3\x04Ü\x07\x05BÁfM\x9fV×$/jqvasjfZX6c9xCytBVqddAJFve2DXcLWClTAsZvl48='
print(verify('234567', a))
print(verify('123456', a))

参考资料与拓展阅读