001/* 002 * SVG Salamander 003 * Copyright (c) 2004, Mark McKay 004 * All rights reserved. 005 * 006 * Redistribution and use in source and binary forms, with or 007 * without modification, are permitted provided that the following 008 * conditions are met: 009 * 010 * - Redistributions of source code must retain the above 011 * copyright notice, this list of conditions and the following 012 * disclaimer. 013 * - Redistributions in binary form must reproduce the above 014 * copyright notice, this list of conditions and the following 015 * disclaimer in the documentation and/or other materials 016 * provided with the distribution. 017 * 018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 019 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 020 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 021 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 022 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 023 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 025 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 026 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 027 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 028 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 029 * OF THE POSSIBILITY OF SUCH DAMAGE. 030 * 031 * Mark McKay can be contacted at mark@kitfox.com. Salamander and other 032 * projects can be found at http://www.kitfox.com 033 * 034 * Created on February 12, 2004, 10:34 AM 035 */ 036 037package com.kitfox.svg.xml.cpx; 038 039import com.kitfox.svg.SVGConst; 040import java.io.*; 041import java.util.zip.*; 042import java.security.*; 043import java.util.logging.Level; 044import java.util.logging.Logger; 045 046/** 047 * This class reads/decodes the CPX file format. This format is a simple 048 * compression/encryption transformer for XML data. This stream takes in 049 * encrypted XML and outputs decrypted. It does this by checking for a magic 050 * number at the start of the stream. If absent, it treats the stream as 051 * raw XML data and passes it through unaltered. This is to aid development 052 * in debugging versions, where the XML files will not be in CPX format. 053 * 054 * See http://java.sun.com/developer/technicalArticles/Security/Crypto/ 055 * 056 * @author Mark McKay 057 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a> 058 */ 059public class CPXInputStream extends FilterInputStream implements CPXConsts { 060 061 062 SecureRandom sec = new SecureRandom(); 063 064 Inflater inflater = new Inflater(); 065 066 int xlateMode; 067 068 //Keep header bytes in case this stream turns out to be plain text 069 byte[] head = new byte[4]; 070 int headSize = 0; 071 int headPtr = 0; 072 073 boolean reachedEOF = false; 074 byte[] inBuffer = new byte[2048]; 075 byte[] decryptBuffer = new byte[2048]; 076 077 /** 078 * Creates a new instance of CPXInputStream 079 * @param in 080 * @throws java.io.IOException 081 */ 082 public CPXInputStream(InputStream in) throws IOException { 083 super(in); 084 085 //Determine processing type 086 for (int i = 0; i < 4; i++) 087 { 088 int val = in.read(); 089 head[i] = (byte)val; 090 if (val == -1 || head[i] != MAGIC_NUMBER[i]) 091 { 092 headSize = i + 1; 093 xlateMode = XL_PLAIN; 094 return; 095 } 096 } 097 098 xlateMode = XL_ZIP_CRYPT; 099 } 100 101 /** 102 * We do not allow marking 103 */ 104 @Override 105 public boolean markSupported() { return false; } 106 107 /** 108 * Closes this input stream and releases any system resources 109 * associated with the stream. 110 * This 111 * method simply performs <code>in.close()</code>. 112 * 113 * @exception IOException if an I/O error occurs. 114 * @see java.io.FilterInputStream#in 115 */ 116 @Override 117 public void close() throws IOException { 118 reachedEOF = true; 119 in.close(); 120 } 121 122 /** 123 * Reads the next byte of data from this input stream. The value 124 * byte is returned as an <code>int</code> in the range 125 * <code>0</code> to <code>255</code>. If no byte is available 126 * because the end of the stream has been reached, the value 127 * <code>-1</code> is returned. This method blocks until input data 128 * is available, the end of the stream is detected, or an exception 129 * is thrown. 130 * <p> 131 * This method 132 * simply performs <code>in.read()</code> and returns the result. 133 * 134 * @return the next byte of data, or <code>-1</code> if the end of the 135 * stream is reached. 136 * @exception IOException if an I/O error occurs. 137 * @see java.io.FilterInputStream#in 138 */ 139 @Override 140 public int read() throws IOException 141 { 142 final byte[] b = new byte[1]; 143 int retVal = read(b, 0, 1); 144 if (retVal == -1) return -1; 145 return b[0]; 146 } 147 148 /** 149 * Reads up to <code>byte.length</code> bytes of data from this 150 * input stream into an array of bytes. This method blocks until some 151 * input is available. 152 * <p> 153 * This method simply performs the call 154 * <code>read(b, 0, b.length)</code> and returns 155 * the result. It is important that it does 156 * <i>not</i> do <code>in.read(b)</code> instead; 157 * certain subclasses of <code>FilterInputStream</code> 158 * depend on the implementation strategy actually 159 * used. 160 * 161 * @param b the buffer into which the data is read. 162 * @return the total number of bytes read into the buffer, or 163 * <code>-1</code> if there is no more data because the end of 164 * the stream has been reached. 165 * @exception IOException if an I/O error occurs. 166 * @see java.io.FilterInputStream#read(byte[], int, int) 167 */ 168 @Override 169 public int read(byte[] b) throws IOException 170 { 171 return read(b, 0, b.length); 172 } 173 174 /** 175 * Reads up to <code>len</code> bytes of data from this input stream 176 * into an array of bytes. This method blocks until some input is 177 * available. 178 * <p> 179 * This method simply performs <code>in.read(b, off, len)</code> 180 * and returns the result. 181 * 182 * @param b the buffer into which the data is read. 183 * @param off the start offset of the data. 184 * @param len the maximum number of bytes read. 185 * @return the total number of bytes read into the buffer, or 186 * <code>-1</code> if there is no more data because the end of 187 * the stream has been reached. 188 * @exception IOException if an I/O error occurs. 189 * @see java.io.FilterInputStream#in 190 */ 191 @Override 192 public int read(byte[] b, int off, int len) throws IOException 193 { 194 if (reachedEOF) return -1; 195 196 if (xlateMode == XL_PLAIN) 197 { 198 int count = 0; 199 //Write header if appropriate 200 while (headPtr < headSize && len > 0) 201 { 202 b[off++] = head[headPtr++]; 203 count++; 204 len--; 205 } 206 207 return (len == 0) ? count : count + in.read(b, off, len); 208 } 209 210 //Decrypt and inflate 211 if (inflater.needsInput() && !decryptChunk()) 212 { 213 reachedEOF = true; 214 215 //Read remaining bytes 216 int numRead; 217 try { 218 numRead = inflater.inflate(b, off, len); 219 } 220 catch (Exception e) 221 { 222 Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e); 223 return -1; 224 } 225 226 if (!inflater.finished()) 227 { 228 Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, 229 "Inflation imncomplete"); 230 } 231 232 return numRead == 0 ? -1 : numRead; 233 } 234 235 try 236 { 237 return inflater.inflate(b, off, len); 238 } 239 catch (DataFormatException e) 240 { 241 Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e); 242 return -1; 243 } 244 } 245 246 247 /** 248 * Call when inflater indicates that it needs more bytes. 249 * @return - true if we decrypted more bytes to deflate, false if we 250 * encountered the end of stream 251 * @throws java.io.IOException 252 */ 253 protected boolean decryptChunk() throws IOException 254 { 255 while (inflater.needsInput()) 256 { 257 int numInBytes = in.read(inBuffer); 258 if (numInBytes == -1) return false; 259// int numDecryptBytes = cipher.update(inBuffer, 0, numInBytes, decryptBuffer); 260// inflater.setInput(decryptBuffer, 0, numDecryptBytes); 261inflater.setInput(inBuffer, 0, numInBytes); 262 } 263 264 return true; 265 } 266 267 /** 268 * This method returns 1 if we've not reached EOF, 0 if we have. Programs 269 * should not rely on this to determine the number of bytes that can be 270 * read without blocking. 271 */ 272 @Override 273 public int available() { return reachedEOF ? 0 : 1; } 274 275 /** 276 * Skips bytes by reading them into a cached buffer 277 */ 278 @Override 279 public long skip(long n) throws IOException 280 { 281 int skipSize = (int)n; 282 if (skipSize > inBuffer.length) skipSize = inBuffer.length; 283 return read(inBuffer, 0, skipSize); 284 } 285 286} 287 288/* 289 import java.security.KeyPairGenerator; 290 import java.security.KeyPair; 291 import java.security.KeyPairGenerator; 292 import java.security.PrivateKey; 293 import java.security.PublicKey; 294 import java.security.SecureRandom; 295 import java.security.Cipher; 296 297 .... 298 299 java.security.Security.addProvider(new cryptix.provider.Cryptix()); 300 301 SecureRandom random = new SecureRandom(SecureRandom.getSeed(30)); 302 KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA"); 303 keygen.initialize(1024, random); 304 keypair = keygen.generateKeyPair(); 305 306 PublicKey pubkey = keypair.getPublic(); 307 PrivateKey privkey = keypair.getPrivate(); 308 */ 309 310/* 311 * 312 *Generate key pairs 313KeyPairGenerator keyGen = 314 KeyPairGenerator.getInstance("DSA"); 315KeyGen.initialize(1024, new SecureRandom(userSeed)); 316KeyPair pair = KeyGen.generateKeyPair(); 317 */