java - 无法让 AES/GCM 使用密码

标签 java encryption cryptography

我正在尝试使用 Java 的密码库来实现使用 GCM 模式的 AES 加密(因为它是一个 AEAD 方案,我想确保数据完好无损地到达)但我想使用密码以方便用户使用。直到我把头撞在砖墙上几次并阅读 this other stackoverflow response. 之前,让程序编译无误是一件困难的事。

现在,不幸的是程序可以编译,但它返回的密文只是消息中字节数的一小部分(我怀疑它只是 GCM 使用的 header ,实际消息根本没有被写入)。有人可以帮忙吗?

下面是一些程序变量:

private static final byte[] SALT = {(byte)0x3b,(byte)0x12,(byte)0x44,(byte)0x61,
        (byte)0xec,(byte)0xc0,(byte)0xa1,(byte)0x82,
        (byte)0x4d, (byte)0x97, (byte)0x4d, (byte)0x3c,
        (byte)0x57, (byte)0xd9, (byte)0x94, (byte)0x52};

private static final String CIPHER_ALG = "AES/GCM/PKCS5Padding";
private static final String SEC_RANDOM_ALG = "SHA1PRNG";
private static final String S_KEY_FACTORY_ALG = "PBKDF2WithHmacSHA1";

private static final int AES_KEY_SIZE = 128; //num bits
private static final int GCM_NONCE_LENGTH = 16; //num bytes
private static final int GCM_TAG_LENGTH = 16; //num bytes
private static final int KEY_SPEC_ROUNDS = 65536;

这里是加密方法:

//FIXME somehow the cipher is truncating the message or something
private byte[] encrypt(File file, String password) throws IOException{

    Path path = Paths.get(file.toURI());
    byte[] messageBytes = Files.readAllBytes(path);
    //Message bytes: 3253
    System.out.printf("Message bytes: %d%n", messageBytes.length);
    byte[] messageName = path.getFileName().toString().getBytes();

    byte[] cipherBytes = {};
    byte[] iv = {};
    try{

        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(S_KEY_FACTORY_ALG);
        PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), SALT, KEY_SPEC_ROUNDS, AES_KEY_SIZE);
        SecretKey key = keyFactory.generateSecret(pbeKeySpec);
        System.out.printf("Secret key take 1: " + Arrays.toString(key.getEncoded()) + "%n");

        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getEncoded(), "AES");

        //FIXME this is not encrypting the message correctly
        Cipher cipher = Cipher.getInstance(CIPHER_ALG);
        final byte[] nonce = new byte[GCM_NONCE_LENGTH];
        SecureRandom random = SecureRandom.getInstanceStrong();
        random.nextBytes(nonce);
        GCMParameterSpec paramSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce);
        iv = paramSpec.getIV();

        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, paramSpec);
        cipher.updateAAD(messageName);
        cipher.update(messageBytes);
        cipherBytes = cipher.doFinal();

    }catch(NoSuchAlgorithmException e){
        e.printStackTrace();
    }catch(NoSuchPaddingException e){
        e.printStackTrace();
    }catch(InvalidKeyException e){
        e.printStackTrace();
    }catch(InvalidAlgorithmParameterException e){
        e.printStackTrace();
    }catch(InvalidKeySpecException e){
        e.printStackTrace();
    }catch(BadPaddingException e){
        e.printStackTrace();
    }catch(IllegalBlockSizeException e){
        e.printStackTrace();
    }

    //IV bytes: 16
    System.out.printf("IV bytes: %d%n", iv.length);
    //Cipher bytes: 21
    System.out.printf("Cipher bytes: %d%n", cipherBytes.length);
    //Cipher bytes: [121, 68, 7, -69, -35, -9, -83, 101, -60, -80, 42, 59, -67, 126, 18, -82, 79, -60, 34, -125, 12]
    System.out.printf("Cipher bytes: " + Arrays.toString(cipherBytes) + "%n");

    //FIXME somehow the cipher is shortening the message or something
    return cipherBytes;
}

最佳答案

您不应忽略 cipher.update(messageBytes); 的返回值。此外,GCM 在底层使用 CTR 模式。 CTR 模式不需要填充,因此您应该使用 "AES/GCM/NoPadding"

您现在可能只是得到密文的最后一部分和身份验证标签(即尾部而不是头部)。

关于java - 无法让 AES/GCM 使用密码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36635576/

相关文章:

java - 如何在 Java 中创建指向引用变量的指针变量?

java - Jetty 6 到 Jetty 7 升级 : what happened to system property "jetty.lib"? (-Djetty.lib=my/lib/dir)

java - 将yyMdd转换成java中的正常日期(十六进制月份数)

c# - SocketAsyncEventArgs 加密——SSL 与自定义预传输加密

python - 如何将输出输入到 tkinter 输入字段中

java - 通过按下 ANDROID 按钮加载 Xml

c++ - AtmelATAES132 Mac一代

c# - .NET 应用程序位置中的离线安全性在代码中存储混淆 key

c# - Oracle 的 DBMS_CRYPTO.DES_CBC_PKCS 等效于 C#

delphi - 无法获得 Spring4D 加密示例的预期结果