Skip to content

Commit

Permalink
BACKLOG-2041 - Create encrypted data transfer flow
Browse files Browse the repository at this point in the history
What was done:
1) added granular exception handling
2) added negative scenario tests
  • Loading branch information
kolinus committed Feb 19, 2015
1 parent dfe0dfb commit 7e90065
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 31 deletions.
Expand Up @@ -22,17 +22,21 @@

package org.pentaho.di.core.encryption;

import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

import org.pentaho.di.core.logging.LogChannel;
Expand All @@ -43,8 +47,8 @@
public class CertificateGenEncryptUtil {

public static final int KEY_SIZE = 1024;
public static final String PUBLIC_KEY_ALGORYTHM = "RSA";
public static final String SINGLE_KEY_ALGORYTHM = "AES";
public static final String PUBLIC_KEY_ALGORITHM = "RSA";
public static final String SINGLE_KEY_ALGORITHM = "AES";
public static final String TRANSMISSION_CIPHER_PARAMS = "RSA/ECB/PKCS1Padding";
private static final LoggingObjectInterface loggingObject = new SimpleLoggingObject(
"Certificate Encryption Utility", LoggingObjectType.GENERAL, null );
Expand All @@ -53,7 +57,7 @@ public class CertificateGenEncryptUtil {
public static KeyPair generateKeyPair() {
KeyPair pair = null;
try {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance( PUBLIC_KEY_ALGORYTHM );
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance( PUBLIC_KEY_ALGORITHM );
keyPairGen.initialize( KEY_SIZE );
pair = keyPairGen.generateKeyPair();
} catch ( Exception ex ) {
Expand All @@ -63,42 +67,44 @@ public static KeyPair generateKeyPair() {
}

public static Key generateSingleKey() throws NoSuchAlgorithmException {
Key key = KeyGenerator.getInstance( SINGLE_KEY_ALGORYTHM ).generateKey();
Key key = KeyGenerator.getInstance( SINGLE_KEY_ALGORITHM ).generateKey();
return key;
}

public static byte[] encodeKeyForTransmission( Key encodingKey, Key keyToEncode ) throws Exception {
public static byte[] encodeKeyForTransmission( Key encodingKey, Key keyToEncode ) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance( TRANSMISSION_CIPHER_PARAMS );
cipher.init( Cipher.WRAP_MODE, encodingKey );
byte[] encodedKey = cipher.wrap( keyToEncode );
return encodedKey;
}

public static Key decodeTransmittedKey( byte[] sessionKey, byte[] transmittedKey, boolean privateKey )
throws Exception {
throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
KeySpec keySpec = null;
Key keyKey = null;
if ( transmittedKey == null || sessionKey == null ) {
return null;
}
if ( !privateKey ) {
keySpec = new X509EncodedKeySpec( sessionKey );
keyKey = KeyFactory.getInstance( PUBLIC_KEY_ALGORYTHM ).generatePublic( keySpec );
keyKey = KeyFactory.getInstance( PUBLIC_KEY_ALGORITHM ).generatePublic( keySpec );
} else {
keySpec = new PKCS8EncodedKeySpec( sessionKey );
keyKey = KeyFactory.getInstance( PUBLIC_KEY_ALGORYTHM ).generatePrivate( keySpec );
keyKey = KeyFactory.getInstance( PUBLIC_KEY_ALGORITHM ).generatePrivate( keySpec );
}
Cipher keyCipher = Cipher.getInstance( TRANSMISSION_CIPHER_PARAMS );
keyCipher.init( Cipher.UNWRAP_MODE, keyKey );
return keyCipher.unwrap( transmittedKey, SINGLE_KEY_ALGORYTHM, Cipher.SECRET_KEY );
return keyCipher.unwrap( transmittedKey, SINGLE_KEY_ALGORITHM, Cipher.SECRET_KEY );
}

public static Cipher initDecryptionCipher( Key unwrappedKey, byte[] unencryptedKey ) throws Exception {
Cipher decryptionCip = Cipher.getInstance( SINGLE_KEY_ALGORYTHM );
public static Cipher initDecryptionCipher( Key unwrappedKey, byte[] unencryptedKey )
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
Cipher decryptionCip = Cipher.getInstance( SINGLE_KEY_ALGORITHM );
if ( unwrappedKey != null ) {
decryptionCip.init( Cipher.ENCRYPT_MODE, unwrappedKey );
} else {
SecretKeySpec sks = new SecretKeySpec( unencryptedKey, SINGLE_KEY_ALGORYTHM );
SecretKeySpec sks = new SecretKeySpec( unencryptedKey, SINGLE_KEY_ALGORITHM );
decryptionCip.init( Cipher.ENCRYPT_MODE, sks );
}
return decryptionCip;
Expand All @@ -107,7 +113,7 @@ public static Cipher initDecryptionCipher( Key unwrappedKey, byte[] unencryptedK
public static byte[] encryptUsingKey( byte[] data, Key key ) {
byte[] result = null;
try {
Cipher cipher = Cipher.getInstance( PUBLIC_KEY_ALGORYTHM );
Cipher cipher = Cipher.getInstance( PUBLIC_KEY_ALGORITHM );
cipher.init( Cipher.ENCRYPT_MODE, key );
result = cipher.doFinal( data );
} catch ( Exception ex ) {
Expand All @@ -119,7 +125,7 @@ public static byte[] encryptUsingKey( byte[] data, Key key ) {
public static byte[] decryptUsingKey( byte[] data, Key key ) {
byte[] result = null;
try {
Cipher cipher = Cipher.getInstance( PUBLIC_KEY_ALGORYTHM );
Cipher cipher = Cipher.getInstance( PUBLIC_KEY_ALGORITHM );
cipher.init( Cipher.DECRYPT_MODE, key );
result = cipher.doFinal( data );
} catch ( Exception ex ) {
Expand Down
Expand Up @@ -128,4 +128,90 @@ public void testSessionKeyEncryptionDecryption2() throws Exception {
Key key1 = CertificateGenEncryptUtil.decodeTransmittedKey( privateKey.getEncoded(), encryptedKey, true );
assertTrue( key.equals( key1 ) );
}

@Test
public void testImproperSessionKeyEncryptionDecryption() {
try {
Key key = CertificateGenEncryptUtil.generateSingleKey();
KeyPair kp = CertificateGenEncryptUtil.generateKeyPair();
Key privateKey = kp.getPrivate();
byte[] encryptedKey = CertificateGenEncryptUtil.encodeKeyForTransmission( kp.getPublic(), key );
Key key1 = CertificateGenEncryptUtil.decodeTransmittedKey( privateKey.getEncoded(), encryptedKey, false );
fail();
} catch ( Exception ex ) {}
}

@Test
public void testImproperSessionKeyEncryptionDecryption2() throws Exception {
try {
Key key = CertificateGenEncryptUtil.generateSingleKey();
KeyPair kp = CertificateGenEncryptUtil.generateKeyPair();
Key privateKey = kp.getPrivate();
byte[] encryptedKey = CertificateGenEncryptUtil.encodeKeyForTransmission( privateKey, key );
Key key1 = CertificateGenEncryptUtil.decodeTransmittedKey( kp.getPublic().getEncoded(), encryptedKey, true );
fail();
} catch ( Exception ex ) {}
}

@Test
public void testImproperSessionKeyEncryptionDecryption3() {
try {
Key key = CertificateGenEncryptUtil.generateSingleKey();
KeyPair kp = CertificateGenEncryptUtil.generateKeyPair();
Key privateKey = kp.getPrivate();
byte[] encryptedKey = CertificateGenEncryptUtil.encodeKeyForTransmission( kp.getPublic(), key );
byte[] encryptedKey1 = new byte[encryptedKey.length];
System.arraycopy( encryptedKey, 0, encryptedKey1, 0, encryptedKey.length );
encryptedKey1[encryptedKey1.length - 1] = (byte) ( encryptedKey1[encryptedKey1.length - 1] - 1 );
encryptedKey = encryptedKey1;
Key key1 = CertificateGenEncryptUtil.decodeTransmittedKey( privateKey.getEncoded(), encryptedKey, true );
fail();
} catch ( Exception ex ) {}
}

@Test
public void testImproperSessionKeyEncryptionDecryption4() throws Exception {
try {
Key key = CertificateGenEncryptUtil.generateSingleKey();
KeyPair kp = CertificateGenEncryptUtil.generateKeyPair();
Key privateKey = kp.getPrivate();
byte[] encryptedKey = CertificateGenEncryptUtil.encodeKeyForTransmission( privateKey, key );
byte[] encryptedKey1 = new byte[encryptedKey.length];
System.arraycopy( encryptedKey, 0, encryptedKey1, 0, encryptedKey.length );
encryptedKey1[encryptedKey1.length - 1] = (byte) ( encryptedKey1[encryptedKey1.length - 1] - 1 );
encryptedKey = encryptedKey1;
Key key1 = CertificateGenEncryptUtil.decodeTransmittedKey( kp.getPublic().getEncoded(), encryptedKey, false );
fail();
} catch ( Exception ex ) {}
}

@Test
public void testImproperSessionKeyEncryptionDecryption5() {
try {
Key key = CertificateGenEncryptUtil.generateSingleKey();
KeyPair kp = CertificateGenEncryptUtil.generateKeyPair();
Key privateKey = kp.getPrivate();
byte[] encryptedKey = CertificateGenEncryptUtil.encodeKeyForTransmission( kp.getPublic(), key );
byte[] encryptedKey1 = new byte[privateKey.getEncoded().length];
System.arraycopy( privateKey.getEncoded(), 0, encryptedKey1, 0, privateKey.getEncoded().length );
encryptedKey1[encryptedKey1.length - 1] = (byte) ( encryptedKey1[encryptedKey1.length - 1] - 1 );
Key key1 = CertificateGenEncryptUtil.decodeTransmittedKey( encryptedKey1, encryptedKey, true );
fail();
} catch ( Exception ex ) {}
}

@Test
public void testImproperSessionKeyEncryptionDecryption6() throws Exception {
try {
Key key = CertificateGenEncryptUtil.generateSingleKey();
KeyPair kp = CertificateGenEncryptUtil.generateKeyPair();
Key privateKey = kp.getPrivate();
byte[] encryptedKey = CertificateGenEncryptUtil.encodeKeyForTransmission( privateKey, key );
byte[] encryptedKey1 = new byte[kp.getPublic().getEncoded().length];
System.arraycopy( kp.getPublic().getEncoded(), 0, encryptedKey1, 0, kp.getPublic().getEncoded().length );
encryptedKey1[encryptedKey1.length - 1] = (byte) ( encryptedKey1[encryptedKey1.length - 1] - 1 );
Key key1 = CertificateGenEncryptUtil.decodeTransmittedKey( encryptedKey1, encryptedKey, false );
fail();
} catch ( Exception ex ) {}
}
}
20 changes: 10 additions & 10 deletions engine/src/org/pentaho/di/trans/TransMeta.java
Expand Up @@ -282,8 +282,8 @@ public class TransMeta extends AbstractMeta implements XMLInterface, Comparator<
/** The log channel interface. */
protected LogChannelInterface log;

protected byte[] key;
boolean privateKey;
protected byte[] keyForSessionKey;
boolean isKeyPrivate;

/**
* The TransformationType enum describes the various types of transformations in terms of execution, including Normal,
Expand Down Expand Up @@ -2427,11 +2427,11 @@ public String getXML( boolean includeSteps, boolean includeDatabase, boolean inc
.append( " " ).append( XMLHandler.addTagValue( "modified_date", XMLHandler.date2string( modifiedDate ) ) );

try {
retval.append( " " ).append( XMLHandler.addTagValue( "key", key ) );
retval.append( " " ).append( XMLHandler.addTagValue( "key_for_session_key", keyForSessionKey ) );
} catch ( Exception ex ) {
log.logError( "Unable to decode key", ex );
}
retval.append( " " ).append( XMLHandler.addTagValue( "private", privateKey ) );
retval.append( " " ).append( XMLHandler.addTagValue( "is_key_private", isKeyPrivate ) );

retval.append( " " ).append( XMLHandler.closeTag( XML_TAG_INFO ) ).append( Const.CR );

Expand Down Expand Up @@ -3299,8 +3299,8 @@ public void loadXML( Node transnode, String fname, IMetaStore metaStore, Reposit
//
attributesMap = AttributesUtil.loadAttributes( XMLHandler.getSubNode( transnode, AttributesUtil.XML_TAG ) );

key = XMLHandler.stringToBinary( XMLHandler.getTagValue( infonode, "key" ) );
privateKey = "Y".equals( XMLHandler.getTagValue( infonode, "private" ) );
keyForSessionKey = XMLHandler.stringToBinary( XMLHandler.getTagValue( infonode, "key_for_session_key" ) );
isKeyPrivate = "Y".equals( XMLHandler.getTagValue( infonode, "is_key_private" ) );

} catch ( KettleXMLException xe ) {
throw new KettleXMLException( BaseMessages.getString(
Expand Down Expand Up @@ -3332,19 +3332,19 @@ public void loadXML( Node transnode, String fname, IMetaStore metaStore, Reposit
}

public byte[] getKey() {
return key;
return keyForSessionKey;
}

public void setKey( byte[] key ) {
this.key = key;
this.keyForSessionKey = key;
}

public boolean isPrivateKey() {
return privateKey;
return isKeyPrivate;
}

public void setPrivateKey( boolean privateKey ) {
this.privateKey = privateKey;
this.isKeyPrivate = privateKey;
}

/**
Expand Down
15 changes: 14 additions & 1 deletion engine/src/org/pentaho/di/trans/cluster/TransSplitter.java
Expand Up @@ -22,8 +22,10 @@

package org.pentaho.di.trans.cluster;

import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
Expand All @@ -37,6 +39,9 @@
import java.util.Set;
import java.util.UUID;

import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import org.pentaho.di.cluster.ClusterSchema;
import org.pentaho.di.cluster.SlaveServer;
import org.pentaho.di.core.Const;
Expand Down Expand Up @@ -469,7 +474,15 @@ public void splitOriginalTransformation() throws KettleException {
PrivateKey privK = pair.getPrivate();

Key key1 = CertificateGenEncryptUtil.generateSingleKey();
transformationKey = CertificateGenEncryptUtil.encodeKeyForTransmission( privK, key1 );
try {
transformationKey = CertificateGenEncryptUtil.encodeKeyForTransmission( privK, key1 );
} catch ( InvalidKeyException ex ) {
masterTransMeta.getLogChannel().logError( "Invalid key was used for encoding", ex );
} catch ( IllegalBlockSizeException ex ) {
masterTransMeta.getLogChannel().logError( "Error happenned during key encoding", ex );
} catch ( Exception ex ) {
masterTransMeta.getLogChannel().logError( "Error happenned during encryption initialization", ex );
}
}

for ( int r = 0; r < referenceSteps.length; r++ ) {
Expand Down
46 changes: 40 additions & 6 deletions engine/src/org/pentaho/di/trans/step/RemoteStep.java
Expand Up @@ -33,7 +33,9 @@
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.spec.InvalidKeySpecException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
Expand Down Expand Up @@ -358,10 +360,26 @@ public void run() {
socketOut = bufferedOutputStream;
if ( encryptingStreams && key != null ) {
byte[] transKey = baseStep.getTransMeta().getKey();
Key unwrappedKey = CertificateGenEncryptUtil.decodeTransmittedKey( transKey, key,
Key unwrappedKey = null;
try {
unwrappedKey = CertificateGenEncryptUtil.decodeTransmittedKey( transKey, key,
baseStep.getTransMeta().isPrivateKey() );
Cipher decryptionCip = CertificateGenEncryptUtil.initDecryptionCipher( unwrappedKey, key );
socketOut = cipherOutputStream = new CipherOutputStream( bufferedOutputStream, decryptionCip );
} catch ( InvalidKeyException ex ) {
baseStep.logError( "Invalid key was received", ex );
} catch ( InvalidKeySpecException ex ) {
baseStep.logError( "Invalid key specification was received. Most probably public key was "
+ "sent instead of private or vice versa", ex );
} catch ( Exception ex ) {
baseStep.logError( "Error occurred during encryption initialization", ex );
}
try {
Cipher decryptionCip = CertificateGenEncryptUtil.initDecryptionCipher( unwrappedKey, key );
socketOut = cipherOutputStream = new CipherOutputStream( bufferedOutputStream, decryptionCip );
} catch ( InvalidKeyException ex ) {
baseStep.logError( "Invalid key was received", ex );
} catch ( Exception ex ) {
baseStep.logError( "Error occurred during encryption initialization", ex );
}
}
outputStream = new DataOutputStream( socketOut );

Expand Down Expand Up @@ -570,10 +588,26 @@ public synchronized BlockingRowSet openReaderSocket( final BaseStep baseStep ) t

if ( encryptingStreams && key != null ) {
byte[] transKey = baseStep.getTransMeta().getKey();
Key unwrappedKey = CertificateGenEncryptUtil.decodeTransmittedKey( transKey, key,
Key unwrappedKey = null;
try {
unwrappedKey = CertificateGenEncryptUtil.decodeTransmittedKey( transKey, key,
baseStep.getTransMeta().isPrivateKey() );
Cipher decryptionCip = CertificateGenEncryptUtil.initDecryptionCipher( unwrappedKey, key );
socketStream = cipherInputStream = new CipherInputStream( bufferedInputStream, decryptionCip );
} catch ( InvalidKeyException ex ) {
baseStep.logError( "Invalid key was received", ex );
} catch ( InvalidKeySpecException ex ) {
baseStep.logError( "Invalid key specification was received. Most probably public key was "
+ "sent instead of private or vice versa", ex );
} catch ( Exception ex ) {
baseStep.logError( "Error occurred during encryption initialization", ex );
}
try {
Cipher decryptionCip = CertificateGenEncryptUtil.initDecryptionCipher( unwrappedKey, key );
socketStream = cipherInputStream = new CipherInputStream( bufferedInputStream, decryptionCip );
} catch ( InvalidKeyException ex ) {
baseStep.logError( "Invalid key was received", ex );
} catch ( Exception ex ) {
baseStep.logError( "Error occurred during encryption initialization", ex );
}
}
inputStream = new DataInputStream( socketStream );

Expand Down

0 comments on commit 7e90065

Please sign in to comment.