Commit 016508bf authored by R.W.Majeed's avatar R.W.Majeed

Cryptographic streams

parent 772d028f
package de.sekmi.histream.crypto;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
public class DecryptingInputStream implements ReadableByteChannel {
private Cipher cipher;
private ReadableByteChannel in;
private ByteBuffer buffer;
private boolean endOfStream;
public DecryptingInputStream(ReadableByteChannel in, Key asymmetricKey) throws GeneralSecurityException, IOException{
this(in, "AES",128,"RSA", asymmetricKey);
}
public DecryptingInputStream(ReadableByteChannel in, String symmetricAlgorithm, int symmetricKeysize, String asymmetricCipher, Key asymmetricKey) throws GeneralSecurityException, IOException{
// use buffer
endOfStream = false;
buffer = ByteBuffer.allocate(1024*10);
in.read(buffer);
buffer.flip();
int version = buffer.getInt();
int ks = buffer.getShort();
if( version != EncryptingOutputStream.Version )throw new RuntimeException("Unsupported MDAT stream version "+version);
byte[] wrapped = new byte[ks];
buffer.get(wrapped);
buffer.compact();
Cipher unwrap;
try {
unwrap = Cipher.getInstance(asymmetricCipher);
unwrap.init(Cipher.UNWRAP_MODE, asymmetricKey);
Key temp = unwrap.unwrap(wrapped, symmetricAlgorithm, Cipher.SECRET_KEY);
cipher = Cipher.getInstance(symmetricAlgorithm);
cipher.init(Cipher.DECRYPT_MODE, temp);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) {
throw new IOException("Unable to unwrap symmetric key",e);
}
this.in = in;
}
@Override
public boolean isOpen() {
return in.isOpen();
}
@Override
public void close() throws IOException {
}
@Override
public int read(ByteBuffer dst) throws IOException {
if( endOfStream )
return 0; // nothing to do
int bytesRead=0;
if( buffer.hasRemaining() ){
bytesRead = in.read(buffer);
}
buffer.flip();
try {
if( bytesRead == -1 ){
endOfStream = true;
return cipher.doFinal(buffer, dst);
}else{
return cipher.update(buffer, dst);
}
} catch (ShortBufferException | IllegalBlockSizeException | BadPaddingException e) {
throw new IOException(e);
} finally {
buffer.compact();
}
}
}
package de.sekmi.histream.crypto;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import java.security.GeneralSecurityException;
import java.security.Key;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
public class EncryptingOutputStream implements WritableByteChannel{
public static final int Version = 1;
private Cipher cipher;
private WritableByteChannel out;
private ByteBuffer buffer;
public EncryptingOutputStream(WritableByteChannel out, Key asymmetricKey) throws GeneralSecurityException, IOException{
this(out, "AES",128,"RSA", asymmetricKey);
}
public EncryptingOutputStream(WritableByteChannel out, String symmetricAlgorithm, int symmetricKeysize, String asymmetricCipher, Key asymmetricKey) throws GeneralSecurityException, IOException{
KeyGenerator kg = KeyGenerator.getInstance(symmetricAlgorithm);
//log.fine("Generating symmetric key "+symmetricAlgorithm+" with size "+symmetricKeysize);
kg.init( symmetricKeysize );
SecretKey sk = kg.generateKey();
// wrap with asymmetric algorithm and write to output
Cipher wrapper = Cipher.getInstance(asymmetricCipher);
wrapper.init(Cipher.WRAP_MODE, asymmetricKey);
byte[] wrapped = wrapper.wrap(sk);
this.out = out;
// use buffer
buffer = ByteBuffer.allocate(1024*10);
// prefix output with version and key length
buffer.putInt(Version).flip();
out.write(buffer);
buffer.clear();
// write wrapped length
buffer.putShort((short)wrapped.length).flip();
out.write(buffer);
buffer.clear();
out.write(ByteBuffer.wrap(wrapped));
// initialize symmetric cipher
cipher = Cipher.getInstance(symmetricAlgorithm);
cipher.init(Cipher.ENCRYPT_MODE, sk);
}
@Override
public boolean isOpen() {
return out.isOpen();
}
@Override
public void close() throws IOException {
if( buffer.hasRemaining() ){
out.write(buffer);
}
try {
out.write(ByteBuffer.wrap(cipher.doFinal()));
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new IOException(e);
}
out.close();
}
@Override
public int write(ByteBuffer src) throws IOException {
try {
cipher.update(src, buffer);
} catch (ShortBufferException e) {
throw new IOException(e);
} finally {
buffer.flip();
}
return out.write(buffer);
}
}
/**
* Support for encryption and decryption via cryptographic methods.
* TODO move to separate module
*/
package de.sekmi.histream.crypto;
\ No newline at end of file
......@@ -267,10 +267,8 @@ public class GroupedXMLWriter extends GroupedObservationHandler{
* Marshal a fact without writing context information from patient, visit and source.
*
* @param fact fact
* @param patient patient context
* @param visit visit context
* @param source source context
* @throws ObservationException for errors in fact
* @throws JAXBException errors during marshal operation
*/
private void marshalFactWithContext(Observation fact, Visit visit, ExternalSourceType source) throws JAXBException{
......
package de.sekmi.histream.crypto;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import org.junit.Before;
import org.junit.Test;
import org.junit.Assert;
public class TestEncryptDecrypt {
private KeyPair keyPair;
@Before
public void generateKeys() throws NoSuchAlgorithmException{
KeyPairGenerator generatorRSA = KeyPairGenerator.getInstance("RSA");
generatorRSA.initialize(2048, new SecureRandom());
keyPair = generatorRSA.generateKeyPair();
}
public static void assertEqualFiles(Path expected, Path actual) throws IOException{
FileChannel e = FileChannel.open(expected, StandardOpenOption.READ);
FileChannel a = FileChannel.open(expected, StandardOpenOption.READ);
int bufferSize = 1024*1024*10;
ByteBuffer eb = ByteBuffer.allocateDirect(bufferSize);
ByteBuffer ab = ByteBuffer.allocateDirect(bufferSize);
long pos = 0;
while( true ){
int er = e.read(eb);
int ar = a.read(ab);
Assert.assertEquals(er, ar);
if( er == -1 )break;
eb.compact();
ab.compact();
for( int i=0; i<er; i++ ){
Assert.assertEquals("Position "+(pos+eb.position()), eb.get(), ab.get());
}
pos += er;
eb.flip();
ab.flip();
}
e.close();
a.close();
}
@Test
public void testEncryptDecrypt() throws GeneralSecurityException, IOException{
Path source = Paths.get("examples/dwh-jaxb.xml");
Path temp = Files.createTempFile("encrypted", ".enc");
Path dec = Files.createTempFile("decrypted", ".xml");
// encrypt file
FileChannel out = FileChannel.open(temp, StandardOpenOption.WRITE);
WritableByteChannel enc = new EncryptingOutputStream(out, keyPair.getPublic());
FileChannel in = FileChannel.open(source);
in.transferTo(0, Long.MAX_VALUE, enc);
in.close();
enc.close();
out.close();
// decrypt file
in = FileChannel.open(temp, StandardOpenOption.READ);
ReadableByteChannel decrypted = new DecryptingInputStream(in, keyPair.getPrivate());
out = FileChannel.open(dec, StandardOpenOption.WRITE);
out.transferFrom(decrypted, 0, Long.MAX_VALUE);
in.close();
out.close();
decrypted.close();
Files.delete(temp);
// compare files
assertEqualFiles(dec, source);
Files.delete(dec);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment