package hk.hku.cecid.edi.sfrm.handler;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.X509Certificate;
import hk.hku.cecid.edi.sfrm.net.FastHttpConnector;
import hk.hku.cecid.edi.sfrm.pkg.SFRMConstant;
import hk.hku.cecid.edi.sfrm.pkg.SFRMMessage;
import hk.hku.cecid.edi.sfrm.pkg.SFRMMessageClassifier;
import hk.hku.cecid.edi.sfrm.pkg.SFRMMessageException;
import hk.hku.cecid.edi.sfrm.spa.SFRMComponent;
import hk.hku.cecid.edi.sfrm.spa.SFRMException;
import hk.hku.cecid.edi.sfrm.spa.SFRMLog;
import hk.hku.cecid.edi.sfrm.activation.FileRegionDataSource;
import hk.hku.cecid.piazza.commons.net.ConnectionException;
import hk.hku.cecid.piazza.commons.security.KeyStoreManager;
import hk.hku.cecid.piazza.commons.security.TrustedHostnameVerifier;
/**
* The outgoing message handler is a singleton classes
* that provides service for processing outgoing SFRM
* message.<br><br>
*
* Creation Date: 5/12/2006
*
* @author Twinsen Tsang
* @version 1.0.0
* @since 1.0.3
*/
public class OutgoingMessageHandler extends SFRMComponent{
static{
System.setProperty("sun.net.client.defaultConnectTimeout", "60000");
System.setProperty("sun.net.client.defaultReadTimeout" , "60000");
}
private static OutgoingMessageHandler omh;
/**
* @return an instance of OutgoingMessageHandler.
*/
public static OutgoingMessageHandler getInstance(){
return omh;
}
/**
* Initialization of this Component
*/
protected void init() throws Exception{
super.init();
omh = this;
}
/**
* Pack the SMIME (secure MIME) message to become
* secured SFRM Message.
* <br><br>
*
* Currently, the packing mechanisms support: <br>
* <ol>
* <li> Digitial Signing using MD5 or SHA-1 </li>
* <li> Encryption using RC2_CBC or DES_EDE3_CBC </li>
* </ol>
*
* @param message
* The outgoing SFRM Message.
* @param msgDVO
* The message record associated to this SFRM message.
* @param pDVO
*
* @return
* The secured SFRM message.
* @throws UnrecoverableKeyException
* @throws NoSuchAlgorithmException
* @throws SFRMException
*
* @since
* 1.0.3
*/
protected SFRMMessage packOutgoingMessage(
SFRMMessage message, String signAlgorithm, String encryptAlgorithm, X509Certificate encryptCert)
throws SFRMException, NoSuchAlgorithmException, UnrecoverableKeyException {
// No need to sign and encrypt, return immediately.
if (signAlgorithm == null && encryptAlgorithm == null)
return message;
// Create SMIME Header.
KeyStoreManager keyman = getKeyStoreManager();
String logInfo = " msg id: " + message.getMessageID()
+" and sgt no: " + message.getSegmentNo();
// Setup up signing using MD5 or SHA1
if(signAlgorithm != null){
getLogger().info(SFRMLog.OMH_CALLER + SFRMLog.SIGNING_SGT + logInfo);
message.sign(keyman.getX509Certificate(), keyman.getPrivateKey(), signAlgorithm);
}
// Setup up encrypting using RC2, DES
if(encryptAlgorithm != null){
getLogger().info(SFRMLog.OMH_CALLER + SFRMLog.ENCRYPT_SGT + logInfo);
message.encrypt(encryptCert, encryptAlgorithm);
}
return message;
}
/**
* Send SFRM message.
* <br><br>
*
* @param message The original SFRM Message.
* @param isSign Digital signature is required
* @param isEncryptReq Encryption is required
* @param signAlg Signing algorithm
* @param encryptAlg Encryption algorithm
* @param encrypt Partner public certificate for encryption
*
* @return HTTP response
*
* @throws SFRMMessageException
* @throws ConnectionException
*
* @since 2.0.0
*/
// FIXME: other segment type is signed or encrypted in task
public FastHttpConnector sendMessage (
SFRMMessage message, String endpoint, boolean isHostVerified,
String signAlg, String encryptAlg, X509Certificate encryptCert)
throws SFRMMessageException, ConnectionException {
if (message == null)
throw new SFRMMessageException("Missing SFRM Message");
// Pack the SFRM Message
// TODO: All segment should use this method to pack, re-design interface.
if (message.getSegmentType().equals(SFRMConstant.MSGT_META)){
try {
this.packOutgoingMessage(message, signAlg, encryptAlg, encryptCert);
} catch (Exception e) {
throw new SFRMMessageException("Failed to sign/encrypt message", e);
}
}
// Create the HTTP Connection.
// TODO: Use connection pool.
// TODO: refactor and more test on FastHttpConnector
FastHttpConnector httpConn;
try {
httpConn = new FastHttpConnector(endpoint);
} catch (MalformedURLException e) {
throw new ConnectionException("Failed to create FastHttpConnector", e);
}
// Add SSL Verification if switched on.
if (isHostVerified)
httpConn.setHostnameVerifier(new TrustedHostnameVerifier());
// Log sending information.
getLogger().info(
SFRMLog.OMH_CALLER
+ SFRMLog.SEND_SGT
+" To " + endpoint
+" with msg info"
+ message);
int responseCode;
try{
SFRMMessageClassifier classifier = message.getClassifier();
if(message.getSegmentType().equals(SFRMConstant.MSGT_PAYLOAD) && !classifier.isEncrypted() && !classifier.isSigned()){
FileRegionDataSource fSrc = (FileRegionDataSource) message.getContent();
httpConn.send(fSrc.getInputStream(), message.getHeaders());
}else{
httpConn.send(message.getContentStream(), message.getHeaders());
}
responseCode = httpConn.getResponseCode();
if (responseCode < 200 || responseCode > 300)
throw new ConnectionException("Invalid Response Code.");
return httpConn;
} catch (Exception e){
throw new ConnectionException("Failed to make FastHttpConnector connection", e);
}
}
public SFRMMessage sendMessageWithMessageResponse (
SFRMMessage message, String endpoint, boolean isHostVerified,
String signAlg, String encryptAlg, X509Certificate encryptCert)
throws SFRMMessageException, ConnectionException, IllegalStateException, IOException {
FastHttpConnector conn = sendMessage(message, endpoint, isHostVerified,
signAlg, encryptAlg, encryptCert);
SFRMMessage retMessage = new SFRMMessage(conn.getResponseHeaders(), conn.getResponseContentStream());
return retMessage;
}
}