Skip to main content

Secure file transfer with Java and Versile

Just made an example of how to transfer a file over a secure and authenticated connection in pure Java - without having to use additional technology like ssh/scp. Versile has all you need for creating a secure and authenticated connection, and you'll decide what files you want to expose - nothing more - nothing less.

This is the file server (include versile.jar in your classpath - and create a file called "personregister.txt" in the working directory):

 package versileexample;  
 import java.io.File;  
 import java.io.FileNotFoundException;  
 import java.io.RandomAccessFile;  
 import java.security.interfaces.RSAPublicKey;  
 import java.util.logging.Level;  
 import java.util.logging.Logger;  
 import org.versile.Versile;  
 import org.versile.common.auth.VAuth;  
 import org.versile.common.auth.VCredentials;  
 import org.versile.common.auth.VPrivateCredentials;  
 import org.versile.common.peer.VInetSocketPeer;  
 import org.versile.common.peer.VPeer;  
 import org.versile.crypto.VDecentralIdentity;  
 import org.versile.crypto.VRSAKeyPair;  
 import org.versile.orb.entity.VObject;  
 import org.versile.orb.external.Publish;  
 import org.versile.orb.external.VExternal;  
 import org.versile.orb.link.VLinkAuth;  
 import org.versile.orb.service.VGatewayFactory;  
 import org.versile.reactor.service.VOPService;  
 import org.versile.vse.stream.VByteStreamer;  
 import org.versile.vse.stream.VSimpleFileStreamerData;  
 import org.versile.vse.stream.VStreamError;  
 /**  
  * A simple file server  
  */  
 public class VersileFileServerExample extends VExternal {  
   /**  
    * Remotely callable method returning stream to a shared file. We'll only share one file in this example.  
    * @return  
    * @throws VStreamError  
    * @throws FileNotFoundException   
    */  
   @Publish(show=true, ctx=false)    
   public VByteStreamer getSharedFile() throws VStreamError, FileNotFoundException {         
     VSimpleFileStreamerData.Config cfg = new VSimpleFileStreamerData.Config();  
     cfg.setReadable(true);  
     System.out.println("Returning stream to shared file");  
     // Return streamer to the textfile "personregister.txt"  
     return new VByteStreamer(  
         new VSimpleFileStreamerData(  
         new RandomAccessFile(new File("personregister.txt"), "r"),  
         cfg));  
   }  
   /**  
    * Factory class for remotely accessible service object  
    */  
   public static class Factory implements VGatewayFactory {  
     @Override  
     public VObject build() {        
       return new VersileFileServerExample();  
     }  
   }  
   public static void main(String[] args) throws Exception {  
     // Accept AGPL license  
     Versile.setInternalUseAGPL();  
     // Public key authorized for login  
     final RSAPublicKey authorizedKey = VRSAKeyPair.importPublicArmored(("-----BEGIN RSA PUBLIC KEY-----\n" +  
                     "MIGJAoGBAL80uZMXR6vclBMLL9OZS/bpIO6AIitZpEyTZ96qMbpkgoqP6Wi9ev1Isag3jsZORpPf\n" +  
                     "6OJfMEHAWcF5YxzzLKDd13fAswRzbF2VA+QRikJkT8OED1wseX5rZbo7CenStVd6JMecOUr7wsb9\n" +  
                     "MTY/fgcQyYY8u/yBaBUSLeoJfQPdAgMBAAE=\n" +  
                     "-----END RSA PUBLIC KEY-----").getBytes());  
     // Create server identity  
     VPrivateCredentials serverCredentials = new VPrivateCredentials(VDecentralIdentity.dia(1024, "VersileExample", "Versile", "Versile2014"));  
     // Create service  
     VOPService service = new VOPService(new Factory(), serverCredentials,   
         new VLinkAuth(new VAuth(true,false) {  
       /**  
        * Logic for which public keys to accept - this example only accepts the key above  
        * @param credentials  
        * @return   
        */  
       @Override  
       public synchronized boolean acceptCredentials(VCredentials credentials) {  
         if(authorizedKey.equals(credentials.getPublicKey())){  
           Logger.getLogger(getClass().getName()).info("Authorized link");  
           return true;  
         } else {  
           Logger.getLogger(getClass().getName()).info("Unknown public key");  
           return false;  
         }  
       }  
       /**  
        * We can also accept only specified peers - but in this example we'll just log the ip address of the client  
        * @param peer  
        * @return   
        */  
       @Override  
       public synchronized boolean acceptPeer(VPeer peer) {  
         VInetSocketPeer ipeer = (VInetSocketPeer)peer;  
         Logger.getLogger(getClass().getName()).  
             log(Level.INFO,   
             "Link auth request from {0}",   
             ipeer.getInetAddress().getHostString());        
         return super.acceptPeer(peer);  
       }            
     }));  
     // Start file server  
     service.start();  
   }  
 }  

And now a simple client for downloading the shared file (include versile.jar in your classpath):

 package versileexample;  
 import java.io.FileOutputStream;  
 import org.versile.Versile;  
 import org.versile.common.auth.VPrivateCredentials;  
 import org.versile.crypto.VDecentralIdentity;  
 import org.versile.crypto.VRSAKeyPair;  
 import org.versile.orb.entity.VProxy;  
 import org.versile.orb.link.VLinkReference;  
 import org.versile.reactor.url.VUrl;  
 import org.versile.vse.VSEResolver;  
 import org.versile.vse.stream.VByteStream;  
 import org.versile.vse.stream.VByteStreamerProxy;  
 public class VersileFileServerClientExample {  
   public static void main(String[] args) throws Exception {  
     // Versile license   
     Versile.setInternalUseAGPL();  
     // Enable streaming data types  
     VSEResolver.addToGlobalModules();  
     // Create credentials for logging into the server  
     VRSAKeyPair key = VDecentralIdentity.dia(1024, "test", "peter", "dfw/23hcFDawqaDreq");  
     VPrivateCredentials credentials = new VPrivateCredentials(key);  
     // Export public key (can be inserted into authorized key variable in server example)  
     System.out.println(new String(key.exportPublicArmoredPkcs()));  
     // Get proxy to server and connect  
     VProxy proxy = (VProxy) VUrl.resolve("vop://localhost/",credentials);  
     // Call method returning stream to shared file and connect to stream  
     VByteStreamerProxy streamer = (VByteStreamerProxy) proxy.call("getSharedFile", new Object[] {});  
     VByteStream stream = streamer.connect();  
     stream.waitActive();  
     // Read from stream and write to local file  
     FileOutputStream fos = new FileOutputStream("downloadedfile.txt");  
     byte[] filedata;  
     do {  
       filedata = stream.read(65536);        
       fos.write(filedata);  
     } while(filedata.length>0);  
     fos.close();  
     stream.close();      
     System.out.println("File downloaded and saved");  
     // Disconnect from server  
     ((VLinkReference)proxy.get())._v_link().shutdown(false);  
   }  
 }  

Comments

Popular posts from this blog

My VNC based development environment with Visual Studio Code running on Ubuntu

I've used this setup for my development environment for several years - giving me a developer desktop I can access anywhere. Even from my mobile phone. I've been fixing bugs, writing code and deployed emergency fixes from the bus, train and mountain tops. The setup is based on a lightweight desktop environment. There are lot of alternatives, but I've chosen fluxbox. From a plain AWS Amazon Ubuntu 16.04 ec2 instance I've started like this: sudo apt-get update sudo apt-get install fluxbox Download and extract tigervnc from https://github.com/TigerVNC/tigervnc/releases (I downloaded the binary file from https://bintray.com/tigervnc/stable/tigervnc/1.7.0 named tigervnc-1.7.0.x86_64.tar.gz ) then extract: tar -xvzf tigervnc-1.7.0.x86_64.tar.gz You need to install: sudo apt-get install x11-xkb-utils You need to edit .vnc/xstartup: nano .vnc/xstartup last line replace "twm &" with: fluxbox & Then you can start the

Angular components not reloading on route change

Spent a long time wondering why route changes caused strange effects on my component, and found out that it was because my component wasn't reloading at all. I had this assumption that when a route parameter changed (e.g. /projects/1 changed to /projects/2 ) the component for the route would be reloaded. But this is not the default behaviour of the Angular router. The default behaviour of the Angular router is to reuse the route if the configuration is the same (and not reload the component). But we can override this by providing a RouteReuseStrategy to our @NgModule: providers: [ { provide: RouteReuseStrategy, useClass: AARouteReuseStrategy } ] The full custom implementation of the RouteReuseStrategy will then be like this (and it's the shouldReuseRoute method that changes the behaviour so that the component is reloaded on route parameter change): export class AARouteReuseStrategy extends RouteReuseStrategy { shouldDetach(route: ActivatedRou

Intercepting and adjusting SQL generated by Eclipselink JPA

In some cases it might be useful to intercept and adjust the SQL generated by EclipseLink JPA. E.g. if you want to force an index in mysql you need to append force index (myindex) to the table name. If you create a query on the entitymanager: Query q = em.createQuery("select e from MyEntity"); you can cast it to the EclipseLink JpaQuery: JpaQuery jq = (JpaQuery)q; and the JpaQuery gives you access to the EclipseLink DataBaseQuery where you can prepare it before executing it: DatabaseQuery dbQuery = q.getDatabaseQuery(); dbQuery.prepareCall(((org.eclipse.persistence.jpa.JpaEntityManager)em).getActiveSession(),new DatabaseRecord()); You can now get the sql string and add the forced index: dbQuery.setSQLString(dbQuery.getSQLString()+" force index (myindex)"); and finally you can get the resultset using q.getResultList(). If you have parameters in the sql it's a bit more to it. Currently I've only found one option - probably not optimal since the query is transl