简体   繁体   English

使用AES和MAC进行Java加密

[英]Java Encryption With AES and MAC

I am trying to figure out a way to complete my Java coursework. 我试图找出一种方法来完成Java课程。 I am expected to encrypt a file but honestly I didn't really understand the instructions. 我应该对文件进行加密,但是老实说我并不真正理解这些说明。 I achieved file encryption and decryption however I have doubts that it does the thing instructions says. 我实现了文件加密和解密,但是我怀疑它是否可以执行指令中所述的操作。 The instruction is as following: 指令如下:

  • The encryption has three stages: 加密分为三个阶段:
  • Generate 16-byte random data as the Initial Vector (IV) that is needed for the CBC mode 生成16字节的随机数据作为CBC模式所需的初始向量(IV)
  • Apply the AES cipher to encrypt the content of the file in the CBC mode using the PKCS5 padding scheme. 应用AES密码,以使用PKCS5填充方案以CBC模式加密文件的内容。
  • Apply a MAC cipher (eg, “HmacSHA1”) to compute a MAC that encapsulates IV and ciphertext 应用MAC密码(例如“ HmacSHA1”)来计算封装了IV和密文的MAC
  • The encrypted file will be the concatenation of the following data: 16-byte IV || 加密的文件将是以下数据的串联:16字节IV || ciphertext || 密文|| 20-byte HMAC 20字节的HMAC

The code I have written is like this, it successfully encrypts a text file. 我写的代码是这样的,它成功地加密了一个文本文件。 This is the whole code for my application. 这是我的应用程序的全部代码。

import java.io.FileNotFoundException;
import java.io.*;
import java.util.Scanner;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.AlgorithmParameters;
import java.security.SecureRandom;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class AESFileEncryption {

/*public AESFileEncryption(String nameoffile){

}
public String FileReturn(String filename){
    String fl = filename;       
    return fl;      
}*/

public static void main(String[] args) throws Exception {

    File f = new File("plainfile.txt");
    File g = new File("plainfile.txt.8102");
    File fl = new File("plainfile.txt.8102");

    if(g.exists() && !g.isDirectory()){
        System.out.println("The file is already encrypted...");
        String fname = fl.getAbsolutePath();
        System.out.print("Absolute Encrypted File Pathname => "+ fname);
        System.exit(0);
    }       
    else if(f.exists() && !f.isDirectory()) { 
         System.out.println(" The file is found.The encryption process is going to begin...");

    }       
    else{
         System.out.println(" The file is missing!!!!");
         System.exit(0);
    }

    // file to be encrypted
    FileInputStream inFile = new FileInputStream("plainfile.txt");       

    // encrypted file
    FileOutputStream outFile = new FileOutputStream("plainfile.txt.8102");


    // password to encrypt the file
    Scanner scan= new Scanner(System.in);
    System.out.println("Enter the password : => ");
    String password= scan.nextLine();

    //String password = "javapapers";

    // password, iv and salt should be transferred to the other end
    // in a secure manner

    // salt is used for encoding
    // writing it to a file
    // salt should be transferred to the recipient securely
    // for decryption
    byte[] salt = new byte[8];
    SecureRandom secureRandom = new SecureRandom();
    secureRandom.nextBytes(salt);
    FileOutputStream saltOutFile = new FileOutputStream("salt.enc");
    saltOutFile.write(salt);
    saltOutFile.close();

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

    //
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, secret);
    AlgorithmParameters params = cipher.getParameters();

    // iv adds randomness to the text and just makes the mechanism more
    // secure
    // used while initializing the cipher
    // file to store the iv
    FileOutputStream ivOutFile = new FileOutputStream("iv.enc");
    byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
    ivOutFile.write(iv);
    ivOutFile.close();

    //file encryption
    byte[] input = new byte[64];
    int bytesRead;

    while ((bytesRead = inFile.read(input)) != -1) {
        byte[] output = cipher.update(input, 0, bytesRead);
        if (output != null)
            outFile.write(output);
    }

    byte[] output = cipher.doFinal();
    if (output != null)
        outFile.write(output);

    inFile.close();
    outFile.flush();
    outFile.close();

    System.out.println("File Encrypted.");

    }

}

The instructor means that HMAC should be applied to create an authentication tag for the ciphertext. 指导者意味着应该应用HMAC为密文创建身份验证标签。 This is called encrypt-then-MAC . 这称为crypto-then-MAC HMAC is a keyed hash function which provides an integrity/authenticity check for the recipient who has the correct key. HMAC是带密钥的哈希函数,它为具有正确密钥的收件人提供完整性/真实性检查。 Since it is essentially a hash function, it works by updating the internal state. 因为它本质上是一个哈希函数,所以它通过更新内部状态来工作。

Mac mac = Mac.getInstance("HmacSHA1");
SecretKeySpec macKey = new SecretKeySpec(macKeyBytes, "HmacSHA1");
mac.init(macKey);
mac.update(iv); // update for IV
...
mac.update(output); // update for each ciphertext chunk
...
byte[] authTag = mac.doFinal();
outfile.write(authTag); // at the very end

One problem remains and that is the generation of the macKeyBytes . 问题仍然存在,那就是macKeyBytes的生成。 It should not be the same as the encryption key that you generated from the password through PBKDF2. 它不应与您通过PBKDF2从密码生成的加密密钥相同。 You should use the key that you generated to derive the encryption and the MAC key separately. 您应该使用生成的密钥分别导出加密和MAC密钥。 This is usually done with HKDF , but you could also use HMAC again for that. 通常这是使用HKDF来完成的,但是您也可以再次使用HMAC。 Pseudo-code: 伪代码:

byte[] encKeyBytes = hmacSha256(key, "Encryption");
byte[] macKeyBytes = hmacSha256(key, "Authentication");

The instruction doesn't say anything about the key or password, so I'm going to assume it should have been a static key (for testing purposes). 该指令没有说明任何有关密钥或密码的内容,因此我将假定它应该是静态密钥(出于测试目的)。 But the way you're using PBKDF2 is ok, but the salt also must be written to the file. 但是您使用PBKDF2的方式是可以的,但是盐也必须写入文件中。 Otherwise, the recipient won't be able to derive the same key without the random salt. 否则,接收者将无法在没有随机盐的情况下得出相同的密钥。 Also, the salt should be probably 16 bytes long. 另外,盐应该大约为16个字节长。


Another problem with your current code is that you're writing the IV into a separate file. 当前代码的另一个问题是您正在将IV写入单独的文件中。 Don't do that. 不要那样做 Just write the IV to the beginning of the ciphertext file. 只需将IV写入密文文件的开头即可。 The IV has the same size as the block in CBC mode and AES has a fixed block size of 16 bytes. IV具有与CBC模式下的块相同的大小,而AES具有16字节的固定块大小。 The recipient will always know how many bytes are to be read for the IV. 接收者将始终知道要为IV读取多少个字节。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM