package no.ntnu.item.cryptoutil; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.DigestInputStream; import java.security.DigestOutputStream; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.EmptyStackException; import java.util.Stack; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public class CryptoStreamer { private SecretKey secretKey; private IvParameterSpec iv; private Cipher encryptCipher; private Cipher decryptCipher; private byte[] buffer = new byte[1024]; private byte[] digest; private Stack<InputStream> openInputStreams = new Stack<InputStream>(); private Stack<OutputStream> openOutputStreams = new Stack<OutputStream>(); private MessageDigest md; public CryptoStreamer(SecretKey secretKey, IvParameterSpec iv) { this.secretKey = secretKey; this.iv = iv; initCipher(); } public CryptoStreamer(byte[] secretKey, byte[] iv) { this.iv = new IvParameterSpec(iv); this.secretKey = new SecretKeySpec(secretKey, Cryptoutil.SYM_CIPHER); initCipher(); } public CryptoStreamer() { this.secretKey = Cryptoutil.generateSymmetricKey(); this.iv = new IvParameterSpec(Cryptoutil.nHash( this.secretKey.getEncoded(), 2, Cryptoutil.SYM_BLOCK_SIZE / 8)); initCipher(); } public SecretKey getSecretKey() { return secretKey; } public void setSecretKey(SecretKey secretKey) { this.secretKey = secretKey; } public IvParameterSpec getIv() { return iv; } public void setIv(IvParameterSpec iv) { this.iv = iv; } public byte[] getDigest() { return digest; } private void initCipher() { try { this.encryptCipher = Cipher.getInstance(Cryptoutil.SYM_CIPHER + "/" + Cryptoutil.SYM_MODE + "/" + Cryptoutil.SYM_PADDING); this.encryptCipher.init(Cipher.ENCRYPT_MODE, this.secretKey, this.iv); this.decryptCipher = Cipher.getInstance(Cryptoutil.SYM_CIPHER + "/" + Cryptoutil.SYM_MODE + "/" + Cryptoutil.SYM_PADDING); this.decryptCipher.init(Cipher.DECRYPT_MODE, this.secretKey, this.iv); this.md = MessageDigest.getInstance(Cryptoutil.HASH_ALGORITHM); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } } public byte[] encrypt(InputStream is) { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); OutputStream cipherOutput = new CipherOutputStream(buffer, this.encryptCipher); int numRead = 0; try { while ((numRead = is.read(this.buffer)) >= 0) { cipherOutput.write(this.buffer, 0, numRead); } cipherOutput.close(); return buffer.toByteArray(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } public OutputStream getDecryptedAndHashedOutputStream(OutputStream os) { this.md.reset(); BufferedOutputStream netBuffer = new BufferedOutputStream(os); DigestOutputStream digestOutputStream = new DigestOutputStream( netBuffer, md); this.openOutputStreams.push(digestOutputStream); this.openOutputStreams.push(os); BufferedOutputStream digestBuffer = new BufferedOutputStream( digestOutputStream); CipherOutputStream cipherOutputStream = new CipherOutputStream( digestBuffer, this.decryptCipher); this.md = digestOutputStream.getMessageDigest(); BufferedOutputStream cryptoBuffer = new BufferedOutputStream( cipherOutputStream); return cryptoBuffer; } public InputStream getEncryptedAndHashedInputStream(InputStream is) { md.reset(); BufferedInputStream filebuffer = new BufferedInputStream(is); DigestInputStream digestInputStream = new DigestInputStream(filebuffer, md); BufferedInputStream digBuffer = new BufferedInputStream( digestInputStream); CipherInputStream cipherInputStream = new CipherInputStream(digBuffer, this.encryptCipher); BufferedInputStream readBuffer = new BufferedInputStream( cipherInputStream); this.md = digestInputStream.getMessageDigest(); this.openInputStreams.push(digestInputStream); this.openInputStreams.push(is); return readBuffer; } public byte[] finish() { closeStreams(); byte[] tmp = this.md.digest(); this.digest = Cryptoutil.singlehash(tmp, Cryptoutil.SYM_SIZE / 8); this.md.reset(); return this.digest; } public void closeStreams() { // Unknown if we actually need this while (true) { try { this.openOutputStreams.peek(); OutputStream os = this.openOutputStreams.pop(); os.flush(); os.close(); } catch (EmptyStackException e) { break; } catch (IOException e) { } } // while (true) { // try { // this.openInputStreams.peek(); // this.openInputStreams.pop().close(); // } catch (EmptyStackException e) { // return; // } catch (IOException e) { // } // } } }