繁体   English   中英

在 python 中加密 - 在 Javascript 中解密

[英]Encrypt in python - decrypt in Javascript

我需要简单地在 python 中加密一些文本并能够在 JavaScrypt 中解密。

到目前为止,我在 python 中有:

from Crypto import Random
from Crypto.Cipher import AES
import base64

BLOCK_SIZE = 16
key = "1234567890123456" # want to be 16 chars
textToEncrypt = "This is text to encrypt"

def encrypt(message, passphrase):
    # passphrase MUST be 16, 24 or 32 bytes long, how can I do that ?
    IV = Random.new().read(BLOCK_SIZE)
    aes = AES.new(passphrase, AES.MODE_CFB, IV)
    return base64.b64encode(aes.encrypt(message))

def decrypt(encrypted, passphrase):
    IV = Random.new().read(BLOCK_SIZE)
    aes = AES.new(passphrase, AES.MODE_CFB, IV)
    return aes.decrypt(base64.b64decode(encrypted))

print encrypt( textToEncrypt, key )

这是产生文本: ZF9as5JII5TlqcB5tAd4sxPuBXd5TrgE

在 JavaScript 中:

<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script>
    var decrypted = CryptoJS.AES.decrypt( "ZF9as5JII5TlqcB5tAd4sxPuBXd5TrgE", "1234567890123456");
    console.log ( decrypted.toString( CryptoJS.enc.Utf8 ) );
</script>

但是它不会产生原始字符串(而是空字符串)。 我做错了什么?

是否专注于 AES 是一个最好的主意 - 如果我有某种会模糊数据的加密,我会很高兴。

你的 Python 代码CryptoJS 代码有很多问题:

  • 您使用随机 IV 来加密 Python 中的一些明文。 如果要检索该明文,则需要在解密期间使用相同的 IV 没有IV就无法恢复明文。 通常 IV 只是简单地添加到密文之前,因为它不必是秘密的。 所以你需要在解密过程中读取 IV 而不是生成一个新的。

  • 您在 CryptoJS(默认)中使用 CBC 模式而不是 CFB 模式。 模式必须相同。 另一个棘手的部分是 CFB 模式是用段大小参数化的。 PyCrypto 默认使用 8 位段 (CFB8),但 CryptoJS 仅针对 128 位 (CFB128) 的固定段实现。 由于 PyCrypto 版本是可变的,您需要更改它。

  • CryptoJS decrypt()函数需要一个 OpenSSL 格式的字符串或 CipherParams 对象作为密文。 由于您没有 OpenSSL 格式的字符串,因此您必须将密文转换为对象。

  • CryptoJS 的key应该是 WordArray 而不是字符串。

  • 使用相同的填充。 如果使用 CFB8,PyCrypto 不会填充明文,但使用 CFB128 时需要填充。 CryptoJS 默认使用 PKCS#7 填充,因此您只需要在 python 中实现该填充。

Python 代码(适用于版本 2):

def pad(data):
    length = 16 - (len(data) % 16)
    return data + chr(length)*length

def unpad(data):
    return data[:-ord(data[-1])]

def encrypt(message, passphrase):
    IV = Random.new().read(BLOCK_SIZE)
    aes = AES.new(passphrase, AES.MODE_CFB, IV, segment_size=128)
    return base64.b64encode(IV + aes.encrypt(pad(message)))

def decrypt(encrypted, passphrase):
    encrypted = base64.b64decode(encrypted)
    IV = encrypted[:BLOCK_SIZE]
    aes = AES.new(passphrase, AES.MODE_CFB, IV, segment_size=128)
    return unpad(aes.decrypt(encrypted[BLOCK_SIZE:]))

JavaScript 代码:

<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script>
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/components/mode-cfb-min.js"></script>
<script>
    var base64ciphertextFromPython = "...";
    var ciphertext = CryptoJS.enc.Base64.parse(base64ciphertextFromPython);

    // split iv and ciphertext
    var iv = ciphertext.clone();
    iv.sigBytes = 16;
    iv.clamp();
    ciphertext.words.splice(0, 4); // delete 4 words = 16 bytes
    ciphertext.sigBytes -= 16;

    var key = CryptoJS.enc.Utf8.parse("1234567890123456");

    // decryption
    var decrypted = CryptoJS.AES.decrypt({ciphertext: ciphertext}, key, {
      iv: iv,
      mode: CryptoJS.mode.CFB
    });
    console.log ( decrypted.toString(CryptoJS.enc.Utf8));
</script>

其他注意事项:

您似乎想使用密码作为密钥。 密码短语通常是人类可读的,但密钥不是。 您可以使用 PBKDF2、bcrypt 或 scrypt 等函数从密码短语派生出密钥。

上面的代码并不完全安全,因为它缺乏身份验证。 未经身份验证的密文可能会导致可行的攻击和不被注意的数据操作。 通常采用先加密后 MAC 方案,并具有良好的 MAC 功能,例如 HMAC-SHA256。

(一年后,但我希望这对某人有用)

首先,感谢 Artjom B。你的帖子对我帮助很大。 和 OP 一样,我有同样的问题 Python 服务器端和 Javascript 客户端解码。 这是我的解决方案:


Python 3.x (服务器)

我使用了明确的PKCS7编码进行填充,为什么? 因为我想确保我使用相同的填充编码和解码,这是我找到它的链接http://programmerin.blogspot.com.co/2011/08/python-padding-with-pkcs7.html

然后,就像 Artjom B. 所说的,确定你的段大小、IV 大小和 AES 模式(对我来说是 CBC),

这是代码:

def encrypt_val(clear_text):
    master_key = '1234567890123456' 
    encoder = PKCS7Encoder()
    raw = encoder.encode(clear_text)
    iv = Random.new().read( 16 )
    cipher = AES.new( master_key, AES.MODE_CBC, iv, segment_size=128 )
    return base64.b64encode( iv + cipher.encrypt( raw ) ) 

请注意,您在 base64 上编码了 IV 和加密数据的连接。


Javascript (客户端)

function decryptMsg (data) {
    master_key = '1234567890123456';

    // Decode the base64 data so we can separate iv and crypt text.
    var rawData = atob(data);
    // Split by 16 because my IV size
    var iv = rawData.substring(0, 16);
    var crypttext = rawData.substring(16);

    //Parsers
    crypttext = CryptoJS.enc.Latin1.parse(crypttext);
    iv = CryptoJS.enc.Latin1.parse(iv); 
    key = CryptoJS.enc.Utf8.parse(master_key);

    // Decrypt
    var plaintextArray = CryptoJS.AES.decrypt(
      { ciphertext:  crypttext},
      key,
      {iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7}
    );

    // Can be Utf8 too
    output_plaintext = CryptoJS.enc.Latin1.stringify(plaintextArray);
    console.log("plain text : " + output_plaintext);
}

我的主要问题之一是记住所有类型的编码和解码数据,例如,我不知道客户端的 master_key 是用 Utf8 解析的。

//首先 pip install pycryptodome --(pycrypto 已经过时并且会出现问题)// pip install pkcs7

from Crypto import Random
from Crypto.Cipher import AES
import base64
from pkcs7 import PKCS7Encoder
from app_settings.views import retrieve_settings # my custom settings

app_secrets = retrieve_settings(file_name='secrets');


def encrypt_data(text_data):
                    #limit to 16 bytes because my encryption key was too long
                    #yours could just be 'abcdefghwhatever' 
    encryption_key = app_secrets['ENCRYPTION_KEY'][:16]; 

    #convert to bytes. same as bytes(encryption_key, 'utf-8')
    encryption_key = str.encode(encryption_key); 
    
    #pad
    encoder = PKCS7Encoder();
    raw = encoder.encode(text_data) # Padding
    iv = Random.new().read(AES.block_size ) #AES.block_size defaults to 16

                                 # no need to set segment_size=BLAH
    cipher = AES.new( encryption_key, AES.MODE_CBC, iv ) 
    encrypted_text = base64.b64encode( iv + cipher.encrypt( str.encode(raw) ) ) 
    return encrypted_text;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM