001 /*
002 * CPXInputStream.java
003 *
004 *
005 * The Salamander Project - 2D and 3D graphics libraries in Java
006 * Copyright (C) 2004 Mark McKay
007 *
008 * This library is free software; you can redistribute it and/or
009 * modify it under the terms of the GNU Lesser General Public
010 * License as published by the Free Software Foundation; either
011 * version 2.1 of the License, or (at your option) any later version.
012 *
013 * This library is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016 * Lesser General Public License for more details.
017 *
018 * You should have received a copy of the GNU Lesser General Public
019 * License along with this library; if not, write to the Free Software
020 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021 *
022 * Mark McKay can be contacted at mark@kitfox.com. Salamander and other
023 * projects can be found at http://www.kitfox.com
024 *
025 * Created on February 12, 2004, 10:34 AM
026 */
027
028 package com.kitfox.svg.xml.cpx;
029
030 import java.io.*;
031 import java.util.*;
032 import java.util.zip.*;
033 import java.security.*;
034 import javax.crypto.*;
035
036 /**
037 * This class reads/decodes the CPX file format. This format is a simple
038 * compression/encryption transformer for XML data. This stream takes in
039 * encrypted XML and outputs decrypted. It does this by checking for a magic
040 * number at the start of the stream. If absent, it treats the stream as
041 * raw XML data and passes it through unaltered. This is to aid development
042 * in debugging versions, where the XML files will not be in CPX format.
043 *
044 * See http://java.sun.com/developer/technicalArticles/Security/Crypto/
045 *
046 * @author Mark McKay
047 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
048 */
049 public class CPXInputStream extends FilterInputStream implements CPXConsts {
050
051
052 SecureRandom sec = new SecureRandom();
053
054 Inflater inflater = new Inflater();
055
056 int xlateMode;
057
058 //Keep header bytes in case this stream turns out to be plain text
059 byte[] head = new byte[4];
060 int headSize = 0;
061 int headPtr = 0;
062
063 boolean reachedEOF = false;
064 byte[] inBuffer = new byte[2048];
065 byte[] decryptBuffer = new byte[2048];
066
067 /** Creates a new instance of CPXInputStream */
068 public CPXInputStream(InputStream in) throws IOException {
069 super(in);
070
071 //Determine processing type
072 for (int i = 0; i < 4; i++)
073 {
074 int val = in.read();
075 head[i] = (byte)val;
076 if (val == -1 || head[i] != MAGIC_NUMBER[i])
077 {
078 headSize = i + 1;
079 xlateMode = XL_PLAIN;
080 return;
081 }
082 }
083
084 xlateMode = XL_ZIP_CRYPT;
085 }
086
087 /**
088 * We do not allow marking
089 */
090 public boolean markSupported() { return false; }
091
092 /**
093 * Closes this input stream and releases any system resources
094 * associated with the stream.
095 * This
096 * method simply performs <code>in.close()</code>.
097 *
098 * @exception IOException if an I/O error occurs.
099 * @see java.io.FilterInputStream#in
100 */
101 public void close() throws IOException {
102 reachedEOF = true;
103 in.close();
104 }
105
106 /**
107 * Reads the next byte of data from this input stream. The value
108 * byte is returned as an <code>int</code> in the range
109 * <code>0</code> to <code>255</code>. If no byte is available
110 * because the end of the stream has been reached, the value
111 * <code>-1</code> is returned. This method blocks until input data
112 * is available, the end of the stream is detected, or an exception
113 * is thrown.
114 * <p>
115 * This method
116 * simply performs <code>in.read()</code> and returns the result.
117 *
118 * @return the next byte of data, or <code>-1</code> if the end of the
119 * stream is reached.
120 * @exception IOException if an I/O error occurs.
121 * @see java.io.FilterInputStream#in
122 */
123 public int read() throws IOException
124 {
125 final byte[] b = new byte[1];
126 int retVal = read(b, 0, 1);
127 if (retVal == -1) return -1;
128 return b[0];
129 }
130
131 /**
132 * Reads up to <code>byte.length</code> bytes of data from this
133 * input stream into an array of bytes. This method blocks until some
134 * input is available.
135 * <p>
136 * This method simply performs the call
137 * <code>read(b, 0, b.length)</code> and returns
138 * the result. It is important that it does
139 * <i>not</i> do <code>in.read(b)</code> instead;
140 * certain subclasses of <code>FilterInputStream</code>
141 * depend on the implementation strategy actually
142 * used.
143 *
144 * @param b the buffer into which the data is read.
145 * @return the total number of bytes read into the buffer, or
146 * <code>-1</code> if there is no more data because the end of
147 * the stream has been reached.
148 * @exception IOException if an I/O error occurs.
149 * @see java.io.FilterInputStream#read(byte[], int, int)
150 */
151 public int read(byte[] b) throws IOException
152 {
153 return read(b, 0, b.length);
154 }
155
156 /**
157 * Reads up to <code>len</code> bytes of data from this input stream
158 * into an array of bytes. This method blocks until some input is
159 * available.
160 * <p>
161 * This method simply performs <code>in.read(b, off, len)</code>
162 * and returns the result.
163 *
164 * @param b the buffer into which the data is read.
165 * @param off the start offset of the data.
166 * @param len the maximum number of bytes read.
167 * @return the total number of bytes read into the buffer, or
168 * <code>-1</code> if there is no more data because the end of
169 * the stream has been reached.
170 * @exception IOException if an I/O error occurs.
171 * @see java.io.FilterInputStream#in
172 */
173 public int read(byte[] b, int off, int len) throws IOException
174 {
175 if (reachedEOF) return -1;
176
177 if (xlateMode == XL_PLAIN)
178 {
179 int count = 0;
180 //Write header if appropriate
181 while (headPtr < headSize && len > 0)
182 {
183 b[off++] = head[headPtr++];
184 count++;
185 len--;
186 }
187
188 return (len == 0) ? count : count + in.read(b, off, len);
189 }
190
191 //Decrypt and inflate
192 if (inflater.needsInput() && !decryptChunk())
193 {
194 reachedEOF = true;
195
196 //Read remaining bytes
197 int numRead;
198 try {
199 numRead = inflater.inflate(b, off, len);
200 }
201 catch (Exception e)
202 {
203 e.printStackTrace();
204 return -1;
205 }
206
207 if (!inflater.finished())
208 {
209 new Exception("Inflation incomplete").printStackTrace();
210 }
211
212 return numRead == 0 ? -1 : numRead;
213 }
214
215 try {
216 return inflater.inflate(b, off, len);
217 }
218 catch (DataFormatException e)
219 {
220 e.printStackTrace();
221 return -1;
222 }
223 }
224
225
226 /**
227 * Call when inflater indicates that it needs more bytes.
228 * @return - true if we decrypted more bytes to deflate, false if we
229 * encountered the end of stream
230 */
231 protected boolean decryptChunk() throws IOException
232 {
233 while (inflater.needsInput())
234 {
235 int numInBytes = in.read(inBuffer);
236 if (numInBytes == -1) return false;
237 // int numDecryptBytes = cipher.update(inBuffer, 0, numInBytes, decryptBuffer);
238 // inflater.setInput(decryptBuffer, 0, numDecryptBytes);
239 inflater.setInput(inBuffer, 0, numInBytes);
240 }
241
242 return true;
243 }
244
245 /**
246 * This method returns 1 if we've not reached EOF, 0 if we have. Programs
247 * should not rely on this to determine the number of bytes that can be
248 * read without blocking.
249 */
250 public int available() { return reachedEOF ? 0 : 1; }
251
252 /**
253 * Skips bytes by reading them into a cached buffer
254 */
255 public long skip(long n) throws IOException
256 {
257 int skipSize = (int)n;
258 if (skipSize > inBuffer.length) skipSize = inBuffer.length;
259 return read(inBuffer, 0, skipSize);
260 }
261
262 }
263
264 /*
265 import java.security.KeyPairGenerator;
266 import java.security.KeyPair;
267 import java.security.KeyPairGenerator;
268 import java.security.PrivateKey;
269 import java.security.PublicKey;
270 import java.security.SecureRandom;
271 import java.security.Cipher;
272
273 ....
274
275 java.security.Security.addProvider(new cryptix.provider.Cryptix());
276
277 SecureRandom random = new SecureRandom(SecureRandom.getSeed(30));
278 KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
279 keygen.initialize(1024, random);
280 keypair = keygen.generateKeyPair();
281
282 PublicKey pubkey = keypair.getPublic();
283 PrivateKey privkey = keypair.getPrivate();
284 */
285
286 /*
287 *
288 *Generate key pairs
289 KeyPairGenerator keyGen =
290 KeyPairGenerator.getInstance("DSA");
291 KeyGen.initialize(1024, new SecureRandom(userSeed));
292 KeyPair pair = KeyGen.generateKeyPair();
293 */