/*
* [Encrypt.java]
*
* Summary: Encrypt messages with RSA.
*
* Copyright: (c) 2004-2017 Roedy Green, Canadian Mind Products, http://mindprod.com
*
* Licence: This software may be copied and used freely for any purpose but military.
* http://mindprod.com/contact/nonmil.html
*
* Requires: JDK 1.8+
*
* Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/
*
* Version History:
* 1.4 2006-03-05 reformat with IntelliJ and add Javadoc.
*/
package com.mindprod.transporter;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
/**
* Encrypt messages with RSA.
*
* Uses GZIP compression, salting, RSA and base64u armouring.
* No signing. See Transporter.java for example of use.
*
* @author Roedy Green, Canadian Mind Products
* @version 1.4 2006-03-05 reformat with IntelliJ and add Javadoc.
* @since 2004-06-08
*/
public final class Encrypt extends Wrap
{
/**
* true if debugging
*/
private static final boolean DEBUGGING = false;
/**
* The receiver's public key, used by sender to encrypt mesages for his eyes only.
*/
private final PublicKey receiverPublicKey;
/**
* constructor. set up public key prior to encrypting
*
* @param receiverPublicKey public key information
*/
public Encrypt( PublicKey receiverPublicKey )
{
super();
this.receiverPublicKey = receiverPublicKey;
}
/**
* salt, RSA encrypt.
*
* @param compressedMessage Message, usually precompressed.
*
* @return encrypted message.
*/
byte[] encrypt( byte[] compressedMessage )
{
// encrypted messages looks like this:
// (16-bit length-of-block) ( 32-bit salt encrypted, encrypted message
// block) repeat
// encrypted with receiver's public key.
ArrayList v = new ArrayList<>( 11 );
// how big of chunks we can bite off to encrypt at a pop.
// We must leave room for the 4 byte salt, but not for the 2-byte
// unencrypted length block header.
int blockSize = receiverPublicKey.keySize / 8 - 4;
for ( int chunkStart = 0; chunkStart < compressedMessage
.length; chunkStart += blockSize )
{
/* process each block of the message */
int blockLength =
Math.min( blockSize,
compressedMessage.length - chunkStart );
byte[] block =
Tools.subarray( compressedMessage,
chunkStart,
blockLength );
if ( DEBUGGING )
{
Tools.dumpBytes( "block", block );
}
/* salt block, will prepend 4 random bytes. */
byte[] saltedBlock = Tools.salt( block );
if ( DEBUGGING )
{
Tools.dumpBytes( "salted", saltedBlock );
}
/* RSA encrypt each block, will grow to keysize/8 */
byte[] encryptedBlock =
Tools.processWithPublicKey( saltedBlock,
receiverPublicKey );
if ( DEBUGGING )
{
Tools.dumpBytes( "encrypted", encryptedBlock );
}
/* prepend block with unencrypted unsigned 16-bit length */
byte[] countedEncryptedBlock =
Tools.prependLength( encryptedBlock );
// save it for joining end to end.
v.add( countedEncryptedBlock );
} // end for
/*
* At this point compositeMessage contains the encrypted message, made
* up of segments
*/
byte[] compositeMessage = Tools.join( v );
// debugging
if ( DEBUGGING )
{
Tools.dumpBytes( "composite", compositeMessage );
}
return compositeMessage;
}
/**
* Wraps object for sending via CIA Post, serialised, gzips, encrypts and base64u encodes. Does not build
* parm=value.
*
* @param o Object to wrap for sending.
*
* @return base64u-encoded string to send.
* @throws IOException
*/
public String wrap( Serializable o ) throws IOException
{
return armour( encrypt( compress( serialize( o ) ) ) );
}
}