在Java加密和解密中处理IV和salt

时间:2017-07-03 06:25:06

标签: java encryption nullpointerexception

所以我尝试在方法中解密邮件,但它不起作用,因为在我尝试将cipher.init(Cipher.ENCRYPT_MODE, secret)添加到{{1}之前我需要new IvParameterSpec(iv) }。否则,它只返回一个NullPointerException,我想知道它是否可以在方法中执行此操作而不是一直写入它。我无法真正想到一个解决方案,这就是我在这里的原因。加密工作正常但不解密。

项目正在运行: JRE 7

加密代码:

cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));

解密代码:

public static String encrypt(String str) {
    try {
        SecureRandom random = new SecureRandom();
        byte[] salt = new byte[16];
        random.nextBytes(salt);

        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        KeySpec spec = new PBEKeySpec(str.toCharArray(), salt, 65536, 256);
        SecretKey tmp = factory.generateSecret(spec);
        SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secret); //<--- Need to do this before writing IvPerameterSpec,
        // But I think that it's not possible if I have it in another method.
        byte[] encryptedText = cipher.doFinal(str.getBytes("UTF-8"));

        return new String(encryptedText);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

例外:

public static String decrypt(String str) {
    try {
        SecureRandom random = new SecureRandom();
        byte[] salt = new byte[16];
        random.nextBytes(salt);

        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        KeySpec spec = new PBEKeySpec(str.toCharArray(), salt, 65536, 256);
        SecretKey tmp = factory.generateSecret(spec);
        SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

        AlgorithmParameters params = cipher.getParameters();
        byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
        cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
        //                                                ^^^ Returns NullPointerException

        byte[] ciphertext = cipher.doFinal(str.getBytes("UTF-8"));
        String decryptedText = new String(cipher.doFinal(ciphertext), "UTF-8");

        return new String(decryptedText);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

我将方法更改为以下内容并且它们有效:

java.lang.NullPointerException
        at me.Sansanvi.Encryption.api.ComputerAPI.decrypt(ComputerAPI.java:149)
        at me.Sansanvi.Encryption.EncryptionMain.initializeFiles(EncryptionMain.java:46)
        at me.Sansanvi.Encryption.EncryptionMain.<init>(EncryptionMain.java:36)
        at me.Sansanvi.Encryption.EncryptionMain$1.run(EncryptionMain.java:23)
        at java.awt.event.InvocationEvent.dispatch(Unknown Source)
        at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
        at java.awt.EventQueue.access$200(Unknown Source)
        at java.awt.EventQueue$3.run(Unknown Source)
        at java.awt.EventQueue$3.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
        at java.awt.EventQueue.dispatchEvent(Unknown Source)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
        at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
        at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
        at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
        at java.awt.EventDispatchThread.run(Unknown Source)

我不会再收到任何错误/异常,但是当我关闭我的应用程序时,我会在控制台中看到它:

private static final String ALGORITHM = "AES";
    public static byte[] encrypt(byte[] str) {
    try {
        SecretKeySpec secretKey = new SecretKeySpec("MZygpewJsCpRrfOr".getBytes(StandardCharsets.UTF_8), ALGORITHM);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);

        return cipher.doFinal(str);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}
public static byte[] decrypt(byte[] str) {
    try {
        SecretKeySpec secretKey = new SecretKeySpec("MZygpewJsCpRrfOr".getBytes(StandardCharsets.UTF_8), ALGORITHM);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, secretKey);

        return cipher.doFinal(str);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

2 个答案:

答案 0 :(得分:6)

您至少有四个问题而且您只发现其中一个问题。

  1. IV在 en 加密期间生成并使用。在 de 加密期间需要使用完全相同的IV。 IV不应该是秘密的。您可以简单地将其与密文一起发送/存储。通常,IV存储在密文的前面并在解密之前切掉 对于CBC模式,它只需要是不可预测的(读取:随机)。对于CTR模式,它需要是唯一的(当使用相同的密钥时)。

  2. 随机盐需要以与IV完全相同的方式处理:它不是秘密的,可以写在密文之前。

  3. String不是二进制数据的容器。当您使用new String(encryptedText)时,您可能会丢失一些不可打印的字节,这会破坏您的密文并使明文无法恢复。您需要使用类似Base64或Hex编码的内容将二进制数据表示为可打印文本。

  4. 如果加密某些内容,则需要两件事:明文和密码(用于密钥派生)。解密过程中还需要两件事:密文和密码(密钥)。您在加密期间使用相同的字符串值。然后,密文会被破坏,使得在解密期间需要原始字符串值。这将首先打败加密它的目的。您的加密方法基本上是一个哈希函数 感谢wittyameta指出这一点。

  5. 因此生成的代码看起来与此类似:

    public static String encrypt(String str, String password) {
        try {
            SecureRandom random = new SecureRandom();
            byte[] salt = new byte[16];
            random.nextBytes(salt);
    
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256);
            SecretKey tmp = factory.generateSecret(spec);
            SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
    
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secret);
            AlgorithmParameters params = cipher.getParameters();
            byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
            byte[] encryptedText = cipher.doFinal(str.getBytes("UTF-8"));
    
            // concatenate salt + iv + ciphertext
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            outputStream.write(salt);
            outputStream.write(iv);
            outputStream.write(encryptedText);
    
            // properly encode the complete ciphertext
            return DatatypeConverter.printBase64Binary(outputStream.toByteArray());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    public static String decrypt(String str, String password) {
        try {
            byte[] ciphertext = DatatypeConverter.parseBase64Binary(str);
            if (ciphertext.length < 48) {
                return null;
            }
            byte[] salt = Arrays.copyOfRange(ciphertext, 0, 16);
            byte[] iv = Arrays.copyOfRange(ciphertext, 16, 32);
            byte[] ct = Arrays.copyOfRange(ciphertext, 32, ciphertext.length);
    
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256);
            SecretKey tmp = factory.generateSecret(spec);
            SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    
            cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
            byte[] plaintext = cipher.doFinal(ct);
    
            return new String(plaintext, "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    

    我已将this用于兼容Java 7的Base64编码/解码。

答案 1 :(得分:-1)

您没有正确初始化密码首先需要实例(正确执行):

<div class="flex-outer">
  <div class="dashboard">
    <ul class="flex-container">
      <li class="flex-item">1</li>
      <li class="flex-item">2</li>
      <li class="flex-item">3</li>
      <li class="flex-item">4</li>
      <li class="flex-item">5</li>
      <li class="flex-item">6</li>
    </ul>
  </div>

  <div class="col">

  </div>
</div>

然后你需要初始化它有多个init方法:

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

在你的情况下,你想使用这个:

public void init(int opmode, Key key);

public void init(int opmode, Certificate certificate);

public void init(int opmode, Key key, SecureRandom random);

public void init(int opmode, Certificate certificate,
                 SecureRandom random);

public void init(int opmode, Key key,
                 AlgorithmParameterSpec params);

public void init(int opmode, Key key,
                 AlgorithmParameterSpec params, SecureRandom random);

public void init(int opmode, Key key,
                 AlgorithmParameters params);

public void init(int opmode, Key key,
                 AlgorithmParameters params, SecureRandom random);

但是,在init之前调用public void init(int opmode, Key key, AlgorithmParameterSpec params); 始终返回null。有你的NullPointerException。

此外,代码中存在更多错误,例如使用数据加密为密钥。