Skip to content

Commit

Permalink
add support for streams
Browse files Browse the repository at this point in the history
  • Loading branch information
rockaport committed Feb 18, 2017
1 parent 05c65d4 commit 603ce29
Show file tree
Hide file tree
Showing 24 changed files with 787 additions and 78 deletions.
29 changes: 19 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ CI](https://circleci.com/gh/rockaport/alice.svg?style=shield)](https://circleci.
[![codebeat badge](https://codebeat.co/badges/03c01973-7f17-4aa9-8c5f-42c1e6a45d24)](https://codebeat.co/projects/github-com-rockaport-alice)

# alice
Alice is a Java AES encryption library for working with byte arrays and files. Various key lengths, block modes, padding schemes, key deriviation functions, and Message Authentication Codes (MAC) are available. See the [javadoc](https://rockaport.github.io/alice) for more information.
Alice is a Java AES encryption library for working with byte arrays, files, and streams. Various key lengths, block modes, padding schemes, key deriviation functions, and Message Authentication Codes (MAC) are available. See the [javadoc](https://rockaport.github.io/alice) for more information.

Alice provides an easy wrapper around the javax.crypto cipher suite for symmetric key encryption. if a MAC algorithm is selected, additional [Authenticated Encryption](https://en.wikipedia.org/wiki/Authenticated_encryption) is performed using an [encrypt-then-mac](https://en.wikipedia.org/wiki/Authenticated_encryption#Encrypt-then-MAC_.28EtM.29) scheme.

Expand Down Expand Up @@ -59,7 +59,8 @@ Iterations (used when the PBKDF = PBKDF2WithHmacSHA{Length})
# Download
The easist way is to use [jitpack](https://jitpack.io/#rockaport/alice)

# Initialization
# Usage
## Initialization
```java
// Initialize an Alice instance with defaults:
// Cipher = AES/256/CTR/NoPadding
Expand All @@ -68,7 +69,7 @@ The easist way is to use [jitpack](https://jitpack.io/#rockaport/alice)
Alice alice = new Alice(new AliceContextBuilder().build());
```

## AES-CBC or CTR context initialization
### AES-CBC or CTR context initialization
```java
AliceContext aliceContext = new AliceContextBuilder()
.setAlgorithm(AliceContext.Algorithm.AES)
Expand All @@ -77,7 +78,7 @@ AliceContext aliceContext = new AliceContextBuilder()
.build()
```

## AES-GCM Context Initialization
### AES-GCM Context Initialization
```java
AliceContext aliceContext = new AliceContextBuilder()
.setAlgorithm(AliceContext.Algorithm.AES)
Expand All @@ -87,7 +88,7 @@ AliceContext aliceContext = new AliceContextBuilder()
.build()
```

## DES-CBC or CTR Context Initialization
### DES-CBC or CTR Context Initialization
```java
AliceContext aliceContext = new AliceContextBuilder()
.setAlgorithm(AliceContext.Algorithm.DES)
Expand All @@ -96,7 +97,7 @@ AliceContext aliceContext = new AliceContextBuilder()
.build()
```

## 3DES-CBC or CTR Context Initialization
### 3DES-CBC or CTR Context Initialization
```java
AliceContext aliceContext = new AliceContextBuilder()
.setAlgorithm(AliceContext.Algorithm.DESede)
Expand All @@ -105,19 +106,27 @@ AliceContext aliceContext = new AliceContextBuilder()
.build()
```

# Usage
After you've created an Alice instance with the desired context you can encrypt/decrypt byte arrays and files as shown.
## Encryption
After you've created an Alice instance with the desired context you can encrypt/decrypt byte arrays and files as shown. See the unit tests for more detailed usage and options.

## Working with byte arrays
### Working with byte arrays
```java
byte[] encryptedBytes = alice.encrypt(input, password);

byte[] decryptedBytes = alice.decrypt(encryptedBytes, password);
```

## Working with files
### Working with files
```java
alice.encrypt(inputFile, encryptedFile, password);

alice.decrypt(encryptedFile, decryptedFile, password);
```

### Working with streams
Note: Streaming encryption does not support authenticated encryption. You must set the mac algorithm to AliceContext.MacAlgorithm.NONE
```java
alice.encrypt(inputStream, encryptedStream, password);

alice.decrypt(encryptedStream, decryptedStream, password);
```
122 changes: 122 additions & 0 deletions alice/src/main/java/com/rockaport/alice/Alice.java
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,65 @@ public synchronized void encrypt(File input, File output, char[] password)
}
}

/**
* Encrypts the input stream using the supplied password
*
* @param input the input file
* @param output the output file
* @param password the password
* @throws GeneralSecurityException if initialization or encryption fails
* @throws IOException if there's a failure to read/write from/to the input/output stream
*/
@SuppressWarnings("WeakerAccess")
public synchronized void encrypt(InputStream input, OutputStream output, char[] password)
throws GeneralSecurityException, IOException {
if (input == null || output == null) {
throw new IllegalArgumentException("Input or output stream is null");
}

if (password == null || password.length == 0) {
throw new IllegalArgumentException("Password is either null or empty");
}

if (context.getMacAlgorithm() != AliceContext.MacAlgorithm.NONE) {
throw new IllegalArgumentException("Streaming encryption does not support authenticated encryption");
}

BufferedInputStream bufferedInputStream = null;
BufferedOutputStream bufferedOutputStream = null;

try {
// generate the initialization vector
byte[] initializationVector = generateInitializationVector();

// initialize the cipher
cipher.init(Cipher.ENCRYPT_MODE,
deriveKey(password, initializationVector),
getAlgorithmParameterSpec(context.getMode(), initializationVector));

// setup streams
bufferedInputStream = new BufferedInputStream(input);
bufferedOutputStream = new BufferedOutputStream(output);

// write the initialization vector
bufferedOutputStream.write(initializationVector);

// allocate variables
int bytesRead;
byte[] inputStreamBuffer = new byte[4096];
while ((bytesRead = bufferedInputStream.read(inputStreamBuffer)) > 0) {
// encrypt
bufferedOutputStream.write(cipher.update(inputStreamBuffer, 0, bytesRead));
}

// finalize and write the cipher
bufferedOutputStream.write(cipher.doFinal());
} finally {
closeStream(bufferedInputStream);
closeStream(bufferedOutputStream);
}
}

/**
* Decrypts a byte array using the supplied password
*
Expand Down Expand Up @@ -438,6 +497,69 @@ public synchronized void decrypt(File input, File output, char[] password)
}
}

/**
* Decrypts an input stream using the supplied password
*
* @param input the input file
* @param output the output file
* @param password the password
* @throws GeneralSecurityException if initialization or decryption fails
* @throws IOException if there's a failure to read/write from/to the input/output stream
*/
@SuppressWarnings("WeakerAccess")
public synchronized void decrypt(InputStream input, OutputStream output, char[] password)
throws GeneralSecurityException, IOException {
if (input == null || output == null) {
throw new IllegalArgumentException("Input or output stream is null");
}

if (password == null || password.length == 0) {
throw new IllegalArgumentException("Password is either null or empty");
}

if (context.getMacAlgorithm() != AliceContext.MacAlgorithm.NONE) {
throw new IllegalArgumentException("Streaming decryption does not support authenticated encryption");
}

BufferedInputStream bufferedInputStream = null;
BufferedOutputStream bufferedOutputStream = null;

try {
// setup streams
bufferedOutputStream = new BufferedOutputStream(output);
bufferedInputStream = new BufferedInputStream(input);

// read the initialization vector
byte[] initializationVector = new byte[context.getIvLength()];

int ivBytesRead = bufferedInputStream.read(initializationVector);

if (ivBytesRead < context.getIvLength()) {
throw new IOException("Stream does not contain IV");
}

// initialize the cipher
cipher.init(Cipher.DECRYPT_MODE,
deriveKey(password, initializationVector),
getAlgorithmParameterSpec(context.getMode(), initializationVector));

// allocate loop buffers and variables
int bytesRead;
byte[] inputStreamBuffer = new byte[4096];

// decrypt
while ((bytesRead = bufferedInputStream.read(inputStreamBuffer)) > 0) {
bufferedOutputStream.write(cipher.update(inputStreamBuffer, 0, bytesRead));
}

// finalize the cipher
bufferedOutputStream.write(cipher.doFinal());
} finally {
closeStream(bufferedInputStream);
closeStream(bufferedOutputStream);
}
}

/**
* Derives an AES {@link javax.crypto.spec.SecretKeySpec} using a password and iteration count (if needed).
*
Expand Down
Loading

0 comments on commit 603ce29

Please sign in to comment.