001: /*
002: * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved.
003: *
004: * The Apache Software License, Version 1.1
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution, if
019: * any, must include the following acknowlegement:
020: * "This product includes software developed by the
021: * Caucho Technology (http://www.caucho.com/)."
022: * Alternately, this acknowlegement may appear in the software itself,
023: * if and wherever such third-party acknowlegements normally appear.
024: *
025: * 4. The names "Hessian", "Resin", and "Caucho" must not be used to
026: * endorse or promote products derived from this software without prior
027: * written permission. For written permission, please contact
028: * info@caucho.com.
029: *
030: * 5. Products derived from this software may not be called "Resin"
031: * nor may "Resin" appear in their names without prior written
032: * permission of Caucho Technology.
033: *
034: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
035: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
036: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
037: * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS
038: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
039: * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
040: * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
041: * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
042: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
043: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
044: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
045: *
046: * @author Scott Ferguson
047: */
048:
049: package com.caucho.hessian.security;
050:
051: import java.security.*;
052: import java.security.cert.*;
053: import java.util.*;
054: import javax.crypto.*;
055:
056: import java.io.*;
057:
058: import com.caucho.hessian.io.*;
059:
060: public class X509Signature extends HessianEnvelope {
061: private String _algorithm = "HmacSHA256";
062: private X509Certificate _cert;
063: private PrivateKey _privateKey;
064: private SecureRandom _secureRandom;
065:
066: public X509Signature() {
067: }
068:
069: /**
070: * Sets the encryption algorithm for the content.
071: */
072: public void setAlgorithm(String algorithm) {
073: if (algorithm == null)
074: throw new NullPointerException();
075:
076: _algorithm = algorithm;
077: }
078:
079: /**
080: * Gets the encryption algorithm for the content.
081: */
082: public String getAlgorithm() {
083: return _algorithm;
084: }
085:
086: /**
087: * The X509 certificate to obtain the public key of the recipient.
088: */
089: public X509Certificate getCertificate() {
090: return _cert;
091: }
092:
093: /**
094: * The X509 certificate to obtain the public key of the recipient.
095: */
096: public void setCertificate(X509Certificate cert) {
097: _cert = cert;
098: }
099:
100: /**
101: * The key to obtain the private key of the recipient.
102: */
103: public PrivateKey getPrivateKey() {
104: return _privateKey;
105: }
106:
107: /**
108: * The private key.
109: */
110: public void setPrivateKey(PrivateKey key) {
111: _privateKey = key;
112: }
113:
114: /**
115: * The random number generator for the shared secrets.
116: */
117: public SecureRandom getSecureRandom() {
118: return _secureRandom;
119: }
120:
121: /**
122: * The random number generator for the shared secrets.
123: */
124: public void setSecureRandom(SecureRandom random) {
125: _secureRandom = random;
126: }
127:
128: public Hessian2Output wrap(Hessian2Output out) throws IOException {
129: if (_privateKey == null)
130: throw new IOException(
131: "X509Signature.wrap requires a private key");
132:
133: if (_cert == null)
134: throw new IOException(
135: "X509Signature.wrap requires a certificate");
136:
137: OutputStream os = new SignatureOutputStream(out);
138:
139: Hessian2Output filterOut = new Hessian2Output(os);
140:
141: filterOut.setCloseStreamOnClose(true);
142:
143: return filterOut;
144: }
145:
146: public Hessian2Input unwrap(Hessian2Input in) throws IOException {
147: if (_cert == null)
148: throw new IOException(
149: "X509Signature.unwrap requires a certificate");
150:
151: int version = in.readEnvelope();
152:
153: String method = in.readMethod();
154:
155: if (!method.equals(getClass().getName()))
156: throw new IOException("expected hessian Envelope method '"
157: + getClass().getName() + "' at '" + method + "'");
158:
159: return unwrapHeaders(in);
160: }
161:
162: public Hessian2Input unwrapHeaders(Hessian2Input in)
163: throws IOException {
164: if (_cert == null)
165: throw new IOException(
166: "X509Signature.unwrap requires a certificate");
167:
168: InputStream is = new SignatureInputStream(in);
169:
170: Hessian2Input filter = new Hessian2Input(is);
171:
172: filter.setCloseStreamOnClose(true);
173:
174: return filter;
175: }
176:
177: class SignatureOutputStream extends OutputStream {
178: private Hessian2Output _out;
179: private OutputStream _bodyOut;
180: private Mac _mac;
181:
182: SignatureOutputStream(Hessian2Output out) throws IOException {
183: try {
184: KeyGenerator keyGen = KeyGenerator
185: .getInstance(_algorithm);
186:
187: if (_secureRandom != null)
188: keyGen.init(_secureRandom);
189:
190: SecretKey sharedKey = keyGen.generateKey();
191:
192: _out = out;
193:
194: _out.startEnvelope(X509Signature.class.getName());
195:
196: PublicKey publicKey = _cert.getPublicKey();
197:
198: byte[] encoded = publicKey.getEncoded();
199: MessageDigest md = MessageDigest.getInstance("SHA1");
200: md.update(encoded);
201: byte[] fingerprint = md.digest();
202:
203: String keyAlgorithm = _privateKey.getAlgorithm();
204: Cipher keyCipher = Cipher.getInstance(keyAlgorithm);
205: keyCipher.init(Cipher.WRAP_MODE, _privateKey);
206:
207: byte[] encKey = keyCipher.wrap(sharedKey);
208:
209: _out.writeInt(4);
210: _out.writeString("algorithm");
211: _out.writeString(_algorithm);
212: _out.writeString("fingerprint");
213: _out.writeBytes(fingerprint);
214: _out.writeString("key-algorithm");
215: _out.writeString(keyAlgorithm);
216: _out.writeString("key");
217: _out.writeBytes(encKey);
218:
219: _mac = Mac.getInstance(_algorithm);
220: _mac.init(sharedKey);
221:
222: _bodyOut = _out.getBytesOutputStream();
223: } catch (RuntimeException e) {
224: throw e;
225: } catch (IOException e) {
226: throw e;
227: } catch (Exception e) {
228: throw new RuntimeException(e);
229: }
230: }
231:
232: public void write(int ch) throws IOException {
233: _bodyOut.write(ch);
234: _mac.update((byte) ch);
235: }
236:
237: public void write(byte[] buffer, int offset, int length)
238: throws IOException {
239: _bodyOut.write(buffer, offset, length);
240: _mac.update(buffer, offset, length);
241: }
242:
243: public void close() throws IOException {
244: Hessian2Output out = _out;
245: _out = null;
246:
247: if (out == null)
248: return;
249:
250: _bodyOut.close();
251:
252: byte[] sig = _mac.doFinal();
253:
254: out.writeInt(1);
255: out.writeString("signature");
256: out.writeBytes(sig);
257:
258: out.completeEnvelope();
259: out.close();
260: }
261: }
262:
263: class SignatureInputStream extends InputStream {
264: private Hessian2Input _in;
265:
266: private Mac _mac;
267: private InputStream _bodyIn;
268: private CipherInputStream _cipherIn;
269:
270: SignatureInputStream(Hessian2Input in) throws IOException {
271: try {
272: _in = in;
273:
274: byte[] fingerprint = null;
275: String keyAlgorithm = null;
276: String algorithm = null;
277: byte[] encKey = null;
278:
279: int len = in.readInt();
280:
281: for (int i = 0; i < len; i++) {
282: String header = in.readString();
283:
284: if ("fingerprint".equals(header))
285: fingerprint = in.readBytes();
286: else if ("key-algorithm".equals(header))
287: keyAlgorithm = in.readString();
288: else if ("algorithm".equals(header))
289: algorithm = in.readString();
290: else if ("key".equals(header))
291: encKey = in.readBytes();
292: else
293: throw new IOException("'" + header
294: + "' is an unexpected header");
295: }
296:
297: Cipher keyCipher = Cipher.getInstance(keyAlgorithm);
298: keyCipher.init(Cipher.UNWRAP_MODE, _cert);
299:
300: Key key = keyCipher.unwrap(encKey, algorithm,
301: Cipher.SECRET_KEY);
302: _bodyIn = _in.readInputStream();
303:
304: _mac = Mac.getInstance(algorithm);
305: _mac.init(key);
306: } catch (RuntimeException e) {
307: throw e;
308: } catch (IOException e) {
309: throw e;
310: } catch (Exception e) {
311: throw new RuntimeException(e);
312: }
313: }
314:
315: public int read() throws IOException {
316: int ch = _bodyIn.read();
317:
318: if (ch < 0)
319: return ch;
320:
321: _mac.update((byte) ch);
322:
323: return ch;
324: }
325:
326: public int read(byte[] buffer, int offset, int length)
327: throws IOException {
328: int len = _bodyIn.read(buffer, offset, length);
329:
330: if (len < 0)
331: return len;
332:
333: _mac.update(buffer, offset, len);
334:
335: return len;
336: }
337:
338: public void close() throws IOException {
339: Hessian2Input in = _in;
340: _in = null;
341:
342: if (in != null) {
343: _bodyIn.close();
344:
345: int len = in.readInt();
346: byte[] signature = null;
347:
348: for (int i = 0; i < len; i++) {
349: String header = in.readString();
350:
351: if ("signature".equals(header))
352: signature = in.readBytes();
353: }
354:
355: in.completeEnvelope();
356: in.close();
357:
358: if (signature == null)
359: throw new IOException("Expected signature");
360:
361: byte[] sig = _mac.doFinal();
362:
363: if (sig.length != signature.length)
364: throw new IOException("mismatched signature");
365:
366: for (int i = 0; i < sig.length; i++) {
367: if (signature[i] != sig[i])
368: throw new IOException("mismatched signature");
369: }
370:
371: // XXX: save principal
372: }
373: }
374: }
375: }
|