常见编码和加密(python和JavaScript使用示例)
# 常见编码和加密## 编码
ASCII码
Base64
Hex
> Base64 Hex 对二进制数据的一种编码方式,不属于加密,但加密后一般需要对数据进行编码Base64 Hex比较常见。
## 信息摘要(哈希)
md5
```
import hashlib
m = hashlib.md5()
m.update('hello world'.encode('utf-8'))
print(m.hexdigest())
```
```
const CryptoJS = require("crypto-js");
console.log(CryptoJS.MD5('hello world').toString())
```
SHA1
SHA256
> SHA1 SHA256 也可分别使用python的hashlib库和JavaScript的crypto-js实现,使用方法类似
## 加密
AES加密
DES加密
| 算法 | 密钥长度 | 工作模式 | 填充模式 |
| ---- | ----------- | ---------------- | ----------------------------------- |
| AES| 56/64 | ECB/CBC/PCBC/CTR | NoPadding/ZeroPadding/PKCS5Padding/ |
| DES| 128/192/256 | ECB/CBC/PCBC/CTR | NoPadding/PKCS5Padding/PKCS7Padding |
python和javascript使用AES/CBC/PKCS7的代码如下
```python
from Crypto.Cipher import AES# pip install pycryptodome
import base64
import binascii
def encrypt(k, iv, content):
# k:密钥,iv:偏移量,content:需加密的内容
k = k.encode("utf-8")
iv = iv.encode("utf-8")
# pad = lambda s: s + (16 - len(s)%16) * chr(0) # AES加密时,明文长度需为16的倍数。这里的pad用来填充,chr(0)表示为ZeroPadding,在最后填充0直到长度为16的倍数
pad = lambda s: s + (16 - len(s)%16) * chr(16 - len(s)%16) # 这里为Pkcs7填充
content = pad(content).encode("utf-8")
cipher = AES.new(k, AES.MODE_CBC, iv)# CBC模式加密,还有ECB模式
cipher_text = cipher.encrypt(content)
# enc = base64.b64encode(cipher_text).decode("utf-8")
enc = binascii.b2a_hex(cipher_text).decode("utf-8")
return enc
k = "1234567890abcDEF"
iv = "1234567890abcDEF"
print(encrypt(k, iv, "hello world"))
```
```javascript
const CryptoJS = require("crypto-js");
var k = CryptoJS.enc.Utf8.parse('1234567890abcDEF'); // 密钥
var iv = CryptoJS.enc.Utf8.parse('1234567890abcDEF'); // iv
var encrypted = CryptoJS.AES.encrypt("hello world", k, {
mode: CryptoJS.mode.CBC,
iv: iv,
padding: CryptoJS.pad.Pkcs7, // Pkcs7填充
}).toString()
console.log('base64',encrypted)
var e64 = CryptoJS.enc.Base64.parse(encrypted);
var eHex = e64.toString(CryptoJS.enc.Hex);
console.log('hex',eHex)
```
> AES的其他模式和填充方式使用方法类似
>
> ECB模式无iv值
AES对密钥的长度有限制,可以为**16字节,24字节或者32字节**,有时用户输入的密钥不满足长度的要求,crypto-js库的默认做法是根据输入密钥和随机数,利用md5生成新密钥和iv值
```javascript
var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase");
var decrypted = CryptoJS.AES.decrypt(encrypted, "Secret Passphrase");
```
以上crypto-js库默认AES加密的python实现
```python
import base64
from Crypto.Cipher import AES
from Crypto import Random
from hashlib import md5
"""
pip install pycryptodome
"""
def pad(s):
return s + (16 - len(s) % 16) * chr(16 - len(s) % 16).encode()
def unpad(s):
return s)]
def bytes_to_key(data, salt, output=48):
assert len(salt) == 8, len(salt)
data += salt
key = md5(data).digest()
final_key = key
while len(final_key) < output:
key = md5(key + data).digest()
final_key += key
return final_key[:output]
def encrypt(data, passphrase):
salt = Random.new().read(8)
key_iv = bytes_to_key(passphrase, salt, 32+16)
key = key_iv[:32]
iv = key_iv
aes = AES.new(key, AES.MODE_CBC, iv)
cipherbyte = base64.b64encode(b"Salted__" + salt + aes.encrypt(pad(data)))
return cipherbyte
def decrypt(data, passphrase):
data = base64.b64decode(data)
assert data[:8] == b'Salted__'
salt = data
key_iv = bytes_to_key(passphrase, salt, 32+16)
key = key_iv[:32]
iv = key_iv
aes = AES.new(key, AES.MODE_CBC, iv)
plainbyte = unpad(aes.decrypt(data))
return plainbyte
if __name__ == '__main__':
data = "Message".encode()
passphrase = "Secret Passphrase".encode()
encrypt_data = encrypt(data, passphrase)
print(encrypt_data)
```
RSA加密
| 填充方式 | 待填充长度 | 填充后长度 |
| ---------------------- | ---------------------- | ---------- |
| RSA_NO_PADDING | 不填充 | 和公钥等长 |
| RSA_PKCS1_PADDING | 至少RSA_size(rsa) - 11 | 和公钥等长 |
| RSA_PKCS1_OAEP_PADDING | RSA_size(rsa) - 41 | 和公钥等长 |
RSA_PKCS1_PADDING 相同明文每次加密结果不同
```python
import rsa
import binascii
# 生成RSA公钥和私钥
(publickey, privkey) = rsa.newkeys(1024)
# rsa使用的是RSA_PKCS1_PADDING
m = rsa.encrypt('hello world'.encode(), publickey)
encrypted_data = m.hex()
print(encrypted_data)
decrypted_data = rsa.decrypt(binascii.a2b_hex(encrypted_data) ,privkey)
print(decrypted_data)
```
RSA_NO_PADDING 相同明文每次加密结果相同
```python
import rsa
class Encrypt():
"""
该类为参考的这篇文章:https://www.52pojie.cn/forum.php?mod=viewthread&tid=874374
主要用于对应JS中的RSAKeyPair的RSA加密,每次对相同明文加密后的密文都是相同的,原因为NoPadding
"""
def __init__(self, e, n):
self.e = e
self.n = n
def encrypt(self, message):
ee = int(self.e, 16)
nn = int(self.n, 16)
rsa_pubkey = rsa.PublicKey(e=ee, n=nn)
crypto = self._encrypt(message.encode(), rsa_pubkey)
return crypto.hex()
def _pad_for_encryption(self, message, target_length):
message = message[::-1]
max_msglength = target_length - 11
msglength = len(message)
padding = b''
padding_length = target_length - msglength - 3
for i in range(padding_length):
padding += b'\x00'
return b''.join()
def _encrypt(self, message, pub_key):
keylength = rsa.common.byte_size(pub_key.n)
padded = self._pad_for_encryption(message, keylength)
payload = rsa.transform.bytes2int(padded)
encrypted = rsa.core.encrypt_int(payload, pub_key.e, pub_key.n)
block = rsa.transform.int2bytes(encrypted, keylength)
return block
def USE_RSA(message):
e = "010001"
n = "81eb7e93bc0e458f67ad208ffb8039a139dffab9b576309212b6c5b411c0f32266ab8700c617562f294abda31f1195163a058637dfebeb80fa0b8cfa0ef63fac3a52b8c3ecf26cb334745d4c850cb5c7fd0bac81ce39e8814c7284fa8bc9721d66189fe5c6d7a3b1046cfdecc4e2976238ccc0a8a6ebbf0ebdedf4737d2dfc99"
en = Encrypt(e, n)
return en.encrypt(message)
ciphertext = USE_RSA("111111")
print(ciphertext)
```
RSA 密钥的参数和导入方式
RSA 密钥的参数
1. 公钥参数:
- `n`:模数(modulus),通常为两个大素数的乘积。
- `e`:指数(exponent),用于加密操作。
2. 私钥参数:
- `n`:模数(modulus),与公钥中的模数相同。
- `d`:私钥指数(private exponent),用于解密操作。
python中RSA 密钥导入方式可以使用`RSA.import_key(private_key_str)`也可以使用`RSA.construct((private_key.n,private_key.e,private_key.d))`,本质上是一样的可以根据方便程度选择。
```python
from Crypto.Cipher import PKCS1_v1_5
from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
import binascii
sentinel = get_random_bytes(16)
private_key_str = """-----BEGIN RSA PRIVATE KEY-----
MIICXwIBAAKBgQCB636TvA5Fj2etII/7gDmhOd/6ubV2MJIStsW0EcDzImarhwDG
F1YvKUq9ox8RlRY6BYY33+vrgPoLjPoO9j+sOlK4w+zybLM0dF1MhQy1x/0LrIHO
OeiBTHKE+ovJch1mGJ/lxtejsQRs/ezE4pdiOMzAqKbrvw697fRzfS38mQIDAQAB
AoGAYWca+Muur3v6MJQPHnFdw4BOaf09DKURfrJEuuHslNwfuU13yQvJ84WzoUVg
j6AEj++AVvesOl3yGSLR+OIBnqMFZvs6MjBzn5RdKjD3PiiBdUgBh+bGA+9peeqo
8E0FBiPLkedXorM1YtR39cxPPhreNO/R+ApMLA/Z3RZDeWECRQCypZ9xrqWTMYDg
imfyQbKyX2F1ow/fyC29G/ysuT/x8TyiJwWR0QjKEYA1H1G+jEZALJj2H/N9rUFk
ad6ZmN5lPfBCDQI9ALospps+/QyVQl6yIp13XskhjGOhhyzmEOlkeOJbDcy+eLey
ND8jj32YhNMXe29r/InC6msuuLIo9ujdvQJEMzg1PLzcEBWzY62LG/QmLeoW4Ul9
NaYJJx0tFsCOSunlfoA9oo8SPA1Eevad00oYojGnMXn7r97KzuVjwxoHOXPGvMkC
PCxmpb10wkkT9+Y5ucOwSmzRkXfZeDGfFP10ttfVO29PJd85ovhD9N7RVyw493lV
Wb9JOzsgw2/KEUjsSQJEWrAxPbERzSsM+syfrcOhs424v4m97v10kdYXcpuWuAuy
v8yozU2CM1cFEg3CxNJEw1dF9bHon2LJtz2W6gjDLtm9y4I=
-----END RSA PRIVATE KEY-----"""
# 私钥导入方式1
private_key = RSA.import_key(private_key_str)
print('数模n(16进制)',hex(private_key.n))
print('指数e(16进制)',hex(private_key.e))
print('私钥指数private exponent d(16进制)',hex(private_key.d))
# 私钥导入方式2
private_key2 = RSA.construct((private_key.n,private_key.e,private_key.d))
cipher_rsa = PKCS1_v1_5.new(private_key2)
# 密文
encrypted_data = '1d8a3e73f4179e20a9328fe17551d99aa92e0a510c382dec4e03f0d0f4d38e81580748fcce4c6c1361b65ca8010e9eaa8d274b2def2390e00b3b5748da099d3376cf9a43845c52b1f91c4b6a40caf40bc26ef8e8fa98e226afdcca8b8304f8cb4c6e8e336436f0f809f70b65c7f9acd0d7aab25d7b9de89458c9622e6f815477'
decrypted_data = cipher_rsa.decrypt(binascii.a2b_hex(encrypted_data),sentinel=sentinel)
print(decrypted_data)
```
> 参考链接
> https://github.com/DingZaiHub/PythonSpider/tree/master/00-%E9%80%9A%E7%94%A8%E5%8A%A0%E5%AF%86
> https://www.bilibili.com/video/BV16e4y1G7xv?p=27 自己在公司内源项目写了完整的JS加解密方法,包括AES-CCB AES-GCM,ECC,SHA256等等,cryptojs只能解决一部分问题,最核心的随机数生成还是用的nodejs原生的crypto模块 Arctic7 发表于 2023-6-24 15:58
自己在公司内源项目写了完整的JS加解密方法,包括AES-CCB AES-GCM,ECC,SHA256等等,cryptojs只能解决一部 ...
公司做什么的?爬虫还是nodejs的后端,前端的加密要省事一般用cryptojs,要安全性高一般要魔改加密并添加混淆了 用心了,,, 必须一加分 感谢科普,收藏 看着这些代码,头疼…… 谢谢楼主,期待更好的作品! 非常不错,多谢分享!
页:
[1]
2