blob: 2156cec2b91f585e1192292206fac6b11244f18e [file] [log] [blame]
/*
* 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.hessian.security;
import com.alibaba.com.caucho.hessian.io.Hessian2Input;
import com.alibaba.com.caucho.hessian.io.Hessian2Output;
import com.alibaba.com.caucho.hessian.io.HessianEnvelope;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
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 X509Encryption extends HessianEnvelope {
private String _algorithm = "AES";
// certificate for encryption/decryption
private X509Certificate _cert;
// private key for decryption
private PrivateKey _privateKey;
private SecureRandom _secureRandom;
public X509Encryption() {
}
/**
* 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 private key for decryption.
*/
public PrivateKey getPrivateKey() {
return _privateKey;
}
/**
* The X509 certificate to obtain the public key of the recipient.
*/
public void setPrivateKey(PrivateKey privateKey) {
_privateKey = privateKey;
}
/**
* 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 (_cert == null)
throw new IOException("X509Encryption.wrap requires a certificate");
OutputStream os = new EncryptOutputStream(out);
Hessian2Output filterOut = new Hessian2Output(os);
filterOut.setCloseStreamOnClose(true);
return filterOut;
}
@Override
public Hessian2Input unwrap(Hessian2Input in)
throws IOException {
if (_privateKey == null)
throw new IOException("X509Encryption.unwrap requires a private key");
if (_cert == null)
throw new IOException("X509Encryption.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 (_privateKey == null)
throw new IOException("X509Encryption.unwrap requires a private key");
if (_cert == null)
throw new IOException("X509Encryption.unwrap requires a certificate");
InputStream is = new EncryptInputStream(in);
Hessian2Input filter = new Hessian2Input(is);
filter.setCloseStreamOnClose(true);
return filter;
}
class EncryptOutputStream extends OutputStream {
private Hessian2Output _out;
private Cipher _cipher;
private OutputStream _bodyOut;
private CipherOutputStream _cipherOut;
EncryptOutputStream(Hessian2Output out)
throws IOException {
try {
_out = out;
KeyGenerator keyGen = KeyGenerator.getInstance(_algorithm);
if (_secureRandom != null)
keyGen.init(_secureRandom);
SecretKey sharedKey = keyGen.generateKey();
_out = out;
_out.startEnvelope(X509Encryption.class.getName());
PublicKey publicKey = _cert.getPublicKey();
byte[] encoded = publicKey.getEncoded();
MessageDigest md = MessageDigest.getInstance("SHA1");
md.update(encoded);
byte[] fingerprint = md.digest();
String keyAlgorithm = publicKey.getAlgorithm();
Cipher keyCipher = Cipher.getInstance(keyAlgorithm);
if (_secureRandom != null)
keyCipher.init(Cipher.WRAP_MODE, _cert, _secureRandom);
else
keyCipher.init(Cipher.WRAP_MODE, _cert);
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);
_bodyOut = _out.getBytesOutputStream();
_cipher = Cipher.getInstance(_algorithm);
if (_secureRandom != null)
_cipher.init(Cipher.ENCRYPT_MODE, sharedKey, _secureRandom);
else
_cipher.init(Cipher.ENCRYPT_MODE, sharedKey);
_cipherOut = new CipherOutputStream(_bodyOut, _cipher);
} 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 {
_cipherOut.write(ch);
}
@Override
public void write(byte[] buffer, int offset, int length)
throws IOException {
_cipherOut.write(buffer, offset, length);
}
@Override
public void close()
throws IOException {
Hessian2Output out = _out;
_out = null;
if (out != null) {
_cipherOut.close();
_bodyOut.close();
out.writeInt(0);
out.completeEnvelope();
out.close();
}
}
}
class EncryptInputStream extends InputStream {
private Hessian2Input _in;
private Cipher _cipher;
private InputStream _bodyIn;
private CipherInputStream _cipherIn;
EncryptInputStream(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, _privateKey);
Key key = keyCipher.unwrap(encKey, algorithm, Cipher.SECRET_KEY);
_bodyIn = _in.readInputStream();
_cipher = Cipher.getInstance(algorithm);
_cipher.init(Cipher.DECRYPT_MODE, key);
_cipherIn = new CipherInputStream(_bodyIn, _cipher);
} catch (RuntimeException e) {
throw e;
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public int read()
throws IOException {
return _cipherIn.read();
}
@Override
public int read(byte[] buffer, int offset, int length)
throws IOException {
return _cipherIn.read(buffer, offset, length);
}
@Override
public void close()
throws IOException {
Hessian2Input in = _in;
_in = null;
if (in != null) {
_cipherIn.close();
_bodyIn.close();
int len = in.readInt();
if (len != 0)
throw new IOException("Unexpected footer");
in.completeEnvelope();
in.close();
}
}
}
}