java - 尝试解密加密数据时标签不匹配

标签 java encryption

我正在为我的 Java 编程类(class)开发一个项目来创建密码管理器,并且我正在致力于加密和解密我的密码。

我的加密部分工作正常,但我不断收到 javax.crypto.AEADBadTagException: 标签不匹配!错误。

这是完整的错误:

线程“main”中出现异常 javax.crypto.AEADBadTagException:标签不匹配! 在 java.base/com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:580) 在 java.base/com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1116) 在 java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1053) 在 java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853) 在 java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446) 在 java.base/javax.crypto.Cipher.doFinal(Cipher.java:2202) 在PasswordVault.Decrypt(PasswordVault.java:89) 在PasswordVault.main(PasswordVault.java:27)

我一直试图通过在这里的研究来自己找出这个错误,但我没有太多运气或不了解出了什么问题。

这是我的主课:

import java.io.UnsupportedEncodingException;
import java.security.*;
import java.util.ArrayList;
import java.util.Scanner;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import java.util.Base64;

public class PasswordVault {
    private static ArrayList<Password> passwordVault;
    public Scanner keyboard = new Scanner(System.in);
    public String website;
    public String username;
    private String password;
    private SecretKey key;

public static void main(String[] args) throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, UnsupportedEncodingException, InvalidKeyException, NoSuchProviderException, InvalidAlgorithmParameterException {
    passwordVault = new ArrayList<>();
    addPassword();
    System.out.println(passwordVault);
    //byte [] passwordByte = passwordVault.get(0).getPassword().getBytes();
    System.out.println(Decrypt(passwordVault.get(0).getPassword(),generateKey()));
}


public static void addPassword() throws NoSuchPaddingException, BadPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, UnsupportedEncodingException, InvalidKeyException, NoSuchProviderException, InvalidAlgorithmParameterException {
    Scanner keyboard = new Scanner(System.in);
    String website;
    String username;
    String password;
    SecretKey key = null;

    System.out.println("Please enter in the website that you would like to store a password:");
    website = keyboard.nextLine();
    System.out.println("Please enter your username");
    username = keyboard.nextLine();
    System.out.println("Please enter your password");
    password = keyboard.nextLine();
    key = generateKey();
    savePassword(website,username,password,key);
}

private static ArrayList<Password>savePassword(String website, String username, String password, SecretKey key) throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, UnsupportedEncodingException, InvalidKeyException, NoSuchProviderException, InvalidAlgorithmParameterException {
    String encryptedPassword;
    encryptedPassword = Encrypt(password,key);
    String stringEncryptedPassword = new String(encryptedPassword);
    Password savePass = new Password(website, username, stringEncryptedPassword);
    passwordVault.add(savePass);
    return passwordVault;
}

public static SecretKey generateKey() throws NoSuchPaddingException, NoSuchAlgorithmException {
    SecureRandom random = SecureRandom.getInstanceStrong();
    KeyGenerator keyGen = KeyGenerator.getInstance("AES");
    keyGen.init(128, random);
    SecretKey key = keyGen.generateKey();
    return key;
}
private static String Encrypt(String password, SecretKey key) throws UnsupportedEncodingException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchProviderException {
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "SunJCE");
    byte[] iv = new byte[12];
    SecureRandom random = SecureRandom.getInstanceStrong();
    random.nextBytes(iv);
    GCMParameterSpec spec = new GCMParameterSpec(128, iv);
    cipher.init(Cipher.ENCRYPT_MODE, key, spec);
    byte [] bytePassword = password.getBytes("UTF-8");
    byte [] encryptedPassword = cipher.doFinal(bytePassword);
    /*Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    cipher.init(Cipher.ENCRYPT_MODE, key);
    byte [] bytePassword = password.getBytes("UTF-8");
    byte [] encryptedPassword = cipher.doFinal(bytePassword);
    return Base64.getEncoder().encodeToString(encryptedPassword);*/
    //return encryptedPassword;
    return Base64.getEncoder().encodeToString(encryptedPassword);
}

private static String Decrypt(String password, SecretKey key) throws UnsupportedEncodingException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException, NoSuchProviderException, InvalidAlgorithmParameterException {
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "SunJCE");
    byte[] iv = new byte[12];
    SecureRandom random = SecureRandom.getInstanceStrong();
    random.nextBytes(iv);
    GCMParameterSpec spec = new GCMParameterSpec(128, iv);
    cipher.init(Cipher.DECRYPT_MODE, key, spec);
    return new String(cipher.doFinal(Base64.getDecoder().decode(password)));
}
//byte [] byteDecryptPassword = cipher.doFinal(password);
// String newPassword = new String(password, "UTF-8");
//Cipher cipher = null;
//cipher = Cipher.getInstance("AES/GCM/NoPadding");
//cipher.init(Cipher.DECRYPT_MODE, key);
    /*byte [] bytePassword = new byte[0];
    bytePassword = password.getBytes("UTF-8");
    byte [] encryptedPassword = new byte[0];
    encryptedPassword = cipher.doFinal(bytePassword);*/

}

这是我的密码对象:

public class Password {
    String website;
    String login;
    String password;

public Password(String website, String login, String password) {
    this.website = website;
    this.login = login;
    this.password = password;
}

public String getWebsite() {
    return website;
}

public void setWebsite(String website) {
    this.website = website;
}

public String getLogin() {
    return login;
}

public void setLogin(String login) {
    this.login = login;
}

public String getPassword() {
    return password;
}

public void setPassword(String password) {
    this.password = password;
}

@Override
public String toString(){
    return "Website: " + website + " Login: " + login + " Password: " + password;
}


}

我现在希望通过测试得到的是我输入的密码的解密纯文本版本。现在,我只是不断收到此 BadTag 问题。

非常感谢任何帮助。

最佳答案

为了让您了解如何对此进行编程,我们向您展示一个新的 PasswordVault

您自己的新代码片段当然也可以工作,目前回避了密码存储所需的设计;它是一个通用的 GCM 示例,不存储 IV,没有保险库等(如果您无法让 GCM 工作,这可能是一个好主意)。

请注意,以下代码片段仍然不是安全保管库,但它显示了如何以 OO 方式对保管库进行编程。

import static java.nio.charset.StandardCharsets.UTF_8;

import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Scanner;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;

public class PasswordVault {
    private static final int KEY_SIZE_BITS = 128;
    private static final int GCM_TAG_SIZE_BITS = 128;
    private static final int GCM_IV_SIZE_BYTES = 12;
    private ArrayList<PasswordEntry> vaultBoxes;
    private SecretKey key;

    public PasswordVault() throws NoSuchAlgorithmException {
        vaultBoxes = new ArrayList<>();
        key = generateKey();
    }

    public void encryptAndStorePasswordEntry(PasswordEntry passwordEntry) throws GeneralSecurityException {
        String encryptedPassword = encrypt(passwordEntry.getPassword(), key);
        PasswordEntry savePass = new PasswordEntry(passwordEntry.getWebsite(), passwordEntry.getLogin(),
                encryptedPassword);
        vaultBoxes.add(savePass);
    }

    public PasswordEntry retrieveAndDecryptPasswordEntry() throws GeneralSecurityException {
        // TODO think of a way to retrieve the password for a specific entry
        PasswordEntry encryptedPasswordEntry = vaultBoxes.get(0);
        String password = decrypt(encryptedPasswordEntry.getPassword(), key);
        return new PasswordEntry(encryptedPasswordEntry.getWebsite(), encryptedPasswordEntry.getLogin(), password);
    }

    public static SecretKey generateKey() throws NoSuchAlgorithmException {
        SecureRandom random = SecureRandom.getInstanceStrong();
        KeyGenerator keyGen = KeyGenerator.getInstance("AES");
        keyGen.init(KEY_SIZE_BITS, random);
        return keyGen.generateKey();
    }

    public static String encrypt(String password, SecretKey key) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

        byte[] iv = generateRandomIV();

        GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_SIZE_BITS, iv);
        cipher.init(Cipher.ENCRYPT_MODE, key, spec);

        byte[] bytePassword = password.getBytes(UTF_8);
        byte[] ivCTAndTag = new byte[GCM_IV_SIZE_BYTES + cipher.getOutputSize(bytePassword.length)];
        System.arraycopy(iv, 0, ivCTAndTag, 0, GCM_IV_SIZE_BYTES);

        cipher.doFinal(bytePassword, 0, bytePassword.length, ivCTAndTag, GCM_IV_SIZE_BYTES);
        
        return Base64.getEncoder().encodeToString(ivCTAndTag);
    }

    private static byte[] generateRandomIV() {
        byte[] iv = new byte[GCM_IV_SIZE_BYTES];
        SecureRandom random = new SecureRandom();
        random.nextBytes(iv);
        return iv;
    }

    public static String decrypt(String encryptedPassword, SecretKey key) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

        byte[] ivAndCTWithTag = Base64.getDecoder().decode(encryptedPassword);

        GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_SIZE_BITS, ivAndCTWithTag, 0, GCM_IV_SIZE_BYTES);
        cipher.init(Cipher.DECRYPT_MODE, key, spec);
        
        byte[] plaintext = cipher.doFinal(ivAndCTWithTag, GCM_IV_SIZE_BYTES, ivAndCTWithTag.length - GCM_IV_SIZE_BYTES);
        
        return new String(plaintext, UTF_8);
    }

    public static void main(String[] args) throws Exception {
        PasswordVault vault = new PasswordVault();
        PasswordEntry passwordEntry = readPlainPasswordEntry();
        vault.encryptAndStorePasswordEntry(passwordEntry);
        System.out.println(vault.vaultBoxes);
        PasswordEntry decryptedPasswordEntry = vault.retrieveAndDecryptPasswordEntry();
        System.out.println(decryptedPasswordEntry);
    }

    public static PasswordEntry readPlainPasswordEntry() {
        try (Scanner keyboard = new Scanner(System.in)) {
            System.out.println("Please enter in the website that you would like to store a password:");
            String website = keyboard.nextLine();
            System.out.println("Please enter your username");
            String login = keyboard.nextLine();
            System.out.println("Please enter your password");
            String password = keyboard.nextLine();

            return new PasswordEntry(website, login, password);
        }
    }
}

正如您将看到的,我还重命名了很多变量并引入了多个常量。我已将保管库设为一个具有状态的对象,其中包含 key 和安全条目。

当然,最终您希望序列化保管库来存储加密值,没有 key ,这些值必须从其他地方存储/检索/派生。您不想混合检索密码和存储密码条目(我已将其重命名,因为网站和登录名不是密码的一部分)。

我还简化了异常处理和 String 处理(如果您曾经执行过 new String(string) 那么您可能会惊讶于它由 返回字符串; 内部)。要很好地处理加密异常,请查看 this answer of mine .

好的,希望这对您有所帮助。祝剩下的一切顺利。

关于java - 尝试解密加密数据时标签不匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55836052/

相关文章:

java - 在同一个类中有静态和非静态方法是错误的吗?

c++ - 密文不是 block 大小的倍数

c++ - OpenSSL:我使用存储在外部文件中的 RSA key 对文件进行加密和解密,但有时我的程序会崩溃

java - 即使在排除单元测试之后,单元测试也会在集成测试阶段执行

java - 如何检查 JRadioButtons 是否被选中?

ios - iOS 应用程序中用于登录系统的标准程序是什么?

java - AES 中的错误填充异常

ios - 如何使用 Xcode 在 Swift 中对字符串进行 AES 128 加密并将其作为 POST 发送到服务器?

java - Spring Jpa @Transactional 不起作用

java - 在 JPA/Hibernate 中使用 Spring 定义的 transactionManager