在Java中加密字符串,在Node.js中解密,出现错误:解密失败。

7
我可以帮你翻译成中文。以下是您需要翻译的内容:

我正在尝试在Java中加密一个字符串,将其发送到我的Node.js服务器并进行解密。但是,每当我尝试执行此操作时,解密时都会出现错误。

Java加密:

String privateKey = "someprivatekey";
String data = "dataToEncrypt";

DESKeySpec keySpec = new DESKeySpec(privateKey.getBytes("UTF-8"));
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey key = keyFactory.generateSecret(keySpec);

byte[] dataToBytes = data.getBytes("UTF-8");      

Cipher cipher = Cipher.getInstance("DES"); 
cipher.init(Cipher.ENCRYPT_MODE, key);

// send this string to server 
String encryptedStr = Base64.encodeToString(cipher.doFinal(dataToBytes), 0);

Node.js 解密:

var privateKey = 'someprivatekey';
var decipher = crypto.createDecipher('des', privateKey);
var dec = decipher.update(textToDecipher, 'base64', 'utf8'); 
dec += decipher.final('utf8'); 
console.log('deciphered: ' + dec);

然而,我在node.js端的decipher.final()行收到了这个错误:
TypeError: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt

我尝试对字符串进行解码并在节点端使用缓冲区,但也没有起作用,我仍然得到相同的解密错误。

var buffer = new Buffer(textToDecipher, 'base64');
var decipher = crypto.createDecipher('des', privateKey);
var dec = Buffer.concat([decipher.update(buffer), decipher.final()]);

你有什么想法我可能做错了什么吗?


"someprivatekey" 不是有效的 DES 密钥。你实际使用的密钥长度是否更短? - Artjom B.
我认为node.js/java默认使用不同的填充方式。尝试明确指定填充方式。 - user1516873
@ArtjomB。是的,它更短,但在创建DES密钥时字符串长度是否重要? - danjonesgh
是的,DES仅针对8字节密钥(带奇偶校验)进行了规定。现在请不要使用DES。它很容易被一些EC2时间暴力破解。如果您真的想使用DES,至少应该使用Triple DES(3DES)或其他AES。此外,请永远不要使用ECB模式。它不是语义安全的。请至少使用带有随机IV的CBC模式。 - Artjom B.
2个回答

5
当您在节点服务器上创建解密对象时,传递的是密码而不是实际密钥。要指定实际密钥,您需要使用crypto.createDecipheriv(),但这需要一个实际的IV(此示例使用8个空字节,但不建议用于真正的加密; 维基百科上的初始化向量)。
我成功地让您的示例运行起来,方法是显式指定填充、块模式和IV。
String privateKey = "someprivatekey";
String data = "dataToEncrypt";

DESKeySpec keySpec = new DESKeySpec(privateKey.getBytes("UTF-8"));
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey key = keyFactory.generateSecret(keySpec);

byte[] dataToBytes = data.getBytes("UTF-8");      

Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); 
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[8]));

// send this string to server 
String encryptedStr = Base64.encodeToString(cipher.doFinal(dataToBytes), 0);

解密版本还明确指定了块模式(des-cbc),并将密钥文本正确地截断为前8个字节。
var privateKey = 'someprivatekey';
var textToDecipher = '9Y8GTNxhQkKSIm5pmH91VA=='; // Text "dataToEncrypt" encrypted using DES using CBC and PKCS5 padding with the key "someprivatekey"

var iv = new Buffer(8);
iv.fill(0);

var decipher = crypto.createDecipheriv('des-cbc', privateKey.substr(0,8), iv);
var dec = decipher.update(textToDecipher, 'base64', 'utf8');
dec += decipher.final('utf8');
console.log('deciphered: ' + dec);

1
Java加密的DES默认模式是ECB,在nodejs中需要指定:
var decipher = crypto.createDecipheriv('des-ecb', key, new Buffer(0))
var txt = decipher.update(encrypt_text, 'hex', 'utf8');
txt += decipher.final('utf8')
return txt

虽然像tombrown52回答中所建议的同时在两端更改为CBC会更好。 - dave_thompson_085

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接