| /* |
| * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. |
| * |
| * The Apache Software License, Version 1.1 |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, if |
| * any, must include the following acknowlegement: |
| * "This product includes software developed by the |
| * Caucho Technology (http://www.caucho.com/)." |
| * Alternately, this acknowlegement may appear in the software itself, |
| * if and wherever such third-party acknowlegements normally appear. |
| * |
| * 4. The names "Hessian", "Resin", and "Caucho" must not be used to |
| * endorse or promote products derived from this software without prior |
| * written permission. For written permission, please contact |
| * info@caucho.com. |
| * |
| * 5. Products derived from this software may not be called "Resin" |
| * nor may "Resin" appear in their names without prior written |
| * permission of Caucho Technology. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
| * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT |
| * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * @author Scott Ferguson |
| */ |
| |
| package com.alibaba.com.caucho.hessian3.security; |
| |
| import com.alibaba.com.caucho.hessian3.io.Hessian2Input; |
| import com.alibaba.com.caucho.hessian3.io.Hessian2Output; |
| import com.alibaba.com.caucho.hessian3.io.HessianEnvelope; |
| |
| import javax.crypto.Cipher; |
| import javax.crypto.CipherInputStream; |
| import javax.crypto.KeyGenerator; |
| import javax.crypto.Mac; |
| import javax.crypto.SecretKey; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.security.Key; |
| import java.security.MessageDigest; |
| import java.security.PrivateKey; |
| import java.security.PublicKey; |
| import java.security.SecureRandom; |
| import java.security.cert.X509Certificate; |
| |
| public class X509Signature extends HessianEnvelope { |
| private String _algorithm = "HmacSHA256"; |
| private X509Certificate _cert; |
| private PrivateKey _privateKey; |
| private SecureRandom _secureRandom; |
| |
| public X509Signature() { |
| } |
| |
| /** |
| * Gets the encryption algorithm for the content. |
| */ |
| public String getAlgorithm() { |
| return _algorithm; |
| } |
| |
| /** |
| * Sets the encryption algorithm for the content. |
| */ |
| public void setAlgorithm(String algorithm) { |
| if (algorithm == null) |
| throw new NullPointerException(); |
| |
| _algorithm = algorithm; |
| } |
| |
| /** |
| * The X509 certificate to obtain the public key of the recipient. |
| */ |
| public X509Certificate getCertificate() { |
| return _cert; |
| } |
| |
| /** |
| * The X509 certificate to obtain the public key of the recipient. |
| */ |
| public void setCertificate(X509Certificate cert) { |
| _cert = cert; |
| } |
| |
| /** |
| * The key to obtain the private key of the recipient. |
| */ |
| public PrivateKey getPrivateKey() { |
| return _privateKey; |
| } |
| |
| /** |
| * The private key. |
| */ |
| public void setPrivateKey(PrivateKey key) { |
| _privateKey = key; |
| } |
| |
| /** |
| * The random number generator for the shared secrets. |
| */ |
| public SecureRandom getSecureRandom() { |
| return _secureRandom; |
| } |
| |
| /** |
| * The random number generator for the shared secrets. |
| */ |
| public void setSecureRandom(SecureRandom random) { |
| _secureRandom = random; |
| } |
| |
| @Override |
| public Hessian2Output wrap(Hessian2Output out) |
| throws IOException { |
| if (_privateKey == null) |
| throw new IOException("X509Signature.wrap requires a private key"); |
| |
| if (_cert == null) |
| throw new IOException("X509Signature.wrap requires a certificate"); |
| |
| OutputStream os = new SignatureOutputStream(out); |
| |
| Hessian2Output filterOut = new Hessian2Output(os); |
| |
| filterOut.setCloseStreamOnClose(true); |
| |
| return filterOut; |
| } |
| |
| @Override |
| public Hessian2Input unwrap(Hessian2Input in) |
| throws IOException { |
| if (_cert == null) |
| throw new IOException("X509Signature.unwrap requires a certificate"); |
| |
| int version = in.readEnvelope(); |
| |
| String method = in.readMethod(); |
| |
| if (!method.equals(getClass().getName())) |
| throw new IOException("expected hessian Envelope method '" + |
| getClass().getName() + "' at '" + method + "'"); |
| |
| return unwrapHeaders(in); |
| } |
| |
| @Override |
| public Hessian2Input unwrapHeaders(Hessian2Input in) |
| throws IOException { |
| if (_cert == null) |
| throw new IOException("X509Signature.unwrap requires a certificate"); |
| |
| InputStream is = new SignatureInputStream(in); |
| |
| Hessian2Input filter = new Hessian2Input(is); |
| |
| filter.setCloseStreamOnClose(true); |
| |
| return filter; |
| } |
| |
| class SignatureOutputStream extends OutputStream { |
| private Hessian2Output _out; |
| private OutputStream _bodyOut; |
| private Mac _mac; |
| |
| SignatureOutputStream(Hessian2Output out) |
| throws IOException { |
| try { |
| KeyGenerator keyGen = KeyGenerator.getInstance(_algorithm); |
| |
| if (_secureRandom != null) |
| keyGen.init(_secureRandom); |
| |
| SecretKey sharedKey = keyGen.generateKey(); |
| |
| _out = out; |
| |
| _out.startEnvelope(X509Signature.class.getName()); |
| |
| PublicKey publicKey = _cert.getPublicKey(); |
| |
| byte[] encoded = publicKey.getEncoded(); |
| MessageDigest md = MessageDigest.getInstance("SHA1"); |
| md.update(encoded); |
| byte[] fingerprint = md.digest(); |
| |
| String keyAlgorithm = _privateKey.getAlgorithm(); |
| Cipher keyCipher = Cipher.getInstance(keyAlgorithm); |
| keyCipher.init(Cipher.WRAP_MODE, _privateKey); |
| |
| byte[] encKey = keyCipher.wrap(sharedKey); |
| |
| _out.writeInt(4); |
| _out.writeString("algorithm"); |
| _out.writeString(_algorithm); |
| _out.writeString("fingerprint"); |
| _out.writeBytes(fingerprint); |
| _out.writeString("key-algorithm"); |
| _out.writeString(keyAlgorithm); |
| _out.writeString("key"); |
| _out.writeBytes(encKey); |
| |
| _mac = Mac.getInstance(_algorithm); |
| _mac.init(sharedKey); |
| |
| _bodyOut = _out.getBytesOutputStream(); |
| } catch (RuntimeException e) { |
| throw e; |
| } catch (IOException e) { |
| throw e; |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| @Override |
| public void write(int ch) |
| throws IOException { |
| _bodyOut.write(ch); |
| _mac.update((byte) ch); |
| } |
| |
| @Override |
| public void write(byte[] buffer, int offset, int length) |
| throws IOException { |
| _bodyOut.write(buffer, offset, length); |
| _mac.update(buffer, offset, length); |
| } |
| |
| @Override |
| public void close() |
| throws IOException { |
| Hessian2Output out = _out; |
| _out = null; |
| |
| if (out == null) |
| return; |
| |
| _bodyOut.close(); |
| |
| byte[] sig = _mac.doFinal(); |
| |
| out.writeInt(1); |
| out.writeString("signature"); |
| out.writeBytes(sig); |
| |
| out.completeEnvelope(); |
| out.close(); |
| } |
| } |
| |
| class SignatureInputStream extends InputStream { |
| private Hessian2Input _in; |
| |
| private Mac _mac; |
| private InputStream _bodyIn; |
| private CipherInputStream _cipherIn; |
| |
| SignatureInputStream(Hessian2Input in) |
| throws IOException { |
| try { |
| _in = in; |
| |
| byte[] fingerprint = null; |
| String keyAlgorithm = null; |
| String algorithm = null; |
| byte[] encKey = null; |
| |
| int len = in.readInt(); |
| |
| for (int i = 0; i < len; i++) { |
| String header = in.readString(); |
| |
| if ("fingerprint".equals(header)) |
| fingerprint = in.readBytes(); |
| else if ("key-algorithm".equals(header)) |
| keyAlgorithm = in.readString(); |
| else if ("algorithm".equals(header)) |
| algorithm = in.readString(); |
| else if ("key".equals(header)) |
| encKey = in.readBytes(); |
| else |
| throw new IOException("'" + header + "' is an unexpected header"); |
| } |
| |
| Cipher keyCipher = Cipher.getInstance(keyAlgorithm); |
| keyCipher.init(Cipher.UNWRAP_MODE, _cert); |
| |
| Key key = keyCipher.unwrap(encKey, algorithm, Cipher.SECRET_KEY); |
| _bodyIn = _in.readInputStream(); |
| |
| _mac = Mac.getInstance(algorithm); |
| _mac.init(key); |
| } catch (RuntimeException e) { |
| throw e; |
| } catch (IOException e) { |
| throw e; |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| @Override |
| public int read() |
| throws IOException { |
| int ch = _bodyIn.read(); |
| |
| if (ch < 0) |
| return ch; |
| |
| _mac.update((byte) ch); |
| |
| return ch; |
| } |
| |
| @Override |
| public int read(byte[] buffer, int offset, int length) |
| throws IOException { |
| int len = _bodyIn.read(buffer, offset, length); |
| |
| if (len < 0) |
| return len; |
| |
| _mac.update(buffer, offset, len); |
| |
| return len; |
| } |
| |
| @Override |
| public void close() |
| throws IOException { |
| Hessian2Input in = _in; |
| _in = null; |
| |
| if (in != null) { |
| _bodyIn.close(); |
| |
| int len = in.readInt(); |
| byte[] signature = null; |
| |
| for (int i = 0; i < len; i++) { |
| String header = in.readString(); |
| |
| if ("signature".equals(header)) |
| signature = in.readBytes(); |
| } |
| |
| in.completeEnvelope(); |
| in.close(); |
| |
| |
| if (signature == null) |
| throw new IOException("Expected signature"); |
| |
| byte[] sig = _mac.doFinal(); |
| |
| if (sig.length != signature.length) |
| throw new IOException("mismatched signature"); |
| |
| for (int i = 0; i < sig.length; i++) { |
| if (signature[i] != sig[i]) |
| throw new IOException("mismatched signature"); |
| } |
| |
| // XXX: save principal |
| } |
| } |
| } |
| } |