首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用加密js库在客户端加密消息并在Java服务器上解密

如何使用加密js库在客户端加密消息并在Java服务器上解密
EN

Stack Overflow用户
提问于 2014-11-27 22:55:17
回答 3查看 10.7K关注 0票数 0

背景:我正在处理的应用程序应该脱机工作。我有一个HTML5页面,用户输入的数据使用密码-js库加密。我希望加密的消息被发送到,然后在服务器端解密它。

我可以使用Crypto-js加密消息

代码语言:javascript
复制
<code>
var message = "my message text";
var password = "user password";
var encrypted = CryptoJS.AES.encrypt( message ,password );
console.log(encrypted.toString());
// this prints an encrypted text "D0GBMGzxKXU757RKI8hDuQ=="
</code>

我想要做的是将加密的文本"D0GBMGzxKXU757RKI8hDuQ==“传递给java服务器端代码,并对加密的消息进行解密。

我在java服务器端尝试了许多方法来解密加密消息。请在服务器端找到我的代码,以便对加密的文本进行解密。

代码语言:javascript
复制
<code>
public static String decrypt(String keyText,String encryptedText) 
{
// generate key 
Key key = new SecretKeySpec(keyText.getBytes(), "AES");
Cipher chiper = Cipher.getInstance("AES");
chiper.init(Cipher.DECRYPT_MODE, key);
byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedText);
byte[] decValue = chiper.doFinal(decordedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
}  
</code>

我从下面的代码调用java方法解密。

代码语言:javascript
复制
<code>
// performs decryption 
public static void main(String[] args) throws Exception 
{
String decryptedText = CrypterUtil.decrypt("user password","D0GBMGzxKXU757RKI8hDuQ==");
}
</code>

但是,当我运行java解密代码时,我会得到以下异常

代码语言:javascript
复制
<code>
Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 13 bytes
at com.sun.crypto.provider.AESCipher.engineGetKeySize(AESCipher.java:372)
at javax.crypto.Cipher.passCryptoPermCheck(Cipher.java:1052)
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1010)
at javax.crypto.Cipher.implInit(Cipher.java:786)
at javax.crypto.Cipher.chooseProvider(Cipher.java:849)
at javax.crypto.Cipher.init(Cipher.java:1213)
at javax.crypto.Cipher.init(Cipher.java:1153)
at au.gov.daff.pems.model.utils.CrypterUtil.decrypt(CrypterUtil.java:34)
at au.gov.daff.pems.model.utils.CrypterUtil.main(CrypterUtil.java:47)
Process exited with exit code 1.
</code>

我不知道我做错了什么?使用密码-js库对消息进行加密的最佳方法是什么,以便在使用用户输入密码的地方对其进行解密。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-12-02 23:29:03

感谢Artjom B和艾萨克·波特克兹尼-琼斯的及时回应和建议。为了其他人的利益,我给出了在下面为我工作的完整解决方案。

Java代码用于在Java服务器端对加密的at加密消息进行解密

代码语言:javascript
复制
public static void main(String args[]) throws Exception{

    String password = "Secret Passphrase";
    String salt = "222f51f42e744981cf7ce4240eeffc3a";
    String iv = "2b69947b95f3a4bb422d1475b7dc90ea";
    String encrypted = "CQVXTPM2ecOuZk+9Oy7OyGJ1M6d9rW2D/00Bzn9lkkehNra65nRZUkiCgA3qlpzL";

    byte[] saltBytes = hexStringToByteArray(salt);
    byte[] ivBytes = hexStringToByteArray(iv);
    IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);        
    SecretKeySpec sKey = (SecretKeySpec) generateKeyFromPassword(password, saltBytes);
    System.out.println( decrypt( encrypted , sKey ,ivParameterSpec));
}

public static SecretKey generateKeyFromPassword(String password, byte[] saltBytes) throws GeneralSecurityException {

    KeySpec keySpec = new PBEKeySpec(password.toCharArray(), saltBytes, 100, 128);
    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    SecretKey secretKey = keyFactory.generateSecret(keySpec);

    return new SecretKeySpec(secretKey.getEncoded(), "AES");
}

public static byte[] hexStringToByteArray(String s) {

    int len = s.length();
    byte[] data = new byte[len / 2];

    for (int i = 0; i < len; i += 2) {
        data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                + Character.digit(s.charAt(i+1), 16));
    }

    return data;
}

public static String decrypt(String encryptedData, SecretKeySpec sKey, IvParameterSpec ivParameterSpec) throws Exception { 

    Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
    c.init(Cipher.DECRYPT_MODE, sKey, ivParameterSpec);
    byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedData);
    byte[] decValue = c.doFinal(decordedValue);
    String decryptedValue = new String(decValue);

    return decryptedValue;
}

可以在客户端执行加密和解密的加密at javascript代码。

代码语言:javascript
复制
function  generateKey(){
    var salt = CryptoJS.lib.WordArray.random(128/8);
    var iv = CryptoJS.lib.WordArray.random(128/8);
    console.log('salt  '+ salt );
    console.log('iv  '+ iv );
    var key128Bits100Iterations = CryptoJS.PBKDF2("Secret Passphrase", salt, { keySize: 128/32, iterations: 100 });
    console.log( 'key128Bits100Iterations '+ key128Bits100Iterations);
    var encrypted = CryptoJS.AES.encrypt("Message", key128Bits100Iterations, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7  });
}

function  decrypt(){
    var salt = CryptoJS.enc.Hex.parse("4acfedc7dc72a9003a0dd721d7642bde");
    var iv = CryptoJS.enc.Hex.parse("69135769514102d0eded589ff874cacd");
    var encrypted = "PU7jfTmkyvD71ZtISKFcUQ==";
    var key = CryptoJS.PBKDF2("Secret Passphrase", salt, { keySize: 128/32, iterations: 100 });
    console.log( 'key '+ key);
    var decrypt = CryptoJS.AES.decrypt(encrypted, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
    var ddd = decrypt.toString(CryptoJS.enc.Utf8); 
    console.log('ddd '+ddd);
}
票数 3
EN

Stack Overflow用户

发布于 2014-11-27 23:29:31

你必须明白密码不是钥匙。密码通常要经过一些哈希函数才能得到一个位字符串或字节数组,这是一个键。它不能打印,所以它被表示为十六进制或base64。

在JavaScript中,您使用密码,但在Java中,您假设相同的密码是密钥,而不是密码。您可以确定CryptoJS如何散列密码才能到达密钥,并在Java中重新创建密码,但它的实现方式似乎是每次使用密码加密时生成新的盐,并且没有办法更改salt。

如果您真的想要从用户那里使用will密码,那么您需要自己派生密钥。CryptoJS为此提供了PBKDF2,但它也需要一个盐。您可以为应用程序生成一个,并将其添加到代码中。您将以这种方式生成它一次:

代码语言:javascript
复制
CryptoJS.lib.WordArray.random(128/8).toString();

每次都要将静态盐传递给基于密码的密钥派生函数(这里是AES-256),以派生密钥。

代码语言:javascript
复制
var key = CryptoJS.PBKDF2(userPassword,
        CryptoJS.enc.Hex.parse(salt),
        { keySize: 256/32, iterations: 1000 });
var iv = CryptoJS.lib.WordArray.random(256/8); // random IV
var encrypted = CryptoJS.AES.encrypt("Message", key, { iv: iv });

在服务器上,您需要将十六进制键字符串转换为字节数组。。您还需要将服务器上的方案从AES调整为AES/CBC/PKCS5Padding,因为它是默认在CryptoJS中。注意,PKCS5和PKCS7对于AES是一样的。

还请注意,您需要将IV从客户端传递到服务器,并将其作为

代码语言:javascript
复制
chiper.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ivBytes));

当然,您可以使用PBKDF的Java实现从服务器上的密码和salt重新创建密钥,也可以将密钥保存为已知的密码和salt。您可以使用PBKDF的迭代,这对用户来说是可以接受的。

票数 3
EN

Stack Overflow用户

发布于 2014-11-29 04:48:01

AES和相关的算法可以以多种不同的方式使用,当混合语言时,找出客户端正在使用的模式并将它们与服务器的模式相匹配总是有点棘手。

Java代码的第一个问题是不能将字符串的字节用作AES键。在互联网上有很多这样做的例子,但这是非常错误的。就像@artjom在CryptoJS代码中显示的那样,您需要使用“基于密码的密钥派生函数”,它还需要在客户机和服务器上对进行完全相同的参数化。

此外,客户端需要生成salt,并将其与密码文本一起发送;否则,服务器无法从给定的密码生成相同的密钥。我不确定CryptoJS是如何做到这一点的--这里有一些在Java中是合理的东西,在学习cryptoJS如何工作时,您可以调整参数:

代码语言:javascript
复制
public static SecretKey generateKeyFromPassword(String password, byte[] salt) throws GeneralSecurityException {
    KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 1000, 256);
    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
    return new SecretKeySpec(keyBytes, "AES");
}

使用AES CBC,您还需要随机生成一个IV,并将其与密码文本一起发送。

因此,总结如下:

  • 计算CryptoJS使用的AES参数。不确定它们是什么,但听起来像是:键大小(256)、填充(pkcs5)、模式(CBC)、PBE算法(PBKDF2)、salt (随机)、迭代计数(100)。
  • 使用相同的参数配置服务器
  • 使用PBE密钥生成器,以及一个非秘密(但随机)的盐。
  • 在非秘密(但随机) IV中使用AES CBC
  • 将密码文本、IV和salt发送到服务器
  • 然后在服务器端,使用salt、迭代计数和密码生成AES密钥。
  • 然后base64解码并解密它。
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27179685

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档