Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/338.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
JavascriptJava AES_Java_Javascript_Aes - Fatal编程技术网

JavascriptJava AES

JavascriptJava AES,java,javascript,aes,Java,Javascript,Aes,我正在尝试编写一个web应用程序,它使用AJAX上的AES加密与Java后端交互 我花了一些时间寻找和测试库,但没有一个是有效的 我使用以下Java代码使Java PHP正常工作: public static String encrypt(String input, String key){ IvParameterSpec ips = new IvParameterSpec("sixteenbyteslong".getBytes()); try { key = m

我正在尝试编写一个web应用程序,它使用AJAX上的AES加密与Java后端交互

我花了一些时间寻找和测试库,但没有一个是有效的

我使用以下Java代码使Java PHP正常工作:

public static String encrypt(String input, String key){
    IvParameterSpec ips = new IvParameterSpec("sixteenbyteslong".getBytes());
    try {
        key = md5(key);
    } catch (NoSuchAlgorithmException e1) {
        e1.printStackTrace();
    }
    byte[] crypted = null;
    try{
        SecretKeySpec skey = new SecretKeySpec(key.getBytes(), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, skey, ips);
        crypted = cipher.doFinal(input.getBytes());
    }catch(Exception e){
        System.out.println(e.toString());
    }
    return new String(Base64.encodeBase64(crypted));
}

public static String decrypt(String input, String key){
    IvParameterSpec ips = new IvParameterSpec("sixteenbyteslong".getBytes());
    try {
        key = md5(key);
    } catch (NoSuchAlgorithmException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }
    byte[] output = null;
    try{
        SecretKeySpec skey = new SecretKeySpec(key.getBytes(), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, skey,ips);
        output = cipher.doFinal(Base64.decodeBase64(input));
    }catch(Exception e){
        System.out.println(e.toString());
    }
    return new String(output);
}
Base64是org.apache.commons.codec.binary.Base64


我尝试了SlowAES,但它不支持PKCS5P添加,但即使存在这种情况,实际的加密也可能无法工作。

我查看了SlowAES,我认为您是正确的。它坏了

代码打算在CBC模式下操作时应用PKCS7填充,但未成功。PKCS7只是PKCS5扩展为16字节块加密算法。我认为java在AES中使用术语PKCS5是一个错误——他们应该称之为PKCS7,因为他们正在进行16字节的填充

我修改了slowAes以正确填充,通过修改,我在slowAes和Java代码之间获得了良好的互操作性。但是您需要修改Java代码。稍后再谈

下面是我使用的java代码:仅演示;不适合在实际应用程序中使用

JS模块的输出:

key       : 2e0160e078aa4b925e62b20610378253
iv        : 7369787465656e62797465736c6f6e67
plaintext : AbbaDabbaDo_Once_upon_a_time....
ciphertext: f353e4dd6fb11ea13254dfef670ad88f8fbebcd24217374c06daefbbfe152df504035ae2d82537392c9ab1f719993ec1
decrypted : AbbaDabbaDo_Once_upon_a_time....
JS代码:

var keystring = "keystring",
md5String = MD5.getDigest(keystring),
keybytes = cryptoHelpers.toNumbers(md5String), // <- NO NO NO!
iv = "sixteenbyteslong".getBytes(),  // <- NO NO NO
keysize, key = cryptoHelpers.toHex(keybytes),
plaintext, bytesToEncrypt, mode, result,
decrypted, recoveredText;
say("key       : " + key);
keysize = slowAES.aes.keySize.SIZE_128;
say("iv        : " + cryptoHelpers.toHex(iv));

plaintext = "AbbaDabbaDo_Once_upon_a_time....";

bytesToEncrypt = cryptoHelpers.convertStringToByteArray(plaintext);
mode = slowAES.modeOfOperation.CBC;
result = slowAES.encrypt(bytesToEncrypt,
                         mode,
                         keybytes,
                         keysize,
                         iv);

say( "plaintext : " + plaintext);
say( "ciphertext: " + cryptoHelpers.toHex(result.cipher));

decrypted = slowAES.decrypt(result.cipher,
                            result.mode,
                            keybytes,
                            keysize,
                            iv) ;

recoveredText = cryptoHelpers.convertByteArrayToString(decrypted);
say( "decrypted : " + recoveredText);
您可以获得我使用的修改后的AES源代码,以及

重要提示:您不应该使用密码短语的MD5来获取密钥字节。使用PBKDF2。有一个java版本的PBKDF2,它可以工作,还有一个。此外,一些J2EE服务器还包括PBKDF2类。对于IV字节也是如此。这些也应该来自密码短语。如果你对此表示怀疑,请说明理由

不要将我上面发布的代码用于真正的应用程序。将其修改为使用PBKDF2

编辑 关于填充

在解密返回的消息时,我将如何删除额外的填充,因为我不一定知道未加密的长度。我认为padding字节应该等于padding长度,但它们似乎不是

AES是块加密机;它加密长度正好为16字节的块。如果你输入32字节的明文,你会得到32字节的加密文本作为回报。如果输入1024字节,则输出1024字节。不完全正确,稍后你会明白原因的。现在就假设这是真的

正如您所看到的,当明文不是16字节的偶数倍时,因为AES需要16字节块,所以出现了一个问题-我应该添加什么作为额外的内容来生成完整的16字节块?答案是填充

有不同的是垫。在CBC模式下,典型的方式是PKCS7Java将其称为PKCS5,正如我所说的,我认为这是一个误称。如果您发送25字节的明文,填充意味着AES将实际加密32字节:25字节的实际数据和7字节的填充。好的,但是7字节的填充中有哪些值

PKCS7表示pad字节是值16 len,其中len是最后一个块中实际数据字节的长度。换句话说,该值与pad字节数相同,这就是您所说的。在上面的示例中,如果加密25个字节,则需要7个pad字节,每个pad字节的值为7。在加密之前,这些pad字节被添加到明文的末尾。它产生的加密流是由16字节块组成的整数

这很好,因为在解密时,解密程序只需查看解密流中的最后一个字节,现在就知道要从解密流中删除多少pad字节。使用PKCS7填充,应用层不需要担心在解密时删除填充或在加密时添加填充。AES库应该处理所有这些。假设解密程序解密32字节的加密文本,结果明文中的最后一个字节的值为7。使用PKCS7填充,解密程序知道从最后一个块的末尾切下7个字节,并为最后一个块向应用程序交付9个字节的部分块,总共25个字节的明文

Java正确地做到了这一点。slowAES做得很正确,除了明文长度是16字节的倍数的情况。PKCS7表示,在这种情况下,需要添加16字节的填充,所有填充的值都是16。如果你想加密32个字节,PKCS7 for AES说,你需要添加16个字节的pad,总共加密48个字节。这样解密程序就可以做正确的事情。想想看:如果不添加16字节的填充,解密程序就无法判断明文的最后一个字节不是填充字节

在本例中,SlowAES没有填充,这是您无法让它与Java互操作的部分原因。我注意到了这一点,因为对于32字节的明文,加密流正好是32字节,这意味着没有填充。当我查看代码时,逻辑错误就在那里。提醒我:我需要验证slowaes中是否存在关于解密端填充的逻辑错误

所以你是对的 您的应用程序不知道未加密的长度。但是,如果使用PKCS7填充,解密程序确实知道要切掉多少字节,正确的解密程序将始终向您返回正确的明文字节数。要使其正常工作,解密时必须使用与加密时相同的填充约定。您通常需要告诉加密库要使用什么填充,尽管像slowAES这样的一些人没有给您选择权

如果在CBC模式下,在某些库中使用非填充选项,但在slowAES中不使用,那么,是的,您的应用程序必须知道未加密数据的大小,以便可以丢弃最后N个字节的明文。在某些数据格式和协议中,这是正常的。但通常使用PKCS7填充更容易

编辑

再看一遍,是的,slowAES中的解密逻辑也存在填充问题。它希望你通过解密的长度-我现在明白了,我明白了这就是你提问的原因。如果正确填充PKCS7,则不需要这样做。现在不是。应该是一个简单的解决方案。稍后将在此更新

编辑

好的,更新的AES文件现在可用;更新的测试代码是。它在加密和解密时正确填充PKCS7。我可能应该把这些更改发送回slowAES的所有者

编辑


哦,还有一件事:

我正在创建一个可以通过HTTP轻松访问的API,它要求速度快。SSL会减慢请求速度并使客户端的设置复杂化。与使用AES设置的SSL相比,我有一个固有的优势,即有一个共享的秘密,客户端和服务器都知道密钥,因此请求是一个简单的调用->响应,而不是执行握手、证书检查以及最终的传输。对于长寿命的连接,SSL是一个明智的选择,但我重视快速响应时间。AES是有道理的,但我想我可以相信不是这样的。我会先遵循KISS,然后优化是否存在性能问题,以及是否已经证明它来自SSL层。此外,您将如何分发您的密钥?如果钥匙被泄露了怎么办?客户将如何保护他们?如果他们必须在HTML页面中硬编码,它们将很快公开。API基于用户名/密码。在加载时,密码永远不会存储在页面中,但在javascript的情况下,将由用户输入。SSL对于短请求来说是一个巨大的开销,简单地说,这是由操作原理决定的。有人对问题中描述的AES情况有什么建议吗?绝对令人惊讶。你救了我,也救了很多其他人,他们发现这个问题花了很多时间。我现在有了JavaJavaScript,但PHP加密不再有效。我想这只是我需要换的填充物?它与以前的Java实现一起工作。我没有改变Java程序中的填充;我把它保持原样了。但是我确实更改了字符串的编码——在我的Java代码中,它使用十六进制编码,而您使用的是Base64。因此,您可能希望在Java程序中切换回该选项,并用base64等价物替换JS程序中的toHex。您需要为Javascript获取Base64编码器。有一个在。一定要拿到PKBDF2密钥生成器。到目前为止,你帮了我这么多忙,问其他问题似乎是不礼貌的,但我认为事情并没有那么复杂。在解密返回的消息时,我将如何删除额外的填充,因为我不一定知道未加密的长度。我认为padding字节应该等于padding长度,但它们似乎不是。谢谢。我的回复太长,无法放在这里,所以我编辑了上面的答案。祝你好运问是不礼貌的。如果我不想回答,我不会。我说过你应该使用PBKDF2吗?@Cheeso-我无法打开你共享的测试URL。我遇到了一个类似的问题,Java向我发送加密文本,我需要在UI上使用javascript对其进行解密。你能分享一个地方,在那里你有完整的Javascript代码,我可以用它来解密。除了JS代码之外,您还使用其他库吗
key       : 2e0160e078aa4b925e62b20610378253
iv        : 7369787465656e62797465736c6f6e67
plaintext : AbbaDabbaDo_Once_upon_a_time....
ciphertext: f353e4dd6fb11ea13254dfef670ad88f8fbebcd24217374c06daefbbfe152df504035ae2d82537392c9ab1f719993ec1
decrypted : AbbaDabbaDo_Once_upon_a_time....
var keystring = "keystring",
md5String = MD5.getDigest(keystring),
keybytes = cryptoHelpers.toNumbers(md5String), // <- NO NO NO!
iv = "sixteenbyteslong".getBytes(),  // <- NO NO NO
keysize, key = cryptoHelpers.toHex(keybytes),
plaintext, bytesToEncrypt, mode, result,
decrypted, recoveredText;
say("key       : " + key);
keysize = slowAES.aes.keySize.SIZE_128;
say("iv        : " + cryptoHelpers.toHex(iv));

plaintext = "AbbaDabbaDo_Once_upon_a_time....";

bytesToEncrypt = cryptoHelpers.convertStringToByteArray(plaintext);
mode = slowAES.modeOfOperation.CBC;
result = slowAES.encrypt(bytesToEncrypt,
                         mode,
                         keybytes,
                         keysize,
                         iv);

say( "plaintext : " + plaintext);
say( "ciphertext: " + cryptoHelpers.toHex(result.cipher));

decrypted = slowAES.decrypt(result.cipher,
                            result.mode,
                            keybytes,
                            keysize,
                            iv) ;

recoveredText = cryptoHelpers.convertByteArrayToString(decrypted);
say( "decrypted : " + recoveredText);
    if (mode == this.modeOfOperation.CBC) {
        padLength = 16 - (bytesIn.length % 16);
    }
    // the AES input/output
    if (bytesIn !== null)
    {
        for (var j = 0;j < Math.ceil((bytesIn.length + padLength)/16); j++)
        {
        ....