You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by ay...@apache.org on 2012/10/23 11:43:57 UTC

svn commit: r1401223 - in /cxf/trunk/api/src: main/java/org/apache/cxf/io/CachedOutputStream.java test/java/org/apache/cxf/io/CachedOutputStreamTest.java

Author: ay
Date: Tue Oct 23 09:43:57 2012
New Revision: 1401223

URL: http://svn.apache.org/viewvc?rev=1401223&view=rev
Log:
[CXF-4596] adding an option for encrypting tmp files generated by COS

Modified:
    cxf/trunk/api/src/main/java/org/apache/cxf/io/CachedOutputStream.java
    cxf/trunk/api/src/test/java/org/apache/cxf/io/CachedOutputStreamTest.java

Modified: cxf/trunk/api/src/main/java/org/apache/cxf/io/CachedOutputStream.java
URL: http://svn.apache.org/viewvc/cxf/trunk/api/src/main/java/org/apache/cxf/io/CachedOutputStream.java?rev=1401223&r1=1401222&r2=1401223&view=diff
==============================================================================
--- cxf/trunk/api/src/main/java/org/apache/cxf/io/CachedOutputStream.java (original)
+++ cxf/trunk/api/src/main/java/org/apache/cxf/io/CachedOutputStream.java Tue Oct 23 09:43:57 2012
@@ -33,10 +33,18 @@ import java.io.OutputStream;
 import java.io.PipedInputStream;
 import java.io.PipedOutputStream;
 import java.io.Reader;
+import java.security.GeneralSecurityException;
+import java.security.Key;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.KeyGenerator;
+import javax.crypto.spec.IvParameterSpec;
+
 import org.apache.cxf.common.util.SystemPropertyAction;
 import org.apache.cxf.helpers.FileUtils;
 import org.apache.cxf.helpers.IOUtils;
@@ -46,6 +54,7 @@ public class CachedOutputStream extends 
     private static final File DEFAULT_TEMP_DIR;
     private static int defaultThreshold;
     private static long defaultMaxSize;
+    private static String defaultCipherTransformation;
     static {
         
         String s = SystemPropertyAction.getPropertyOrNull("org.apache.cxf.io.CachedOutputStream.OutputDirectory");
@@ -62,6 +71,7 @@ public class CachedOutputStream extends 
 
         setDefaultThreshold(-1);
         setDefaultMaxSize(-1);
+        setDefaultCipherTransformation(null);
     }
 
     protected boolean outputLocked;
@@ -78,6 +88,10 @@ public class CachedOutputStream extends 
     private File tempFile;
     private File outputDir = DEFAULT_TEMP_DIR;
     private boolean allowDeleteOfFile = true;
+    private String cipherTransformation = defaultCipherTransformation;
+    private Cipher enccipher;
+    private Cipher deccipher;
+    
 
     private List<CachedOutputStreamCallback> callbacks;
     
@@ -235,7 +249,7 @@ public class CachedOutputStream extends 
                 // read the file
                 currentStream.close();
                 if (copyOldContent) {
-                    FileInputStream fin = new FileInputStream(tempFile);
+                    InputStream fin = createInputStream(tempFile);
                     IOUtils.copyAndCloseInput(fin, out);
                 }
                 streamList.remove(currentStream);
@@ -265,7 +279,7 @@ public class CachedOutputStream extends 
             }
         } else {
             // read the file
-            FileInputStream fin = new FileInputStream(tempFile);
+            InputStream fin = createInputStream(tempFile);
             return IOUtils.readBytesFromStream(fin);
         }
     }
@@ -280,7 +294,7 @@ public class CachedOutputStream extends 
             }
         } else {
             // read the file
-            FileInputStream fin = new FileInputStream(tempFile);
+            InputStream fin = createInputStream(tempFile);
             IOUtils.copyAndCloseInput(fin, out);
         }
     }
@@ -310,7 +324,7 @@ public class CachedOutputStream extends 
             }
         } else {
             // read the file
-            FileInputStream fin = new FileInputStream(tempFile);
+            InputStream fin = createInputStream(tempFile);
             Reader reader = new InputStreamReader(fin, charsetName);
             char bytes[] = new char[1024];
             long x = reader.read(bytes);
@@ -350,7 +364,7 @@ public class CachedOutputStream extends 
             }
         } else {
             // read the file
-            FileInputStream fin = new FileInputStream(tempFile);
+            InputStream fin = createInputStream(tempFile);
             Reader reader = new InputStreamReader(fin, charsetName);
             char bytes[] = new char[1024];
             int x = reader.read(bytes);
@@ -439,7 +453,7 @@ public class CachedOutputStream extends 
                 tempFile = FileUtils.createTempFile("cos", "tmp", outputDir, false);
             }
             
-            currentStream = new FileOutputStream(tempFile);
+            currentStream = createOutputStream(tempFile);
             bout.writeTo(currentStream);
             currentStream = new BufferedOutputStream(currentStream);
             inmem = false;
@@ -475,7 +489,7 @@ public class CachedOutputStream extends 
             }
         } else {
             try {
-                FileInputStream fileInputStream = new FileInputStream(tempFile) {
+                InputStream fileInputStream = new FileInputStream(tempFile) {
                     boolean closed;
                     public void close() throws IOException {
                         if (!closed) {
@@ -486,6 +500,18 @@ public class CachedOutputStream extends 
                     }
                 };
                 streamList.add(fileInputStream);
+                if (cipherTransformation != null) {
+                    fileInputStream = new CipherInputStream(fileInputStream, deccipher) {
+                        boolean closed;
+                        public void close() throws IOException {
+                            if (!closed) {
+                                super.close();
+                                closed = true;
+                            }
+                        }
+                    };
+                }
+                
                 return fileInputStream;
             } catch (FileNotFoundException e) {
                 throw new IOException("Cached file was deleted, " + e.toString());
@@ -527,6 +553,10 @@ public class CachedOutputStream extends 
     public void setMaxSize(long maxSize) {
         this.maxSize = maxSize;
     }
+
+    public void setCipherTransformation(String cipherTransformation) {
+        this.cipherTransformation = cipherTransformation;
+    }
     
     public static void setDefaultMaxSize(long l) {
         if (l == -1) {
@@ -548,4 +578,72 @@ public class CachedOutputStream extends 
         defaultThreshold = i;
         
     }
+    public static void setDefaultCipherTransformation(String n) {
+        if (n == null) {
+            n = SystemPropertyAction.getPropertyOrNull("org.apache.cxf.io.CachedOutputStream.CipherTransformation");
+        }
+        defaultCipherTransformation = n;
+    }
+
+    private synchronized void initCiphers() throws GeneralSecurityException {
+        if (enccipher == null) {
+            int d = cipherTransformation.indexOf('/');
+            String a;
+            if (d > 0) {
+                a = cipherTransformation.substring(0, d);
+            } else {
+                a = cipherTransformation;
+            }
+            try {
+                Key key = KeyGenerator.getInstance(a).generateKey();
+                enccipher = Cipher.getInstance(cipherTransformation);
+                deccipher = Cipher.getInstance(cipherTransformation);
+                enccipher.init(Cipher.ENCRYPT_MODE, key);
+                final byte[] ivp = enccipher.getIV();
+                deccipher.init(Cipher.DECRYPT_MODE, key, ivp == null ? null : new IvParameterSpec(ivp));
+            } catch (GeneralSecurityException e) {
+                enccipher = null;
+                deccipher = null;
+                throw e;
+            }
+        }
+    }
+
+    private OutputStream createOutputStream(File file) throws IOException {
+        OutputStream out = new FileOutputStream(file);
+        if (cipherTransformation != null) {
+            try {
+                initCiphers();
+            } catch (GeneralSecurityException e) {
+                throw new IOException(e.getMessage(), e);
+            }
+            out = new CipherOutputStream(out, enccipher) {
+                boolean closed;
+                public void close() throws IOException {
+                    if (!closed) {
+                        super.close();
+                        closed = true;
+                    }
+                }
+            };
+        }
+        return out;
+    }
+
+    private InputStream createInputStream(File file) throws IOException {
+        InputStream in = new FileInputStream(file);
+        if (cipherTransformation != null) {
+            in = new CipherInputStream(in, deccipher) {
+                boolean closed;
+                public void close() throws IOException {
+                    if (!closed) {
+                        super.close();
+                        closed = true;
+                    }
+                }
+            };
+        }
+        return in;
+    }
+
 }

Modified: cxf/trunk/api/src/test/java/org/apache/cxf/io/CachedOutputStreamTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/api/src/test/java/org/apache/cxf/io/CachedOutputStreamTest.java?rev=1401223&r1=1401222&r2=1401223&view=diff
==============================================================================
--- cxf/trunk/api/src/test/java/org/apache/cxf/io/CachedOutputStreamTest.java (original)
+++ cxf/trunk/api/src/test/java/org/apache/cxf/io/CachedOutputStreamTest.java Tue Oct 23 09:43:57 2012
@@ -20,7 +20,9 @@ package org.apache.cxf.io;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 
 import org.junit.Assert;
 import org.junit.Test;
@@ -53,6 +55,81 @@ public class CachedOutputStreamTest exte
         //assert tmp file is deleted after close the CachedOutputStream
         assertFalse(tempFile.exists());
     }
+
+    @Test
+    public void testEncryptAndDecryptWithDeleteOnClose() throws IOException {
+        CachedOutputStream cos = new CachedOutputStream();
+        cos.setThreshold(4);
+        // need a 8-bit cipher so that all bytes are flushed when the stream is flushed.
+        cos.setCipherTransformation("DES/CFB8/NoPadding");
+        
+        final String text = "Hello Secret World!";
+        cos.write(text.getBytes("UTF-8"));
+        cos.flush();
+        
+        File tmpfile = cos.getTempFile();
+        assertNotNull(tmpfile);
+        
+        final String enctext = readFromStream(new FileInputStream(tmpfile));
+        assertFalse("text is not encoded", text.equals(enctext));
+
+        InputStream fin = cos.getInputStream();
+
+        assertTrue("file is deleted", tmpfile.exists());
+        
+        final String dectext = readFromStream(fin);
+        assertEquals("text is not decoded correctly", text, dectext);
+
+        // the file is deleted when cos is closed while all the associated inputs are closed
+        assertTrue("file is deleted", tmpfile.exists());
+        cos.close();
+        assertFalse("file is not deleted", tmpfile.exists());
+    }
+
+    @Test
+    public void testEncryptAndDecryptWithDeleteOnInClose() throws IOException {
+        CachedOutputStream cos = new CachedOutputStream();
+        cos.setThreshold(4);
+        // need a 8-bit cipher so that all bytes are flushed when the stream is flushed.
+        cos.setCipherTransformation("DES/CFB8/NoPadding");
+        
+        final String text = "Hello Secret World!";
+        cos.write(text.getBytes("UTF-8"));
+        cos.flush();
+        
+        File tmpfile = cos.getTempFile();
+        assertNotNull(tmpfile);
+        
+        final String enctext = readFromStream(new FileInputStream(tmpfile));
+        assertFalse("text is not encoded", text.equals(enctext));
+
+        InputStream fin = cos.getInputStream();
+
+        cos.close();
+        assertTrue("file is deleted", tmpfile.exists());
+        
+        // the file is deleted when cos is closed while all the associated inputs are closed
+        final String dectext = readFromStream(fin);
+        assertEquals("text is not decoded correctly", text, dectext);
+        assertFalse("file is not deleted", tmpfile.exists());
+    }
+
+    private static String readFromStream(InputStream is) throws IOException {
+        ByteArrayOutputStream buf = new ByteArrayOutputStream();
+        try {
+            byte[] b = new byte[100];
+            for (;;) {
+                int n = is.read(b, 0, b.length);
+                if (n < 0) {
+                    break;
+                }
+                buf.write(b, 0, n);
+            }
+        } finally {
+            is.close();
+        }
+        return new String(buf.toByteArray(), "UTF-8");
+    }
     
     String initTestData(int packetSize) {
         String temp = "abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+?><[]/0123456789";