1 /* |
|
2 * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * questions. |
|
24 */ |
|
25 package sun.jkernel; |
|
26 |
|
27 import java.io.FilterOutputStream; |
|
28 import java.io.OutputStream; |
|
29 import java.io.IOException; |
|
30 import java.security.NoSuchAlgorithmException; |
|
31 import sun.jkernel.StandaloneMessageDigest; |
|
32 |
|
33 |
|
34 /* |
|
35 * This class provides the main functionality of <code>FilterOutputStream</code>, |
|
36 * and accumulates a check value as bytes are written to |
|
37 * it. The check value is available by method <code>getCheckValue</code>. |
|
38 *<p> |
|
39 * Operations on the public <code>out</code> field of this class should be |
|
40 * avoided to prevent an invalid check code being generated. |
|
41 * |
|
42 * TODO: The javadoc HTML hasn't been generated and eyeballed for yet. |
|
43 * TODO: There is a javadoc trick to cause the parent class javadoc to be |
|
44 * automagically used: try to take advantage of this. |
|
45 * TODO: Add javadoc links instead of <code>API</code> where it would be useful. |
|
46 * TODO: Go visit the Docs style guide again and get the periods right and |
|
47 * consistent for all sun.* classes. |
|
48 * @author Pete Soper |
|
49 * @see java.lang.FilterOutputStream |
|
50 * @see getCheckValue |
|
51 */ |
|
52 |
|
53 public class DigestOutputStream extends FilterOutputStream { |
|
54 private static final String DEFAULT_ALGORITHM = "SHA-1"; |
|
55 |
|
56 private final boolean debug = false; |
|
57 |
|
58 private StandaloneMessageDigest smd = null; |
|
59 |
|
60 private void initDigest(String algorithm) throws NoSuchAlgorithmException { |
|
61 smd = StandaloneMessageDigest.getInstance(algorithm); |
|
62 } |
|
63 |
|
64 // The underlying stream. |
|
65 |
|
66 protected volatile OutputStream out; |
|
67 |
|
68 /** |
|
69 * Creates a <code>DigestOutputStream</code> with stream <code>s</code> |
|
70 * to be checked with using <code>algorithm</code>. |
|
71 * <p> |
|
72 * If <code>algorithm</code> is not supported then |
|
73 * <code>NoSuchAlgorithm</code> is thrown. |
|
74 * <p> |
|
75 * See {linkplain sun.security.provider.StandaloneMessageDigest} for an |
|
76 * implementation-specific list of supported algorithms. |
|
77 * |
|
78 * @throws NoSuchAlgorithm if <code>algorithm</code> is not supported |
|
79 * @see sun.security.provider.StandaloneMessageDigest |
|
80 */ |
|
81 |
|
82 /** |
|
83 * Creates an output stream filter built on top of |
|
84 * underlying output stream <code>out</code> for checking with |
|
85 * algorithm <code>algorithm</code>. |
|
86 * <p> |
|
87 * If <code>algorithm</code> is not supported then |
|
88 * <code>NoSuchAlgorithm</code> is thrown. |
|
89 * <p> |
|
90 * See {linkplain sun.security.provider.StandaloneMessageDigest} for an |
|
91 * implementation-specific list of supported algorithms. |
|
92 * |
|
93 * @param out the underlying output stream to be assigned to |
|
94 * the field <tt>this.out</tt> for later use, or |
|
95 * <code>null</code> if this instance is to be |
|
96 * created without an underlying stream. |
|
97 * @param algorithm the check algorithm to use. |
|
98 * @throws NoSuchAlgorithm if <code>algorithm</code> is not supported |
|
99 * @see sun.security.provider.StandaloneMessageDigest |
|
100 * @see DigestInputStream(InputStream, String) |
|
101 */ |
|
102 |
|
103 public DigestOutputStream(OutputStream out, String algorithm) throws NoSuchAlgorithmException { |
|
104 super(out); |
|
105 initDigest(algorithm); |
|
106 this.out = out; |
|
107 } |
|
108 |
|
109 /** |
|
110 * Creates an output stream filter built on top of |
|
111 * underlying output stream <code>out</code> for the default checking |
|
112 * algorithm. |
|
113 * <p> |
|
114 * This implemention provides "SHA-1" as the default checking algorithm. |
|
115 * |
|
116 * @param out the underlying output stream to be assigned to |
|
117 * the field <tt>this.out</tt> for later use, or |
|
118 * <code>null</code> if this instance is to be |
|
119 * created without an underlying stream. |
|
120 * @see DigestInputStream(InputStream) |
|
121 */ |
|
122 |
|
123 public DigestOutputStream(OutputStream out) { |
|
124 super(out); |
|
125 try { |
|
126 initDigest(DEFAULT_ALGORITHM); |
|
127 } catch (NoSuchAlgorithmException e) { |
|
128 // Impossible to get here, but stranger things have happened... |
|
129 throw new RuntimeException("DigestOutputStream() unknown algorithm"); |
|
130 } |
|
131 // superstition from a test failure this.out = out; |
|
132 } |
|
133 |
|
134 /** |
|
135 * Writes a byte specified by <code>v</code> to this stream |
|
136 * and updates the check information. |
|
137 * |
|
138 * |
|
139 * @param v the byte to be written. |
|
140 * @throws IOException if an I/O error occurs. |
|
141 */ |
|
142 public void write(int v) throws IOException { |
|
143 super.write(v); |
|
144 // TODO Could create this array once |
|
145 byte[] b = new byte[] {(byte) (v & 0xff)}; |
|
146 smd.update(b,0,1); |
|
147 } |
|
148 |
|
149 /** |
|
150 * Writes the bytes in array <code>data</code> |
|
151 * to this stream and updates the check information. |
|
152 * |
|
153 * @param data the data. |
|
154 * @throws IOException if an I/O error occurs. |
|
155 * @throws NullPointerException if <code>data</code> is <code>null</code> |
|
156 */ |
|
157 public void write(byte[] data) throws IOException { |
|
158 write(data,0,data.length); |
|
159 } |
|
160 |
|
161 /** |
|
162 * Writes a sub array as a sequence of bytes to this output stream and |
|
163 * updates the check information. |
|
164 * @param data the data to be written |
|
165 * @param ofs the start offset in the data |
|
166 * @param len the number of bytes that are written |
|
167 * @throws IOException If an I/O error has occurred. |
|
168 * @throws NullPointerException if <code>data</code> is <code>null</code> |
|
169 * @throws IndexOutOfBoundsException If <code>ofs</code> is negative, |
|
170 * <code>len</code> is negative, or <code>len</code> is greater than |
|
171 * <code>b.length - ofs</code> |
|
172 */ |
|
173 public void write(byte[] data, int ofs, int len) throws IOException { |
|
174 if (debug) { |
|
175 System.out.print("DigestOutputStream.write: "); |
|
176 for (int i=ofs; i<(len - ofs); i++) { |
|
177 System.out.format("%02X",data[i]); |
|
178 } |
|
179 System.out.println(); |
|
180 } |
|
181 if (data == null) { |
|
182 throw new NullPointerException("null array in DigestOutputStream.write"); |
|
183 } else if (ofs < 0 || len < 0 || len > data.length - ofs) { |
|
184 throw new IndexOutOfBoundsException(); |
|
185 } |
|
186 //super.write(data,ofs,len); |
|
187 // WATCH OUT: FilterOutputStream does a byte at a time write(byte) |
|
188 // TODO: Will this work all the time, or is there another caveat |
|
189 // to publish |
|
190 out.write(data,ofs,len); |
|
191 if (debug) { |
|
192 System.out.println("DigestOutputStream.write before"); |
|
193 } |
|
194 smd.update(data,ofs,len); |
|
195 if (debug) { |
|
196 System.out.println("DigestOutputStream.write after"); |
|
197 } |
|
198 } |
|
199 |
|
200 /** |
|
201 * Closes this file output stream and releases any system resources |
|
202 * associated with this stream and makes the check value for the stream |
|
203 * available via <code>getCheckValue</code>. This file output stream may |
|
204 * no longer be used for writing bytes. |
|
205 * |
|
206 * @throws IOException if an I/O error occurs. |
|
207 * @see getCheckValue |
|
208 */ |
|
209 public void close() throws IOException { |
|
210 super.close(); |
|
211 } |
|
212 |
|
213 /** |
|
214 * Return the check value computed for the stream and reset the state of |
|
215 * check value generation. |
|
216 * |
|
217 * @return the check value bytes |
|
218 */ |
|
219 public byte[] getCheckValue() { |
|
220 byte[] b = smd.digest(); |
|
221 if (debug) { |
|
222 System.out.print("DigestOutputStream.getCheckValue: "); |
|
223 for (int i=0; i<b.length; i++) { |
|
224 System.out.format("%02X",b[i]); |
|
225 } |
|
226 System.out.println(); |
|
227 } |
|
228 smd.reset(); |
|
229 return b; |
|
230 } |
|
231 |
|
232 /** |
|
233 * Flushes this output stream. |
|
234 * |
|
235 * @throws IOException if an I/O error occurs. |
|
236 * @see java.io.FilterOutputStream#flush() |
|
237 */ |
|
238 public void flush() throws IOException { |
|
239 super.flush(); |
|
240 } |
|
241 |
|
242 /** |
|
243 * Compares two digests for equality. Does a simple byte compare. |
|
244 * |
|
245 * @param digesta one of the digests to compare. |
|
246 * |
|
247 * @param digestb the other digest to compare. |
|
248 * |
|
249 * @return true if the digests are equal, false otherwise. |
|
250 */ |
|
251 // public static boolean isEqual(byte digesta[], byte digestb[]) { |
|
252 // return StandaloneMessageDigest.isEqual(digesta, digestb); |
|
253 // } |
|
254 |
|
255 } |
|