File Decryption

To decrypt an encrypted file we simply need to reverse the encryption process. However, rather than using the recipient's public key, we need to use the private key in order to recover the session key.

The decryptFile() method will:

1.decode the input from the various components and decipher the session key with the recipient's private key,

2.initialize the bulk cipher with the session key and algorithm parameters,

3.initialize the MAC algorithm,

4.process the encrypted input,

5.verify the calculated MAC with the MAC from the document, and

6.write out the decrypted result.

Step 1 - Decrypt the session key

static final String PROVIDER = "SAFENET";
static final String WRAP_KEYSTORE = "CRYPTOKI";
static final String WRAP_TRANSFORM = "RSA/ECB/PKCS1Padding";
static final String BULK_ALGORITHM = "DES";

Key decryptKey(PrivateKey wrapKey, byte[] wrappedKey)
{
   WrappingKeyStore keyStore;
   keyStore = WrappingKeyStore.getInstance(WRAP_KEYSTORE,
                                           PROVIDER);

   return keyStore.unwrapKey(wrapKey, WRAP_TRANSFORM,
                             wrappedKey, BULK_ALGORITHM);
}

Step 2 - Initialize the Bulk Cipher

Next, we need to create and initialize the Cipher instance we will use to decrypt the document. It is important here to ensure that our Cipher instance that will be used to perform the decryption is initialized with the same parameters generated by the encryption Cipher. In the case of the Gemalto SAFENET provider, the only parameter type is the IvParameterSpec, so we convert our serialized parameters directly.

static final String PROVIDER = "SAFENET";
static final String BULK_ALGORITHM = "DES";

Cipher bulkCipher = Cipher.getInstance(BULK_TRANSFORM,
                                       PROVIDER);
if (algParams != null)
{
   AlgorithmParameterSpec params;
   params = new IvParameterSpec(algParams);

   bulkCipher.init(Cipher.DECRYPT_MODE, secretKey,
                   params);
}
else
{
   bulkCipher.init(Cipher.DECRYPT_MODE, secretKey);
}

Step 3 - Initialize the MAC Algorithm

Initialization of the MAC during decryption is identical to that during encryption:

static final String PROVIDER = "SAFENET";
static final String MAC_ALGORITHM = "DESMac";

   Mac mac = Mac.getInstance(MAC_ALGORITHM, PROVIDER);
   mac.init(secretKey);

Step 4 - Process the encrypted input

Next we need to recover the plaintext from the ciphertext and calculate a new MAC. This process is nearly identical to the encrypt() method, however, since the MAC is calculated on the plaintext, we update the Mac with the output from the Cipher.

static final int READ_BUFFER = 50;

byte[] decrypt(Cipher cipher, Mac mac, InputStream in, OutputStream out)
{
   /*
    * read the input in chunks and process each chunk
    */
   byte[] block = new byte[READ_BUFFER];
   int len;
   while ((len = in.read(block)) != -1)
   {
      /*
       * decipher the data
       */
      byte[] plain = cipher.update(block, 0, len);
      if (plain != null)
      {
         /*
          * update our MAC value
          */
         mac.update(plain);

         /*
          * output the deciphered data
          */
         out.write(plain);
      }
   }

   /*
    * output the final block if required
    */
   byte[] finalBlock = cipher.doFinal();
   if (finalBlock != null)
   {
      /*
       * update our MAC value
       */
      mac.update(finalBlock);

      /*
       * output the deciphered data
       */
      out.write(finalBlock);
   }

   return mac.doFinal();
}

Step 5 - Verify the MAC

To verify the MAC, we simply compare the MAC bytes we previously extracted with the value just calculated.

if (!Arrays.equals(fileMac, calculatedMac))

{
   throw new GeneralSecurityException("File has been
   tampered with.");
}

Step 6 - Write out the decrypted result

Now that we have verified that the file is not corrupted we can output the contents to the destination.

static final String PROVIDER = "SAFENET";
static final String BULK_ALGORITHM = "DES";
static final String BULK_TRANSFORM = "DES/CBC/PKCS5Padding";
static final String MAC_ALGORITHM = "DESMac";

void decryptFile(InputStream in, OutputStream out,PrivateKey privateKey)
{
   /*
    * Decode the input file
    */
   DataInputStream dIn = new DataInputStream(in);

   /*
    * recover the encrypted Key data
    */
   int keyLen = dIn.readInt();
   byte[] keyBytes = new byte[keyLen];
   dIn.readFully(keyBytes);

   /*
    * recover the algorithm parameters
    */
   int algLen = dIn.readInt();
   byte[] algBytes = null;
   if (algLen > 0)
   {
      algBytes = new byte[algLen];
      dIn.readFully(algBytes);
   }

   /*
    * recover the stored MAC value
    */
   int macLen = dIn.readInt();
   byte[] fileMac = new byte[macLen];
   dIn.readFully(fileMac);

   /*
    * recreate the session key
    */
   Key secretKey = decryptKey(privateKey, keyBytes);

   /*
    * Create our Cipher and initialise it with our key
    * and algorithm parameters.
    */
   Cipher bulkCipher =
             Cipher.getInstance(BULK_TRANSFORM,PROVIDER);
   if (algBytes != null)
   {
      AlgorithmParameterSpec params;
      params = new IvParameterSpec(algBytes);

      bulkCipher.init(Cipher.DECRYPT_MODE, secretKey,
                      params);
   }
   else
   {
      bulkCipher.init(Cipher.DECRYPT_MODE, secretKey);
   }

   /*
    * Initialise the Mac we use to verify the file
      integrity
    */
   Mac mac = Mac.getInstance(MAC_ALGORITHM, PROVIDER);
   mac.init(secretKey);

   /*
    * Decrypt the file to a temporary buffer
    */
   ByteArrayOutputStream bOut =
                          new ByteArrayOutputStream();
   byte[] calculatedMac = decrypt(bulkCipher, mac, in,
                                  bOut);

   /*
    * verify the stored MAC value with the calculated
      value
    */
   if (!Arrays.equals(fileMac, calculatedMac))
   {
      throw new GeneralSecurityException(
         "File has been tampered with.");
   }
   else
   {
      /*
       * save the decrypted output to the outputstream
       */
      bOut.writeTo(out);
   }
}