/* ==========================================
* Laverca Project
* https://sourceforge.net/projects/laverca/
* ==========================================
* Copyright 2015 Laverca Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fi.laverca.mss;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.util.GregorianCalendar;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import javax.xml.rpc.ServiceException;
import org.apache.axis.AxisFault;
import org.apache.axis.EngineConfiguration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import fi.laverca.jaxb.mss.DataType;
import fi.laverca.jaxb.mss.MSSHandshakeReq;
import fi.laverca.jaxb.mss.MSSHandshakeResp;
import fi.laverca.jaxb.mss.MSSProfileReq;
import fi.laverca.jaxb.mss.MSSProfileResp;
import fi.laverca.jaxb.mss.MSSReceiptReq;
import fi.laverca.jaxb.mss.MSSReceiptResp;
import fi.laverca.jaxb.mss.MSSRegistrationReq;
import fi.laverca.jaxb.mss.MSSRegistrationResp;
import fi.laverca.jaxb.mss.MSSSignatureReq;
import fi.laverca.jaxb.mss.MSSSignatureResp;
import fi.laverca.jaxb.mss.MSSStatusReq;
import fi.laverca.jaxb.mss.MSSStatusResp;
import fi.laverca.jaxb.mss.MeshMemberType;
import fi.laverca.jaxb.mss.MessageAbstractType;
import fi.laverca.jaxb.mss.MessagingModeType;
import fi.laverca.jaxb.mss.MobileUserType;
import fi.laverca.jaxb.mss.MssURIType;
import fi.laverca.jaxb.mss.ObjectFactory;
import fi.laverca.mss.ws.MSS_HandshakeBindingStub;
import fi.laverca.mss.ws.MSS_ProfileQueryBindingStub;
import fi.laverca.mss.ws.MSS_ReceiptBindingStub;
import fi.laverca.mss.ws.MSS_RegistrationBindingStub;
import fi.laverca.mss.ws.MSS_SignatureBindingStub;
import fi.laverca.mss.ws.MSS_SignatureServiceLocator;
import fi.laverca.mss.ws.MSS_StatusQueryBindingStub;
import fi.laverca.util.AbstractSoapBindingStub;
import fi.laverca.util.ComponentsHTTPSender;
import fi.laverca.util.DTBS;
import fi.laverca.util.JMarshallerFactory;
import fi.laverca.util.LavercaHttpClient;
import fi.laverca.util.ProxySettings;
/**
* A raw ETSI TS 102 204 client object.
*/
public class MssClient {
private static Log log = LogFactory.getLog(MssClient.class);
public static ObjectFactory mssObjFact = new ObjectFactory();
// AP settings
private String apId = null;
private String apPwd = null;
// MSSP AE connection settings
private final MSS_SignatureServiceLocator mssService = new MSS_SignatureServiceLocator();
private URL MSSP_SI_URL = null;
private URL MSSP_RC_URL = null;
private URL MSSP_HS_URL = null;
private URL MSSP_ST_URL = null;
private URL MSSP_PR_URL = null;
private URL MSSP_RG_URL = null;
private int poolSize = 16;
/** New connection timeout: milliseconds, 0 ≤ not set */
private int newConnTimeout;
/** New SO read timeout: milliseconds, 0 ≤ not set */
private int newSoTimeout;
private String newUsername;
private String newPassword;
private ProxySettings proxySettings;
private SSLSocketFactory sslSocketFactory;
private LavercaHttpClient httpClient;
private static boolean marshallerInitDone;
private static void marshallerInit() {
if (marshallerInitDone) return;
// Record global package names of generated JAXB classes
for (final Class> c : new Class[] {
fi.laverca.jaxb.mss.ObjectFactory.class,
fi.laverca.jaxb.mssfi.ObjectFactory.class,
fi.laverca.jaxb.mid204as1.ObjectFactory.class,
fi.laverca.jaxb.saml2a.ObjectFactory.class,
fi.laverca.jaxb.saml2p.ObjectFactory.class,
fi.laverca.jaxb.sco204ext1.ObjectFactory.class,
fi.laverca.jaxb.soap12env.ObjectFactory.class,
fi.laverca.jaxb.kiurumssp5.ObjectFactory.class }) {
final String p = c.getPackage().getName();
JMarshallerFactory.addJAXBPath(p);
}
marshallerInitDone = true;
}
/**
* NOTE:
*
if any of the URLs require SSL, you must
* call {@link fi.laverca.util.JvmSsl#setSSL(String,String,String,String,String)} OR set the engine configuration before sending any requests.
*
* @param apId Your identifier; MessageAbstractType/AP_Info/AP_ID. Not null.
* @param apPwd Your password; MessageAbstractType/AP_Info/AP_PWD. Not null.
* @param msspSignatureUrl Connection URL to the AE for signature requests.
* @param msspStatusUrl Connection URL to the AE for status query requests.
* @param msspReceiptUrl Connection URL to the AE for receipt requests.
* @param msspRegistrationUrl Connection URL to the AE for registration requests.
* @param msspProfileUrl Connection URL to the AE for profile query requests.
* @param msspHandshakeUrl Connection URL to the AE for handshake requests.
*
* @throws IllegalArgumentException if AP ID or AP PWD is missing or invalid.
*
*/
public MssClient(final String apId, // AP settings
final String apPwd,
final String msspSignatureUrl, // AE connection settings
final String msspStatusUrl,
final String msspReceiptUrl,
final String msspRegistrationUrl,
final String msspProfileUrl,
final String msspHandshakeUrl)
throws IllegalArgumentException
{
if (apId != null) {
this.apId = apId;
} else {
throw new IllegalArgumentException("null apId not allowed.");
}
if (apPwd != null) {
this.apPwd = apPwd;
} else {
throw new IllegalArgumentException("null apPwd not allowed.");
}
this.setAeAddress(msspSignatureUrl,
msspStatusUrl,
msspReceiptUrl,
msspRegistrationUrl,
msspProfileUrl,
msspHandshakeUrl);
marshallerInit();
}
/**
* NOTE:
*
if any of the URLs require SSL, you must
* call {@link fi.laverca.util.JvmSsl#setSSL(String,String,String,String,String)} OR set the engine configuration before sending any requests.
*
* @param apId Your identifier; MessageAbstractType/AP_Info/AP_ID. Not null.
* @param apPwd Your password; MessageAbstractType/AP_Info/AP_PWD. Not null.
* @param msspSignatureUrl Connection URL to the AE for signature requests.
* @param msspStatusUrl Connection URL to the AE for status query requests.
* @param msspReceiptUrl Connection URL to the AE for receipt requests.
*/
public MssClient(final String apId,
final String apPwd,
final String msspSignatureUrl,
final String msspStatusUrl,
final String msspReceiptUrl) {
this(apId, apPwd, msspSignatureUrl, msspStatusUrl, msspReceiptUrl, null, null, null);
}
/**
* Set this socket factory before calling MSS operations,
* if you want to e.g. inclusion of your client certificate
* on the outgoing calls.
*
* @param ssf Define a SSL SocketFactory with a client side key
*/
public void setSSLSocketFactory(SSLSocketFactory ssf) {
this.sslSocketFactory = ssf;
}
/**
* Create an SSLSocketFactory
* @param ksFile Keystore filename
* @param ksPwd Keystore password
* @param ksType Keystore type
* @return Created SSLSocketFactory
* @throws IOException
* @throws GeneralSecurityException
*/
public static SSLSocketFactory createSSLFactory(final String ksFile, final String ksPwd, final String ksType)
throws GeneralSecurityException, IOException
{
return createSSLFactory(ksFile, ksPwd, ksType, null, null, null);
}
/**
* Create an SSLSocketFactory
* @param ksFile Keystore filename
* @param ksPwd Keystore password
* @param ksType Keystore type
* @param tsFile Truststore filename
* @param tsPwd Truststore password
* @param tsType Truststore type
* @return Created SSLSocketFactory
*/
public static SSLSocketFactory createSSLFactory(final String ksFile, final String ksPwd, final String ksType,
final String tsFile, final String tsPwd, final String tsType)
throws GeneralSecurityException, IOException
{
KeyStore ks = KeyStore.getInstance(ksType);
KeyStore ts = KeyStore.getInstance(tsType);
InputStream kis = new FileInputStream(ksFile);
InputStream tis = null;
if (tsFile != null) {
tis = new FileInputStream(tsFile);
}
try {
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
ks.load(kis, ksPwd.toCharArray());
kmf.init(ks, ksPwd.toCharArray());
TrustManagerFactory tmf = null;
if (tsFile != null) {
tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
ts.load(tis, tsPwd.toCharArray());
tmf.init(ts);
}
SSLContext ctx = SSLContext.getInstance("TLSv1.2");
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return ctx.getSocketFactory();
} finally {
tis.close();
kis.close();
}
}
/**
* Set a custom Axis EngineConfiguration
* @param conf Axis EngineConfiguration
*/
public void setEngineConfiguration(final EngineConfiguration conf) {
this.mssService.setEngineConfiguration(conf);
}
/**
* Sets the AE connection URLs.
* @param msspSignatureUrl Connection URL to the AE for signature requests.
* @param msspStatusUrl Connection URL to the AE for status query requests.
* @param msspReceiptUrl Connection URL to the AE for receipt requests.
* @param msspRegistrationUrl Connection URL to the AE for registration requests.
* @param msspProfileUrl Connection URL to the AE for profile query requests.
* @param msspHandshakeUrl Connection URL to the AE for handshake requests.
*/
public void setAeAddress( String msspSignatureUrl,
String msspStatusUrl,
String msspReceiptUrl,
String msspRegistrationUrl,
String msspProfileUrl,
String msspHandshakeUrl )
throws IllegalArgumentException
{
try {
if (msspSignatureUrl != null) {
this.MSSP_SI_URL = new URL (msspSignatureUrl);
}
if (msspStatusUrl != null) {
this.MSSP_ST_URL = new URL (msspStatusUrl);
}
if (msspReceiptUrl != null) {
this.MSSP_RC_URL = new URL (msspReceiptUrl);
}
if (msspRegistrationUrl != null) {
this.MSSP_RG_URL = new URL (msspRegistrationUrl);
}
if (msspProfileUrl != null) {
this.MSSP_PR_URL = new URL (msspProfileUrl);
}
if (msspHandshakeUrl != null) {
this.MSSP_HS_URL = new URL (msspHandshakeUrl);
}
} catch (MalformedURLException mue) {
throw new IllegalArgumentException(mue.getMessage());
}
}
private LavercaHttpClient getHttpClient() {
synchronized (this) {
if (this.httpClient == null) {
// If SSLSocketFactory is set, use it, otherwise use system default
SSLSocketFactory ssf = this.sslSocketFactory;
if (ssf == null) {
ssf = (SSLSocketFactory)SSLSocketFactory.getDefault();
}
this.httpClient = new LavercaHttpClient("mssClientPool",
this.poolSize,
this.newConnTimeout,
this.newSoTimeout,
this.newUsername,
this.newPassword,
this.proxySettings,
ssf);
}
}
return this.httpClient;
}
/**
* Fills Minorversion, Majorversion, AP_Info and MSS_Info to the given message.
* @param mat Message to fill
* @param apTransId AP Transaction ID
*/
private void initializeRequestMessage(final MessageAbstractType mat, final String apTransId) {
if (mat == null)
throw new IllegalArgumentException("can't fill a null mat");
if (apTransId == null)
throw new IllegalArgumentException("null apTransId not allowed.");
// Set the interface versions. 1 for both, as per ETSI TS 102 204.
mat.setMajorVersion(Long.valueOf(1));
mat.setMinorVersion(Long.valueOf(1));
// Create the AP_Info.
final MessageAbstractType.APInfo aiObject = mssObjFact.createMessageAbstractTypeAPInfo();
aiObject.setAPID(this.apId);
aiObject.setAPPWD(this.apPwd);
aiObject.setAPTransID(apTransId);
aiObject.setInstant(new GregorianCalendar());
mat.setAPInfo(aiObject);
final MessageAbstractType.MSSPInfo miObject = mssObjFact.createMessageAbstractTypeMSSPInfo();
miObject.setMSSPID(mssObjFact.createMeshMemberType());
mat.setMSSPInfo(miObject);
}
/**
* Creates a signature request.
*
* @param apTransId AP Transaction ID - not null.
* @param msisdn MSISDN of the mobile user - not null.
* @param dtbs Data to be Signed - not null.
* @param dataToBeDisplayed Data to be displayed - may be null.
* @param signatureProfile Signature profile to use - not null.
* @param mss_format MSS_Format to use - not null.
* @param messagingMode Messaging mode to use - not null.
* @return Created signature request
*/
public MSSSignatureReq createSignatureRequest(final String apTransId,
final String msisdn,
final DTBS dtbs,
final String dataToBeDisplayed,
final String signatureProfile,
final String mss_format,
final MessagingModeType messagingMode) {
final MSSSignatureReq req = mssObjFact.createMSSSignatureReq();
this.initializeRequestMessage(req, apTransId);
if (msisdn == null)
throw new IllegalArgumentException("null msisdn is not allowed.");
MobileUserType muObject = mssObjFact.createMobileUserType();
muObject.setMSISDN(msisdn);
req.setMobileUser(muObject);
if (dtbs == null)
throw new IllegalArgumentException("null dataToBeSigned is not allowed.");
final DataType dsObject = dtbs.toDataToBeSigned();
req.setDataToBeSigned(dsObject);
if (dataToBeDisplayed != null) {
final DataType ddObject = mssObjFact.createDataType();
ddObject.setValue(dataToBeDisplayed);
req.setDataToBeDisplayed(ddObject);
}
if (signatureProfile == null)
throw new IllegalArgumentException("null signatureProfile is not allowed.");
final MssURIType spObject = mssObjFact.createMssURIType();
spObject.setMssURI(signatureProfile);
req.setSignatureProfile(spObject);
if(mss_format != null) {
MssURIType mfObject = mssObjFact.createMssURIType();
mfObject.setMssURI(mss_format);
req.setMSSFormat(mfObject);
}
if (messagingMode == null)
throw new IllegalArgumentException("null messagingMode is not allowed.");
req.setMessagingMode(messagingMode);
req.setAdditionalServices(MssClient.mssObjFact.createMSSSignatureReqAdditionalServices());
return req;
}
/**
* Create a MSS_ReceiptRequest based on received MSS_SignatureResponse
*
* @param sigResp MSS_SignatureResponse on which the receipt request is constructed
* @param apTransId each new MSS request needs a new apTransID
* @param message Message to display
* @return Created MSS_ReceiptReq
*/
public MSSReceiptReq createReceiptRequest(final MSSSignatureResp sigResp,
final String apTransId,
final String message)
{
final MSSReceiptReq req = mssObjFact.createMSSReceiptReq();
this.initializeRequestMessage(req, apTransId);
if (sigResp == null) {
throw new IllegalArgumentException("null sigResp not allowed.");
}
if (sigResp.getMSSPInfo() == null) {
throw new IllegalArgumentException("null sigResp.MSSP_Info not allowed.");
}
final MeshMemberType msspId = sigResp.getMSSPInfo().getMSSPID();
if (msspId == null) {
throw new IllegalArgumentException("null sigResp.MSSP_Info.MSSP_ID not allowed.");
}
req.getMSSPInfo().setMSSPID(msspId); // fillMatStuff creates an empty MSSP_Info
final String msspTransId = sigResp.getMSSPTransID();
req.setMSSPTransID(msspTransId);
if (message != null) {
final DataType meObject = mssObjFact.createDataType();
meObject.setValue(message);
req.setMessage(meObject);
}
return req;
}
/**
* Create a status request for a signature response.
*
* @param apTransId new AP transaction id
* @param sigResp Original MSS_SignatureResp
* @return Created MSS_StatusReq
* @throws IllegalArgumentException if the given signature response does not contain all the necessary data to create an MSS_StatusReq
*/
public MSSStatusReq createStatusRequest(final MSSSignatureResp sigResp,
final String apTransId)
throws IllegalArgumentException
{
MSSStatusReq req = new MSSStatusReq();
this.initializeRequestMessage(req, apTransId);
if (sigResp == null) {
throw new IllegalArgumentException("null sigResp not allowed.");
}
if (sigResp.getMSSPInfo() == null) {
throw new IllegalArgumentException("null sigResp.MSSP_Info not allowed.");
}
final MeshMemberType msspId = sigResp.getMSSPInfo().getMSSPID();
if (msspId == null) {
throw new IllegalArgumentException("null sigResp.MSSP_Info.MSSP_ID not allowed.");
}
req.getMSSPInfo().setMSSPID(msspId); // fillMatStuff creates an empty MSSP_Info
final String msspTransId = sigResp.getMSSPTransID();
req.setMSSPTransID(msspTransId);
return req;
}
/**
* Send the MSS_SignatureRequest to MSS system receiving answer
* @param req the MSS_SignatureReq
* @return received MSS_SignatureResp
* @throws IOException if a HTTP communication error occurs
* @throws IllegalArgumentException if req is null
* occurred i.e. a SOAP fault was generated by the local
* SOAP client stub.
*/
public MSSSignatureResp send(final MSSSignatureReq req) throws IOException {
if (req == null) throw new IllegalArgumentException ("Unable to send null SignatureReq");
if (req.getAdditionalServices() != null) {
if (req.getAdditionalServices().getServices().size() == 0) {
req.setAdditionalServices(null);
}
}
return (MSSSignatureResp)this.sendMat(req);
}
/**
* Send the MSS_ReceiptRequest to MSS system receiving answer
* @param req the MSS_ReceiptReq
* @return received MSS_ReceiptResp
* @throws IOException if a HTTP communication error occurs
* @throws IllegalArgumentException if req is null
* occurred i.e. a SOAP fault was generated by the local
* SOAP client stub.
*/
public MSSReceiptResp send(final MSSReceiptReq req) throws IOException {
if (req == null) throw new IllegalArgumentException ("Unable to send null ReceiptReq");
return (MSSReceiptResp)this.sendMat(req);
}
/**
* Send the MSS_HandshakeRequest to MSS system receiving answer
* @param req the MSS_HandshakeReq
* @return received MSS_HandshakeResp
* @throws IOException if a HTTP communication error occurs
* @throws IllegalArgumentException if req is null
* occurred i.e. a SOAP fault was generated by the local
* SOAP client stub.
*/
public MSSHandshakeResp send(final MSSHandshakeReq req) throws IOException {
if (req == null) throw new IllegalArgumentException ("Unable to send null HandshakeReq");
return (MSSHandshakeResp)this.sendMat(req);
}
/**
* Send the MSS_StatusRequest to MSS system receiving answer
* @param req the MSS_StatusReq
* @return received MSS_StatusResp
* @throws IOException if a HTTP communication error occurs
* @throws IllegalArgumentException if req is null
* occurred i.e. a SOAP fault was generated by the local
* SOAP client stub.
*/
public MSSStatusResp send(final MSSStatusReq req) throws IOException {
if (req == null) throw new IllegalArgumentException ("Unable to send null StatusReq");
return (MSSStatusResp)this.sendMat(req);
}
/**
* Send the MSS_ProfileRequest to MSS system receiving answer
* @param req the MSS_ProfileReq
* @return received MSS_ProfileResp
* @throws IOException if a HTTP communication error occurs
* @throws IllegalArgumentException if req is null
* occurred i.e. a SOAP fault was generated by the local
* SOAP client stub.
*/
public MSSProfileResp send(final MSSProfileReq req) throws IOException {
if (req == null) throw new IllegalArgumentException ("Unable to send null ProfileReq");
return (MSSProfileResp)this.sendMat(req);
}
/**
* Send the MSS_RegistrationRequest to MSS system receiving answer
* @param req the MSS_RegistrationReq
* @return received MSS_RegistrationResp
* @throws IOException if a HTTP communication error occurs
* @throws IllegalArgumentException if req is null
* occurred i.e. a SOAP fault was generated by the local
* SOAP client stub.
*/
public MSSRegistrationResp send(final MSSRegistrationReq req) throws IOException {
if (req == null) throw new IllegalArgumentException ("Unable to send null RegistrationReq");
return (MSSRegistrationResp)this.sendMat(req);
}
/**
* Sends an MSS request.
*
* @param req Abstract request type
* @throws IOException if a HTTP communication error occurred i.e. a SOAP fault was generated by the local SOAP client stub.
*/
private MessageAbstractType sendMat(final MessageAbstractType req)
throws AxisFault, IOException
{
AbstractSoapBindingStub port = null;
try {
Long timeout = null;
if (req instanceof MSSSignatureReq) {
timeout = ((MSSSignatureReq)req).getTimeOut();
port = (MSS_SignatureBindingStub)this.mssService.getMSS_SignaturePort(this.MSSP_SI_URL);
} else if (req instanceof MSSReceiptReq) {
port = (MSS_ReceiptBindingStub)this.mssService.getMSS_ReceiptPort(this.MSSP_RC_URL);
} else if (req instanceof MSSHandshakeReq) {
port = (MSS_HandshakeBindingStub)this.mssService.getMSS_HandshakePort(this.MSSP_HS_URL);
} else if (req instanceof MSSStatusReq) {
port = (MSS_StatusQueryBindingStub)this.mssService.getMSS_StatusQueryPort(this.MSSP_ST_URL);
} else if (req instanceof MSSProfileReq) {
port = (MSS_ProfileQueryBindingStub)this.mssService.getMSS_ProfileQueryPort(this.MSSP_PR_URL);
} else if (req instanceof MSSRegistrationReq) {
port = (MSS_RegistrationBindingStub)this.mssService.getMSS_RegistrationPort(this.MSSP_RG_URL);
}
if (port == null) {
throw new IOException("Invalid request type");
}
if (timeout != null) {
// ETSI TS 102 204 defines TimeOut in seconds instead of milliseconds
port.setTimeout(timeout.intValue()*1000);
}
} catch (ServiceException se) {
log.error("Failed to get port: " + se.getMessage());
throw new IOException(se.getMessage());
}
try {
if (port._getCall() == null) {
port._createCall();
}
} catch (Exception e) {
log.fatal("Could not do port._createCall()", e);
}
// Set tools for each context.
port.setProperty(ComponentsHTTPSender.HTTPCLIENT_INSTANCE, this.getHttpClient());
// port.setProperty(CommonsHTTPSender.FAULTFACTORY_INSTANCE, MssClientFaultFactory.getInstance());
if (port instanceof MSS_SignatureBindingStub) {
return ((MSS_SignatureBindingStub)port).MSS_Signature((MSSSignatureReq)req);
} else if (port instanceof MSS_StatusQueryBindingStub) {
return ((MSS_StatusQueryBindingStub)port).MSS_StatusQuery((MSSStatusReq)req);
} else if (port instanceof MSS_ReceiptBindingStub) {
return ((MSS_ReceiptBindingStub)port).MSS_Receipt((MSSReceiptReq)req);
} else if (port instanceof MSS_HandshakeBindingStub) {
return ((MSS_HandshakeBindingStub)port).MSS_Handshake((MSSHandshakeReq)req);
} else if (port instanceof MSS_ProfileQueryBindingStub) {
return ((MSS_ProfileQueryBindingStub)port).MSS_ProfileQuery((MSSProfileReq)req);
} else if (port instanceof MSS_RegistrationBindingStub) {
return ((MSS_RegistrationBindingStub)port).MSS_Registration((MSSRegistrationReq)req);
}
throw new IOException("Invalid call parameters");
}
/**
* Return whether s is a valid xs:NCName String.
*
* @param s String to test
* @return true if s is a valid xs:NCName
*/
public static boolean isNCName(final String s) {
if (s == null) {
return false;
} else {
return org.apache.axis.types.NCName.isValid(s);
}
}
}