python 中的 RSA 加密和 JS 中的解密
RSA encryption in python & decrypt in JS
我对密码学完全陌生。我想从服务器端生成 RSA 密钥对并将其发送到所有客户端(浏览器)。但在此之前,我通过简单地在 python 中加密数据并通过 pubnub 发送到 index.html 文件并尝试在 JavaScript 中解密来测试场景。问题是当我通过;
进行加密时
random_generator = Random.new().read
key = RSA.generate(1024, random_generator)
print key.exportKey() #<--private key
public_key = key.publickey()
print public_key.exportKey() #<--public key
msg = "hello"
enc_data = public_key.encrypt(msg, 32)
print '----ENCRYPTED DATA----'
enc = enc_data[0]
并发送加密数据 enc,它给我这个错误:
UnicodeDecodeError: 'utf8' codec can't decode byte 0xc4 in position 2: invalid continuation byte
我试着把它转换成
enc = base64.b64encode(enc_data[0])
并且发送没有错误。但是 JS 解密方法得到 None
var enc_from_python = $('#input').val();
console.log("ENCRYPTED data:", enc_from_python);
var decrypt = new JSEncrypt();
decrypt.setPrivateKey($('#privkey').val());
var uncrypted = decrypt.decrypt(enc_from_python);
console.log(">>>",uncrypted); //<-- this is None ! why ?
这两段代码本身 enc/dec 都做得很好。我还尝试使用从 python 接收到的密钥对 enc/dec JS 中的数据,并且效果很好。我想问题出在 Pycrypto 的编码数据的 unicode 编码格式不匹配。谁能告诉我我在这里错过了什么。
Python 的完整代码:
import time
from pubnub.pnconfiguration import PNConfiguration
from pubnub.pubnub import PubNub
from Crypto.PublicKey import RSA
from Crypto import Random
import base64
pnconfig = PNConfiguration()
pnconfig.subscribe_key = 'demo'
pnconfig.publish_key = 'demo'
channel = "my_channel"
pubnub = PubNub(pnconfig)
def my_publish_callback(envelope, status):
if not status.is_error():
pass # Message successfully published to specified channel.
else:
pass # Handle message publish error. Check 'category' property to find out possible issue
time.sleep(1)
random_generator = Random.new().read
key = RSA.generate(1024, random_generator)
print key.exportKey() #<--private key
public_key = key.publickey()
print public_key.exportKey() #<--public key
msg = "hello"
enc_data = public_key.encrypt(msg, 32)
print '----ENCRYPTED DATA----'
#enc = enc_data[0]
enc = base64.b64encode(enc_data[0])
print enc
#print type(enc_data[0])
print '----ENCRYPTED DATA----'
print ''
print '----DECRYPTED DATA begin----'
print key.decrypt(enc_data[0])
print '----DECRYPTED DATA end----'
pubnub.publish().channel(channel).message({"data": enc , "private": (key.exportKey()), "public" : (public_key.exportKey())}).async(my_publish_callback)
完整的 JS 代码是;
<!doctype html>
<html>
<head>
<title>JavaScript RSA Encryption</title>
<script src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsencrypt/2.3.1/jsencrypt.min.js"></script>
<script src="https://cdn.pubnub.com/sdk/javascript/pubnub.4.12.0.min.js"></script>
<script type="text/javascript">
// Call this code when the page is done loading.
$(function() {
pubnub = new PubNub({
publish_key: 'demo',
subscribe_key: 'demo'
});
pubnub.subscribe({
channels: ['my_channel']
});
pubnub.addListener({
message: function(message) {
var msg = message.message;
console.log("msg:" + JSON.stringify(msg));
if (msg.private){
$("#privkey").val(msg.private);
}
if(msg.public){
$("#pubkey").val(msg.public);
}
if(msg.data){
$("#input").val(msg.data);
}
}
})
// Run a quick encryption/decryption when they click.
$('#testme').click(function() {
var enc_from_python = $('#input').val();
console.log("ENCRYPTED data:", enc_from_python);
// Decrypt with the private key...
var decrypt = new JSEncrypt();
decrypt.setPrivateKey($('#privkey').val());
var uncrypted = decrypt.decrypt(enc_from_python);
console.log(">>>",uncrypted); //<-- this is None ! why ?
// Now a simple check to see if the round-trip worked.
if (uncrypted == $('#input').val()) {
alert('It works!!!');
}
else {
alert('Something went wrong....');
}
});
});
</script>
</head>
<body>
<label for="privkey">Private Key</label><br/>
<textarea id="privkey" rows="15" cols="65"> </textarea><br/>
<label for="pubkey">Public Key</label><br/>
<textarea id="pubkey" rows="15" cols="65"> </textarea><br/>
<label for="input">Text to decrypt:</label><br/>
<textarea id="input" name="input" type="text" rows=4 cols=70>This is a test!</textarea><br/>
<input id="testme" type="button" value="Decrypt Me!!!" /><br/>
</body>
</html>
如果你使用 Crypto.Cipher.PKCS1_v1_5
是可以的。
这是 python 代码。
我只测试了 python 3,但我相信它在 python 2 中与 __future__
导入的效果应该相同。
from __future__ import unicode_literals, print_function # python2
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64
private_key = """-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBANBOMQo9wX55+w1ijEaPoYRP2T4BOjoFv3ma0QWqYYQ8FH0z14Zc
B/jb0j2PWpyNcsUUBovj+yWxQnQohCck64kCAwEAAQJBAL4s9PbNpO9MfFkfBMSS
8zoyEDtcsYUxpDtojbandDpdXfvn5D279QaOVLb1C3DgQTTEmroYB8dbeZBc5YJC
2AECIQDqyUn68ehRcx/EyLMUB1IuckZBWCIApgfn7phgVwSwiQIhAOMgY4bN+xrx
UV15Ian4ZbkME1IbAvDPcWuNGHxdsaMBAiBoz0K/S44yDfp4lj+bCUmeglTqhrVn
JLcSymgrWa02QQIhAMJFvPvcilGkYl1atCHHt3LN0mTjd+N0/OXq3SvblIsBAiAc
8RzaV1GmjMEJxw9vM/tQwQg0kyAPlITMRXnwGA6E0A==
-----END RSA PRIVATE KEY-----"""
rsa = RSA.importKey(private_key)
cipher = PKCS1_v1_5.new(rsa)
def encrypt(msg):
ciphertext = cipher.encrypt(msg.encode('utf8'))
return base64.b64encode(ciphertext).decode('ascii')
def decrypt(msg):
ciphertext = base64.b64decode(msg.encode('ascii'))
plaintext = cipher.decrypt(ciphertext, b'DECRYPTION FAILED')
return plaintext.decode('utf8')
ciphertext = encrypt('hello Whosebug!')
print(ciphertext)
plaintext = decrypt(ciphertext)
print(plaintext)
明文输出密文样本"hello Whosebug!":
tZDRXXcf7ppbVr9JBHQ3+2k3geofl8BdDmLT3HRoqBGvfknY+xISbvy5hYH2alPAUDu2ae4iSYsLyRFBOnzpgw==
在javascript中,原始代码应该可以工作。这是一个简化版本,用于证明此特定密文和私有 rsa 密钥有效。
const decrypt = () => {
const privateKey = document.getElementById('private_key').value
const cipherText = document.getElementById('ciphertext').value
const decrypt = new JSEncrypt()
decrypt.setPrivateKey(privateKey)
const plainText = decrypt.decrypt(cipherText) || 'DECRYPTION FAILED'
document.getElementById('plaintext').innerHTML = plainText
}
document.querySelector('button').addEventListener('click', decrypt)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsencrypt/2.3.1/jsencrypt.min.js"></script>
<button>Decrypt</button>
<p>plaintext: <em id=plaintext></em> </p>
<label>ciphertext:</label><br>
<textarea cols=64 rows=3 id=ciphertext>
tZDRXXcf7ppbVr9JBHQ3+2k3geofl8BdDmLT3HRoqBGvfknY+xISbvy5hYH2alPAUDu2ae4iSYsLyRFBOnzpgw==
</textarea>
<br>
<label>private key:</label><br>
<textarea cols=64 rows=10 id=private_key>
-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBANBOMQo9wX55+w1ijEaPoYRP2T4BOjoFv3ma0QWqYYQ8FH0z14Zc
B/jb0j2PWpyNcsUUBovj+yWxQnQohCck64kCAwEAAQJBAL4s9PbNpO9MfFkfBMSS
8zoyEDtcsYUxpDtojbandDpdXfvn5D279QaOVLb1C3DgQTTEmroYB8dbeZBc5YJC
2AECIQDqyUn68ehRcx/EyLMUB1IuckZBWCIApgfn7phgVwSwiQIhAOMgY4bN+xrx
UV15Ian4ZbkME1IbAvDPcWuNGHxdsaMBAiBoz0K/S44yDfp4lj+bCUmeglTqhrVn
JLcSymgrWa02QQIhAMJFvPvcilGkYl1atCHHt3LN0mTjd+N0/OXq3SvblIsBAiAc
8RzaV1GmjMEJxw9vM/tQwQg0kyAPlITMRXnwGA6E0A==
-----END RSA PRIVATE KEY-----
</textarea>
我对密码学完全陌生。我想从服务器端生成 RSA 密钥对并将其发送到所有客户端(浏览器)。但在此之前,我通过简单地在 python 中加密数据并通过 pubnub 发送到 index.html 文件并尝试在 JavaScript 中解密来测试场景。问题是当我通过;
进行加密时random_generator = Random.new().read
key = RSA.generate(1024, random_generator)
print key.exportKey() #<--private key
public_key = key.publickey()
print public_key.exportKey() #<--public key
msg = "hello"
enc_data = public_key.encrypt(msg, 32)
print '----ENCRYPTED DATA----'
enc = enc_data[0]
并发送加密数据 enc,它给我这个错误:
UnicodeDecodeError: 'utf8' codec can't decode byte 0xc4 in position 2: invalid continuation byte
我试着把它转换成
enc = base64.b64encode(enc_data[0])
并且发送没有错误。但是 JS 解密方法得到 None
var enc_from_python = $('#input').val();
console.log("ENCRYPTED data:", enc_from_python);
var decrypt = new JSEncrypt();
decrypt.setPrivateKey($('#privkey').val());
var uncrypted = decrypt.decrypt(enc_from_python);
console.log(">>>",uncrypted); //<-- this is None ! why ?
这两段代码本身 enc/dec 都做得很好。我还尝试使用从 python 接收到的密钥对 enc/dec JS 中的数据,并且效果很好。我想问题出在 Pycrypto 的编码数据的 unicode 编码格式不匹配。谁能告诉我我在这里错过了什么。
Python 的完整代码:
import time
from pubnub.pnconfiguration import PNConfiguration
from pubnub.pubnub import PubNub
from Crypto.PublicKey import RSA
from Crypto import Random
import base64
pnconfig = PNConfiguration()
pnconfig.subscribe_key = 'demo'
pnconfig.publish_key = 'demo'
channel = "my_channel"
pubnub = PubNub(pnconfig)
def my_publish_callback(envelope, status):
if not status.is_error():
pass # Message successfully published to specified channel.
else:
pass # Handle message publish error. Check 'category' property to find out possible issue
time.sleep(1)
random_generator = Random.new().read
key = RSA.generate(1024, random_generator)
print key.exportKey() #<--private key
public_key = key.publickey()
print public_key.exportKey() #<--public key
msg = "hello"
enc_data = public_key.encrypt(msg, 32)
print '----ENCRYPTED DATA----'
#enc = enc_data[0]
enc = base64.b64encode(enc_data[0])
print enc
#print type(enc_data[0])
print '----ENCRYPTED DATA----'
print ''
print '----DECRYPTED DATA begin----'
print key.decrypt(enc_data[0])
print '----DECRYPTED DATA end----'
pubnub.publish().channel(channel).message({"data": enc , "private": (key.exportKey()), "public" : (public_key.exportKey())}).async(my_publish_callback)
完整的 JS 代码是;
<!doctype html>
<html>
<head>
<title>JavaScript RSA Encryption</title>
<script src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsencrypt/2.3.1/jsencrypt.min.js"></script>
<script src="https://cdn.pubnub.com/sdk/javascript/pubnub.4.12.0.min.js"></script>
<script type="text/javascript">
// Call this code when the page is done loading.
$(function() {
pubnub = new PubNub({
publish_key: 'demo',
subscribe_key: 'demo'
});
pubnub.subscribe({
channels: ['my_channel']
});
pubnub.addListener({
message: function(message) {
var msg = message.message;
console.log("msg:" + JSON.stringify(msg));
if (msg.private){
$("#privkey").val(msg.private);
}
if(msg.public){
$("#pubkey").val(msg.public);
}
if(msg.data){
$("#input").val(msg.data);
}
}
})
// Run a quick encryption/decryption when they click.
$('#testme').click(function() {
var enc_from_python = $('#input').val();
console.log("ENCRYPTED data:", enc_from_python);
// Decrypt with the private key...
var decrypt = new JSEncrypt();
decrypt.setPrivateKey($('#privkey').val());
var uncrypted = decrypt.decrypt(enc_from_python);
console.log(">>>",uncrypted); //<-- this is None ! why ?
// Now a simple check to see if the round-trip worked.
if (uncrypted == $('#input').val()) {
alert('It works!!!');
}
else {
alert('Something went wrong....');
}
});
});
</script>
</head>
<body>
<label for="privkey">Private Key</label><br/>
<textarea id="privkey" rows="15" cols="65"> </textarea><br/>
<label for="pubkey">Public Key</label><br/>
<textarea id="pubkey" rows="15" cols="65"> </textarea><br/>
<label for="input">Text to decrypt:</label><br/>
<textarea id="input" name="input" type="text" rows=4 cols=70>This is a test!</textarea><br/>
<input id="testme" type="button" value="Decrypt Me!!!" /><br/>
</body>
</html>
如果你使用 Crypto.Cipher.PKCS1_v1_5
是可以的。
这是 python 代码。
我只测试了 python 3,但我相信它在 python 2 中与 __future__
导入的效果应该相同。
from __future__ import unicode_literals, print_function # python2
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64
private_key = """-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBANBOMQo9wX55+w1ijEaPoYRP2T4BOjoFv3ma0QWqYYQ8FH0z14Zc
B/jb0j2PWpyNcsUUBovj+yWxQnQohCck64kCAwEAAQJBAL4s9PbNpO9MfFkfBMSS
8zoyEDtcsYUxpDtojbandDpdXfvn5D279QaOVLb1C3DgQTTEmroYB8dbeZBc5YJC
2AECIQDqyUn68ehRcx/EyLMUB1IuckZBWCIApgfn7phgVwSwiQIhAOMgY4bN+xrx
UV15Ian4ZbkME1IbAvDPcWuNGHxdsaMBAiBoz0K/S44yDfp4lj+bCUmeglTqhrVn
JLcSymgrWa02QQIhAMJFvPvcilGkYl1atCHHt3LN0mTjd+N0/OXq3SvblIsBAiAc
8RzaV1GmjMEJxw9vM/tQwQg0kyAPlITMRXnwGA6E0A==
-----END RSA PRIVATE KEY-----"""
rsa = RSA.importKey(private_key)
cipher = PKCS1_v1_5.new(rsa)
def encrypt(msg):
ciphertext = cipher.encrypt(msg.encode('utf8'))
return base64.b64encode(ciphertext).decode('ascii')
def decrypt(msg):
ciphertext = base64.b64decode(msg.encode('ascii'))
plaintext = cipher.decrypt(ciphertext, b'DECRYPTION FAILED')
return plaintext.decode('utf8')
ciphertext = encrypt('hello Whosebug!')
print(ciphertext)
plaintext = decrypt(ciphertext)
print(plaintext)
明文输出密文样本"hello Whosebug!":
tZDRXXcf7ppbVr9JBHQ3+2k3geofl8BdDmLT3HRoqBGvfknY+xISbvy5hYH2alPAUDu2ae4iSYsLyRFBOnzpgw==
在javascript中,原始代码应该可以工作。这是一个简化版本,用于证明此特定密文和私有 rsa 密钥有效。
const decrypt = () => {
const privateKey = document.getElementById('private_key').value
const cipherText = document.getElementById('ciphertext').value
const decrypt = new JSEncrypt()
decrypt.setPrivateKey(privateKey)
const plainText = decrypt.decrypt(cipherText) || 'DECRYPTION FAILED'
document.getElementById('plaintext').innerHTML = plainText
}
document.querySelector('button').addEventListener('click', decrypt)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsencrypt/2.3.1/jsencrypt.min.js"></script>
<button>Decrypt</button>
<p>plaintext: <em id=plaintext></em> </p>
<label>ciphertext:</label><br>
<textarea cols=64 rows=3 id=ciphertext>
tZDRXXcf7ppbVr9JBHQ3+2k3geofl8BdDmLT3HRoqBGvfknY+xISbvy5hYH2alPAUDu2ae4iSYsLyRFBOnzpgw==
</textarea>
<br>
<label>private key:</label><br>
<textarea cols=64 rows=10 id=private_key>
-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBANBOMQo9wX55+w1ijEaPoYRP2T4BOjoFv3ma0QWqYYQ8FH0z14Zc
B/jb0j2PWpyNcsUUBovj+yWxQnQohCck64kCAwEAAQJBAL4s9PbNpO9MfFkfBMSS
8zoyEDtcsYUxpDtojbandDpdXfvn5D279QaOVLb1C3DgQTTEmroYB8dbeZBc5YJC
2AECIQDqyUn68ehRcx/EyLMUB1IuckZBWCIApgfn7phgVwSwiQIhAOMgY4bN+xrx
UV15Ian4ZbkME1IbAvDPcWuNGHxdsaMBAiBoz0K/S44yDfp4lj+bCUmeglTqhrVn
JLcSymgrWa02QQIhAMJFvPvcilGkYl1atCHHt3LN0mTjd+N0/OXq3SvblIsBAiAc
8RzaV1GmjMEJxw9vM/tQwQg0kyAPlITMRXnwGA6E0A==
-----END RSA PRIVATE KEY-----
</textarea>