这是Java中加密的最佳/安全方法吗?

时间:2017-03-29 18:45:29

标签: java security aes

我是安全的新手,我想知道我是否可以让我的程序更好,比如更改或添加一些内容以使其更好(更安全)
(我对程序输出有疑问)

这是输出:

Encrypted Message: +g@þóv«5Ùû`ž   
keybyte: [B@71e7a66b   
Original string: Message   
Original string (Hex): [B@2ac1fdc4

以下是代码:

public class AES {

  public static void main(String ... args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
    final String Algo="AES";
    String key = "aaaaaaaaaaaaaaaa";
    byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);

    MessageDigest sha= MessageDigest.getInstance("SHA-1"); 
    keyBytes=sha.digest(keyBytes);
    keyBytes=Arrays.copyOf(keyBytes, 16);

    SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, Algo);
    Cipher cipher = Cipher.getInstance(Algo);
    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
    byte[] ciphertext = cipher.doFinal("Message".getBytes());
    System.out.println("Encrypted Message: " +new String(ciphertext));

    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
    byte[] original = cipher.doFinal(ciphertext);
    String originalString = new String(original);
    System.out.println("keybyte: "+keyBytes);
    System.out.println("Original string: " + originalString + "\nOriginal string (Hex): " +original);      
  }
}

2 个答案:

答案 0 :(得分:5)

不,这段代码非常可怕:

  • 您的键已修复,然后是一个字符串,然后进行哈希处理。这看起来像你key实际上应该是一个密码。单个哈希不足以从密码派生密钥。您需要使用强大的散列方案,如PBKDF2,bcrypt,scrypt和Argon2。一定要使用高成本因子/迭代计数。选择成本是很常见的,因此单次迭代至少需要100ms。查看更多:How to securely hash passwords?

  • 始终使用完全限定的密码字符串。 Cipher.getInstance("AES");可能会导致不同的密码,具体取决于默认的安全提供程序。它最有可能导致"AES/ECB/PKCS5Padding",但不一定如此。如果它发生变化,您将失去不同JVM之间的兼容性。供参考:Java default Crypto/AES behavior

  • ECB mode非常糟糕。它是确定性的,因此在语义上不安全。您应该至少使用CBCCTR等随机模式。

  • 未经身份验证,您有可能无法检测(恶意)密文修改。最好对您的密文进行身份验证,以便像padding oracle attack这样的攻击是不可能的。这可以通过GCM或EAX等经过身份验证的模式完成,也可以通过具有强哈希函数(如SHA-256)的HMAC使用encrypt-then-MAC方案完成。

由于这些问题,您应该使用库。试试JNCryptorthis library

答案 1 :(得分:5)

您的实施至少存在两个问题:

  • 除非另有说明,否则您在ECB mode中使用AES。无论底层的密码是什么,ECB模式都是不安全的。有许多安全模式,但通常人们实施CBC模式,这是通过将Algo更改为" AES / CBC / PKCS5Padding" (这真的是PKCS7填充,Java只是不知道更好)。然后,您需要通过SecureRandom()选择IV来加密这种方式。这个OWASP example似乎做对了(仅供参考 - 在网络上你会发现99%的实施都存在安全问题)。
  • 您的key不是密钥,而是密码被转换为密钥。你不应该硬编码,但我认为你只是为了概念验证而这样做。在任何情况下,问题是使用诸如SHA-1之类的散列函数将密码转换为密钥不是一个好的决定,因为密码往往具有低熵并且可以强制使用。因此,在将密码转换为密钥时,您应该使用专用于抵抗暴力的功能。这些功能包括pbkdf2,bcrypt,scrypt和argon2。有关更多信息,Troy Hunt(.Net家伙)在这种情况下给出了使用SHA-1(或SHA2系列中的任何内容)等问题的good overview

要记住的另一件事是加密通常不会提供消息完整性。这意味着仅仅因为您加密了数据并不意味着有人无法对其进行修改,并且您的软件仍然会解密修改过的数据而不会意识到它已被修改。如果您需要知道您的数据未被修改,那么您将需要添加类似HMAC的内容,否则您将需要切换到GCM等操作模式。

最后,AES是安全性的绝佳选择。但是你需要在正确的模式下使用它,你需要正确地实现它,你需要了解你是否需要更多而不仅仅是加密。