1
2 package es.caib.signatura.impl;
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.UnsupportedEncodingException;
6
7 import es.caib.signatura.api.Signature;
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 public class SMIMEInputStream extends InputStream {
25
26 private static final int CHUNK_SIZE = 48;
27 String boundary;
28 Signature signature;
29 InputStream content;
30 String contentType;
31
32
33
34
35 private int status;
36 static private final int STATUS_IDLE = 0;
37 static private final int STATUS_HEADER = 1;
38 static private final int STATUS_CONTENT_HEADER = 2;
39 static private final int STATUS_CONTENT_BODY = 3;
40 static private final int STATUS_SIGNATURE_HEADER = 4;
41 static private final int STATUS_SIGNATURE_BODY = 5;
42 static private final int STATUS_FOOTER = 6;
43 static private final int STATUS_END = 7;
44
45
46 private boolean contentSizeMultiple=false;
47 private boolean signatureSizeMultiple=false;
48
49 int bufferOffset;
50 byte buffer [];
51
52 byte signatureData [];
53 int signatureDataOffset ;
54
55
56
57
58
59
60
61
62
63 public SMIMEInputStream(Signature signature, InputStream content) throws UnsupportedEncodingException{
64 this.signature = signature;
65 this.content = content;
66 this.contentType = signature.getContentType();
67 status = STATUS_IDLE;
68 bufferOffset = 0;
69 buffer = null;
70 boundary = "----CAIB_BOUNDARY_"+System.currentTimeMillis()+"_DGTIC";
71 signatureData = signature.getPkcs7();
72 signatureDataOffset = 0;
73 }
74
75
76
77
78
79
80
81 public SMIMEInputStream(byte[] pkcs7, InputStream content, String contentType) {
82 this.content = content;
83 this.contentType = contentType;
84 status = STATUS_IDLE;
85 bufferOffset = 0;
86 buffer = null;
87 boundary = "----CAIB_BOUNDARY_"+System.currentTimeMillis()+"_DGTIC";
88 signatureData = pkcs7;
89 signatureDataOffset = 0;
90
91 }
92
93
94
95
96
97
98 public synchronized void close ()
99 throws IOException {
100 status = STATUS_END;
101 content.close ();
102 }
103
104
105
106
107 public synchronized int read ()
108 throws IOException {
109 do {
110 if ( buffer != null && bufferOffset < buffer.length)
111 {
112 byte b = buffer [ bufferOffset ++ ];
113 return b;
114 } else if (status == STATUS_END) {
115 return -1;
116 } else {
117 fetchData();
118 }
119
120 } while (true);
121 }
122
123
124
125
126 public synchronized int read (byte[] targetBuffer, int offset, int length)
127 throws IOException {
128 do {
129 if ( buffer != null && bufferOffset < buffer.length)
130 {
131 int actualLength = buffer.length - bufferOffset;
132 if ( actualLength > length)
133 actualLength = length;
134 System.arraycopy(buffer, bufferOffset, targetBuffer, offset, actualLength);
135 bufferOffset = bufferOffset + actualLength;
136 return actualLength;
137 } else if (status == STATUS_END) {
138 return -1;
139 } else {
140 fetchData();
141 }
142
143 } while (true);
144 }
145
146
147
148
149 public synchronized int available()
150 throws IOException {
151 do {
152 if ( buffer != null && bufferOffset < buffer.length)
153 {
154 return buffer.length - bufferOffset;
155 } else if (status == STATUS_END) {
156 return -1;
157 } else if (status == STATUS_CONTENT_BODY) {
158 return content.available();
159 } else {
160 fetchData ();
161 }
162 } while (true);
163 }
164
165 private void fetchData() throws IOException {
166 switch (status)
167 {
168 case STATUS_IDLE:
169 buffer = fetchIdle ();
170 break;
171 case STATUS_HEADER:
172 buffer = fetchHeader ();
173 break;
174 case STATUS_CONTENT_HEADER:
175 buffer = fetchContentHeader ();
176 break;
177 case STATUS_CONTENT_BODY:
178 buffer = fetchContentBody ();
179 break;
180 case STATUS_SIGNATURE_HEADER:
181 buffer = fetchSignatureHeader ();
182 break;
183 case STATUS_SIGNATURE_BODY:
184 buffer = fetchSignatureBody ();
185 break;
186 case STATUS_FOOTER:
187 buffer = fetchFooter ();
188 break;
189 default:
190 buffer = null;
191 break;
192 }
193 bufferOffset = 0;
194 }
195
196
197
198
199 private byte [] fetchFooter() {
200 try {
201 status = STATUS_END;
202
203 String outString=(signatureSizeMultiple)?"":"\r\n";
204 outString+="--"+boundary+"--\r\n";
205
206 return outString.getBytes("ISO-8859-1");
207 } catch (UnsupportedEncodingException e) {
208 throw new RuntimeException (e);
209 }
210 }
211
212
213 private byte [] fetchContentBody() throws IOException {
214 byte b[] = new byte[CHUNK_SIZE];
215 int offset = 0;
216 int read;
217 contentSizeMultiple=false;
218
219 while (true)
220 {
221 read = content.read(b, offset, b.length-offset);
222 if ( read <= 0)
223 {
224
225 if((offset % CHUNK_SIZE)==0)
226 contentSizeMultiple=true;
227
228 status = STATUS_SIGNATURE_HEADER;
229 break;
230 }
231 offset = offset + read;
232 if ( offset == b.length)
233 break;
234 }
235 return Base64.toBase64(b, 0, offset);
236 }
237
238
239 private byte [] fetchContentHeader() {
240 try {
241 status = STATUS_CONTENT_BODY;
242 return ("--"+boundary+"\r\n"+
243 "Content-Type: "+contentType+"\r\n"+
244 "Content-Transfer-Encoding: base64\r\n" +
245 "\r\n").getBytes("ISO-8859-1");
246 } catch (UnsupportedEncodingException e) {
247 throw new RuntimeException (e);
248 }
249 }
250
251
252
253 private byte [] fetchSignatureBody() {
254 int len = signatureData.length - signatureDataOffset;
255 signatureSizeMultiple=false;
256
257 if ( len <= 0)
258 {
259
260 if((signatureDataOffset % CHUNK_SIZE)==0)
261 signatureSizeMultiple=true;
262
263 status = STATUS_FOOTER;
264 return null;
265 }
266 else if ( len > CHUNK_SIZE )
267 {
268 len = CHUNK_SIZE;
269 }
270
271 byte result []= Base64.toBase64(signatureData, signatureDataOffset, len);
272 signatureDataOffset = signatureDataOffset + len;
273 return result;
274 }
275
276
277 private byte [] fetchSignatureHeader() {
278 try {
279 status = STATUS_SIGNATURE_BODY;
280
281 String outString=(contentSizeMultiple)?"":"\r\n";
282 outString+="--"+boundary+"\r\n"+
283 "Content-Type: application/pkcs7-signature; name=smime.p7s; smime-type=signed-data\r\n"+
284 "Content-Transfer-Encoding: base64\r\n"+
285 "Content-Disposition: attachment; filename=\"smime.p7s\"\r\n"+
286 "Content-Description: S/MIME Cryptographic Signature\r\n"+
287 "\r\n";
288 return outString.getBytes("ISO-8859-1");
289 } catch (UnsupportedEncodingException e) {
290 throw new RuntimeException (e);
291 }
292 }
293
294
295 private byte [] fetchHeader() {
296 try {
297 status = STATUS_CONTENT_HEADER;
298 return ("Content-Type: multipart/signed; " +
299 "protocol=\"application/pkcs7-signature\"; " +
300 "micalg=\"sha1\";" +
301 "boundary=\""+boundary+"\"\r\n"+
302 "\r\n").getBytes("ISO-8859-1");
303 } catch (UnsupportedEncodingException e) {
304 throw new RuntimeException (e);
305 }
306 }
307
308
309 private byte [] fetchIdle() {
310 status = STATUS_HEADER;
311 return null;
312 }
313
314
315
316
317
318
319
320
321 public synchronized long skip(long n) throws IOException {
322 for (long i = 0; i < n; i++)
323 if (this.read() < 0) return i;
324 return n;
325 }
326
327
328
329
330 public void mark(int readlimit) {}
331
332
333
334
335 public void reset() throws IOException {
336 throw new IOException("Base64InputStream does not support mark/reset");
337 }
338
339
340
341
342 public boolean markSupported() { return false; }
343
344
345
346
347
348 }