/* * [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 ) ) ) ); } }