Swift AES GCM加密器和Java解密器 – 文件问题

huangapple go评论73阅读模式
英文:

Swift AES GCM encryptor and Java decryption - File issues

问题

Here are the translated code sections:

public class FileDecryptor {
    static String secret = "my-xxx-bit-secret-my-secret-my-s";
    static String nonce = "fv1nixTVoYpSvpdA";
    // The following values are obtained from the Swift encryptor class print statements
    static String tag = "KaryGdpX0t1KwXaDFXEnkw==";
    static String cipherText = "UoRsEQ==";

    public static void main(String args[]) throws Exception {
        
        // Decrypt with cipher and tag - WORKS
        String decryptedValue = decrypt(cipherText, tag);
        System.out.println("output from cipher and tag: " + decryptedValue);
        
        // Decrypt from file content which has nonce, tag, ciphertext combined - DOES NOT WORK
        System.out.println("going to decrypt from file which has nonce, tag, ciphertext combined");

        String inputFileName = "a_e.txt";
        String inputFileContent = FileUtils.readFileToString(new File(inputFileName)); // ciphertext and tag are included
        System.println("inputFileContent:" + inputFileContent);
        String decryptedValue2 = decrypt(inputFileContent.getBytes());
        System.out.println("output: " + decryptedValue2);
    }

    // The rest of the code remains the same.
}

I hope this helps with your code translation. If you have any more questions or need further assistance, please let me know.

英文:

I need to encrypt with swift and decrypt with java around 1000 files in a directory.


I tested a prototype with a string and it works thanks to answer from https://stackoverflow.com/users/1831987/vgr at
https://stackoverflow.com/questions/75918859/swift-aes-gcm-encryptor-and-java-decryption-padding-issues


But when I place that string in a file, it does not work. It works if I print cipher text and tag during encryption and use them separately to decrypt in java, but that's not how it can be done in real world. The file content which is cipher text+nonce+tag, must be decrypted as a whole.


What am I doing wrong here please?


SWIFT ENCRYPTOR

static func encryptDecryptSingleTestFile() {
        
        do {
            //encrypt
            let data = try Data(contentsOf: URL(fileURLWithPath: filePath1))
            print("data in original file \(String(decoding: data, as: UTF8.self))")
            let sealedBox = try! AES.GCM.seal(data, using: symKey, nonce: nonce)
            let ciphertext = sealedBox.ciphertext.base64EncodedString()
            print("ciphertext of sealedbox in file: \(ciphertext)")
            let tag = sealedBox.tag
            print("tag of sealedbox in file: \(tag.base64EncodedString())")
            try sealedBox.combined!.write(to: URL(fileURLWithPath: filePath2))

            
            //DECRYPT - just for testing, actual decrypting will happen in java code
            let data2 = try Data(contentsOf: URL(fileURLWithPath: filePath2))
            print("data in encrypted file \(String(decoding: data2, as: UTF8.self))")
            let sealedBox2 = try AES.GCM.SealedBox(combined: data2)
            let decryptedData = try AES.GCM.open(sealedBox2, using: symKey)
            try decryptedData.write(to: URL(fileURLWithPath: filePath3))
            let ciphertext2 = sealedBox2.ciphertext.base64EncodedString()
            let dataInDecryptedFile = try Data(contentsOf: URL(fileURLWithPath: filePath3))
            print("data in decrypted file \(String(decoding: dataInDecryptedFile, as: UTF8.self))")

            
        } catch {
            print(error)
        }
    }

OUTPUT

data in original file: abc
ciphertext of sealedbox in file: UoRsEQ==
tag of sealedbox in file: KaryGdpX0t1KwXaDFXEnkw==
data in encrypted file: ~�g�ա�R��@R�l)���W��J�v�q'�
data in decrypted file: abc

JAVA DECRYPTOR

import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.io.FileUtils;

public class FileDecryptro {
    static String secret = "my-xxx-bit-secret-my-secret-my-s";
    static String nonce = "fv1nixTVoYpSvpdA";
    //below values are obtained from the swift encryptor class print statements
    static String tag = "KaryGdpX0t1KwXaDFXEnkw==";
    static String cipherText = "UoRsEQ==";

    public static void main(String args[]) throws Exception {
      
	//decrypt with cipher and tag - WORKS
        String decriptedValue = decrypt(cipherText,tag);
        System.out.println("output from cipher and tag:" + decriptedValue);
        
        //decrypt from file content which has nonce,tag,ciphertext combined. - DOES NOT WORK
        System.out.println("going to decrypt from file which has nonce, tag, ciphertext combined");

        String inputFileName = "a_e.txt";
        String inputFileContent = FileUtils.readFileToString(new File(inputFileName));///ciphertext and tag is included
        System.out.println("inputFileContent:"+inputFileContent);
        String decriptedValue2 = decrypt(inputFileContent.getBytes());
        System.out.println("output:" + decriptedValue2);
    
        
    }

    static String decrypt(byte[] ciphertextWithTagBytes) throws Exception {
	
           byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8);
           byte[] nonceBytes = Base64.getDecoder().decode(nonce);
           
              //decrypt
           Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
           SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
           GCMParameterSpec gcmSpec = new GCMParameterSpec(128, nonceBytes);
           cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec);
           byte[] plaintextBytes = cipher.doFinal(ciphertextWithTagBytes);
           String plaintext = new String(plaintextBytes, StandardCharsets.UTF_8);
           return plaintext;
           
    }

    static String decrypt(String cipherText, String tag) throws Exception {
	
	
        byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8);
        byte[] nonceBytes = Base64.getDecoder().decode(nonce);
        
        
        //append tagBytes to cipherText
        byte[] tagBytes = Base64.getDecoder().decode(tag);
        Base64.Decoder decoder = Base64.getDecoder();
        byte[] decodedCipherText = decoder.decode(cipherText);
        byte[] decodedTag = decoder.decode(tag);
        byte[] ciphertextBytes = new byte[decodedCipherText.length + decodedTag.length];
        ByteBuffer.wrap(ciphertextBytes).put(decodedCipherText).put(decodedTag);
        
        //decrypt
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
        GCMParameterSpec gcmSpec = new GCMParameterSpec(128, nonceBytes);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec);
        byte[] plaintextBytes = cipher.doFinal(ciphertextBytes);
        String plaintext = new String(plaintextBytes, StandardCharsets.UTF_8);
        return plaintext;
        
    }
    
  
 
}

OUTPUT

output from cipher and tag:abc

going to decrypt from file which has nonce, tag, ciphertext combined
inputFileContent:~�g�ա�R��@R�l)���W��J�v�q'�
Exception in thread "main" javax.crypto.AEADBadTagException: Tag mismatch!

答案1

得分: 2

以下是翻译好的部分:

它是二进制(也称为比特)。 密文以及其他与加密相关的数据,如签名、标记、随机数或盐,都是由比特组成的二进制数据,而不是字符。Java的String只能处理字符,因此将二进制文件读入String然后尝试对其进行getBytes会导致部分错误数据,而现代密码学只能使用完全正确的数据。

你需要将文件以字节形式读取,并且需要从文件中读取随机数和密文+标记(Java密码处理为单个单元)。有至少几种方法可以做到这一点:

  1. 可能最直接的方法是以流的方式打开文件(旧的I/O类型,而不是Java 8类型)并read每个部分。你需要分配具有正确大小的缓冲区,这不难。

  2. 以字节形式读取整个文件,然后从字节数组中复制各个部分。

  3. 以字节形式读取整个文件,将内容放入ByteBuffer中,并get每个部分;同样,你需要分配正确的大小。

  4. 以字节形式读取整个文件,然后将该数组的部分传递给加密例程。

以下代码展示了这四种可能性,只是我把#4放在前面,因为它使代码结构略微简化:

Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
byte[] key = "my-xxx-bit-secret-my-secret-my-s".getBytes(), nonce, ct_tag;
File f = new File(args[0]);
if (args.length == 1) { // 省略以表示#4
    byte[] all = Files.readAllBytes(f.toPath());
    c.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, all, 0, 12));
    byte[] dec = c.doFinal(all, 12, all.length - 12);
    System.out.write(dec);
    System.exit(0);
}
if (args[1].equals("1")) {
    nonce = new byte[12];
    ct_tag = new byte[(int) f.length() - 12];
    try (InputStream is = new FileInputStream(f)) {
        is.read(nonce);
        is.read(ct_tag);
    }
} else if (args[1].equals("2")) {
    byte[] all = Files.readAllBytes(f.toPath());
    nonce = Arrays.copyOf(all, 12);
    ct_tag = Arrays.copyOfRange(all, 12, all.length);
} else if (args[1].equals("3")) {
    byte[] all = Files.readAllBytes(f.toPath());
    nonce = new byte[12];
    ct_tag = new byte[all.length - 12];
    ByteBuffer.wrap(all).get(nonce).get(ct_tag);
} else {
    throw new Exception("bad second arg");
}
c.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, nonce));
byte[] dec = c.doFinal(ct_tag);
System.out.write(dec);

希望这能帮助你!

英文:

It's binary (aka bits). Ciphertext, and other crypto-related data like signature, tag, nonce, or salt, is binary data made up only of bits, not characters. Java's String only works on characters, so reading your binary file into a String and then trying to getBytes on it gives partly wrong data, and modern cryptography only works with completely right data.

You need to read the file as bytes, AND you need to read both the nonce and the ciphertext+tag (which Java crypto treats as a single unit) from the file. There are at least several ways to do this:

  1. probably most straightforward, open the file as a stream (the older I/O type not the java8 type) and read each piece. You need to allocate buffers with the correct size, which isn't hard.

  2. read the whole file as bytes then copy the pieces from the byte array.

  3. read the whole file as bytes, put the contents in a ByteBuffer and get each piece; again you need to allocate the correct size.

  4. read the whole file as bytes, then pass parts of that array to the crypto routines

The following code shows the four possibilities, except I put #4 first because it makes the code structure a bit simpler:

	Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
byte[] key = "my-xxx-bit-secret-my-secret-my-s".getBytes(), nonce, ct_tag;
File f = new File (args[0]);
if( args.length == 1 ){ // omit entirely to indicate #4
byte[] all = Files.readAllBytes(f.toPath());
c.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key,"AES"), new GCMParameterSpec(128,all,0,12));
byte[] dec = c.doFinal(all,12,all.length-12);
System.out.write(dec); System.exit(0);
}
if( args[1].equals("1") ){
nonce = new byte[12]; ct_tag = new byte[(int)f.length()-12];
try(InputStream is = new FileInputStream (f) ){ is.read(nonce); is.read(ct_tag); }
}else if( args[1].equals("2") ){
byte[] all = Files.readAllBytes(f.toPath());
nonce = Arrays.copyOf(all, 12); ct_tag = Arrays.copyOfRange(all, 12, all.length);
}else if( args[1].equals("3") ){
byte[] all = Files.readAllBytes(f.toPath());
nonce = new byte[12]; ct_tag = new byte[all.length-12];
ByteBuffer.wrap(all).get(nonce).get(ct_tag);
}else throw new Exception ("bad second arg");
c.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key,"AES"), new GCMParameterSpec(128, nonce));
byte[] dec = c.doFinal(ct_tag);
System.out.write(dec);

huangapple
  • 本文由 发表于 2023年4月4日 11:07:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/75925219.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定