-1

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 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!
nirav dinmali
  • 197
  • 11

1 Answers1

2

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);
dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70