/**
*
*/
package at.knowcenter.wag.egov.egiz.sig.connectors.moa;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import at.knowcenter.wag.egov.egiz.cfg.SettingsReader;
import at.knowcenter.wag.egov.egiz.exceptions.ConnectorException;
import at.knowcenter.wag.egov.egiz.exceptions.SettingsException;
import at.knowcenter.wag.egov.egiz.sig.SignatureData;
import at.knowcenter.wag.egov.egiz.sig.SignatureObject;
import at.knowcenter.wag.egov.egiz.sig.SignatureResponse;
import at.knowcenter.wag.egov.egiz.sig.X509Cert;
import at.knowcenter.wag.egov.egiz.sig.connectors.Connector;
import at.knowcenter.wag.egov.egiz.sig.connectors.TemplateReplaces;
import at.knowcenter.wag.egov.egiz.sig.connectors.bku.BKUHelper;
import at.knowcenter.wag.egov.egiz.sig.connectors.bku.BKUPostConnection;
import at.knowcenter.wag.egov.egiz.sig.connectors.bku.SignSignatureObject;
import at.knowcenter.wag.egov.egiz.tools.CodingHelper;
import at.knowcenter.wag.egov.egiz.tools.FileHelper;
/**
* @author wprinz
*
*/
public class EnvelopingBase64MOAConnector implements Connector
{
/**
* The log.
*/
private static Log log = LogFactory.getLog(EnvelopingBase64MOAConnector.class);
/**
* The environemnt configuration of this connector containing templates and
* other configurable elements.
*/
protected Environment environment = null;
/**
* Constructor that builds the configuration environment for this connector
* according to the given profile.
*
*
* If confuguration parameters are not defined on that profile, the default
* parameters defined in the configuration are used.
*
*
* @param profile
* The profile from which the Environment should be assembled.
* @throws ConnectorException
* f.e.
*/
public EnvelopingBase64MOAConnector(String profile) throws ConnectorException
{
this.environment = new Environment(profile);
}
/**
* @see at.knowcenter.wag.egov.egiz.sig.connectors.Connector#doSign(at.knowcenter.wag.egov.egiz.sig.SignatureData)
*/
public SignSignatureObject doSign(SignatureData data) throws ConnectorException
{
log.debug("doSign:"); //$NON-NLS-1$
String sign_request_xml = prepareSignRequest(data);
log.debug("sign_request_xml = " + sign_request_xml); //$NON-NLS-1$
String url = this.environment.getSignURL();
Properties response_properties = sendRequest(url, MOASoapConnection.SERVICE_SIGN, sign_request_xml);
log.debug("response_string = " + response_properties.getProperty(BKUPostConnection.RESPONSE_STRING_KEY));; //$NON-NLS-1$
SignSignatureObject sso = analyzeSignResponse(response_properties);
// TODO this could be made more generic
sso.response_properties = response_properties;
log.debug("doSign finished."); //$NON-NLS-1$
return sso;
}
/**
* @see at.knowcenter.wag.egov.egiz.sig.connectors.Connector#doVerify(at.knowcenter.wag.egov.egiz.sig.SignatureData,
* at.knowcenter.wag.egov.egiz.sig.connectors.bku.SignSignatureObject)
*/
public SignatureResponse doVerify(SignatureData data, SignSignatureObject so) throws ConnectorException
{
log.debug("doVerify:"); //$NON-NLS-1$
String verify_request_xml = prepareVerifyRequest(data, so);
log.debug("verify_request_xml = " + verify_request_xml); //$NON-NLS-1$
String url = this.environment.getVerifyURL();
Properties response_properties = sendRequest(url, MOASoapConnection.SERVICE_VERIFY, verify_request_xml);
SignatureResponse signature_response = analyzeVerifyResponse(response_properties);
log.debug("doVerify finished."); //$NON-NLS-1$
return signature_response;
}
protected Properties sendRequest(String url, String mode,
String request_string) throws ConnectorException
{
try
{
Properties response_properties = MOASoapConnection.connectMOA(request_string, MOASoapConnection.SERVICE_SIGN, url);
return response_properties;
}
catch (Exception e)
{
throw new ConnectorException(320, e);
}
}
/**
* Prepares the sign request xml to be sent using the sign request template.
*
* @param data
* The SignatureData.
* @return Returns the sign request xml to be sent.
* @throws ConnectorException
* f.e.
*/
protected String prepareSignRequest(SignatureData data) throws ConnectorException
{
log.debug("prepareSignRequest:"); //$NON-NLS-1$
String sign_request_template = this.environment.getSignRequestTemplate();
String sign_key_identifier = this.environment.getSignKeyIdentifier();
String base64 = BKUHelper.prepareBase64Content(data);
String sign_request_xml = sign_request_template.replaceFirst(TemplateReplaces.KEY_IDENTIFIER_REPLACE, sign_key_identifier);
sign_request_xml = sign_request_xml.replaceFirst(TemplateReplaces.BASE64_CONTENT_REPLACE, base64);
log.debug("prepareSignRequest finished."); //$NON-NLS-1$
return sign_request_xml;
}
/**
* Prepares the verify request xml to be sent using the verify request
* template.
*
* @param data
* The SignatureData.
* @param so
* The signature information object.
* @return Returns the verify request xml to be sent.
* @throws ConnectorException
* f.e.
*/
public String prepareVerifyRequest(SignatureData data,
SignSignatureObject so) throws ConnectorException
{
String verify_request_template = this.environment.getVerifyRequestTemplate();
String xml_content = null;
// TODO implement MOA
// if (sigObject.isMOASigned())
// {
// MOAConnector moa_conn = new MOAConnector();
// // get the MOA-template
// verify_template_str = moa_conn.getVerifyTemplate(normalizedText,
// sigObject);
// }
// else
// {
// get the BKU-template
xml_content = prepareXMLContent(data, so);
// }
String verify_request_xml = verify_request_template.replaceFirst(TemplateReplaces.XML_CONTENT_REPLACE, xml_content);
verify_request_xml = verify_request_xml.replaceFirst(TemplateReplaces.TRUST_PROFILE_ID_REPLACE, this.environment.getVerifyTrustProfileId());
return verify_request_xml;
}
/**
* Analyzes the sign response xml and extracts the signature data.
*
* @param response_properties
* The response properties containing the response String and
* transport related information.
* @return Returns the extracted data encapsulated in a SignatureObject.
* @throws ConnectorException
* f.e.
*/
public SignSignatureObject analyzeSignResponse(Properties response_properties) throws ConnectorException
{
log.debug("analyzeSignResponse:"); //$NON-NLS-1$
String response_string = response_properties.getProperty(BKUPostConnection.RESPONSE_STRING_KEY);
BKUHelper.checkResponseForError(response_string);
SignSignatureObject so = parseCreateXMLResponse(response_string);
log.debug("analyzeSignResponse finished."); //$NON-NLS-1$
return so;
}
/**
* Analyzes the verify response string.
*
* @param response_properties
* The response properties containing the response XML.
* @return Returns the SignatureResponse containing the verification result.
* @throws ConnectorException
* f.e.
*/
public SignatureResponse analyzeVerifyResponse(Properties response_properties) throws ConnectorException
{
log.debug("analyzeVerifyResponse:"); //$NON-NLS-1$
String response_string = response_properties.getProperty(BKUPostConnection.RESPONSE_STRING_KEY);
BKUHelper.checkResponseForError(response_string);
SignatureResponse signature_response = BKUHelper.parseVerifyXMLResponse(response_string);
log.debug("analyzeVerifyResponse finished."); //$NON-NLS-1$
return signature_response;
}
/**
* Prepares the XML content the holds the actual signature data.
*
*
* This strongly rebuilds the XML content as retuned from a sign request.
*
*
* @param data
* The data.
* @param so
* The signature object containing the signature information.
* @return Returns the XML content.
* @throws ConnectorException
* f.e.
*/
public String prepareXMLContent(SignatureData data, SignSignatureObject so) throws ConnectorException
{
log.debug("prepareXMLContent:"); //$NON-NLS-1$
try
{
String verify_template = this.environment.getVerifyTemplate();
X509Certificate cert = so.getX509Certificate();
String cert_alg = this.environment.getCertAlgEcdsa();
if (cert.getPublicKey().getAlgorithm().indexOf("RSA") >= 0) //$NON-NLS-1$
{
cert_alg = this.environment.getCertAlgRsa();
}
// cert alg replace
String verify_xml = verify_template.replaceFirst(TemplateReplaces.CERT_ALG_REPLACE, cert_alg);
// data digest replace
{
byte[] data_value = data.getData();
byte[] data_value_hash = CodingHelper.buildDigest(data_value);
String object_data_hash = CodingHelper.encodeBase64(data_value_hash);
verify_xml = verify_xml.replaceFirst(TemplateReplaces.DIGEST_VALUE_SIGNED_DATA_REPLACE, object_data_hash);
}
verify_xml = verify_xml.replaceFirst(TemplateReplaces.SIGNATURE_VALUE_REPLACE, so.getSignatureValue());
// X.509 Certificate replace
byte[] der = cert.getEncoded();
byte[] cert_hash = CodingHelper.buildDigest(der);
String certDigest = CodingHelper.encodeBase64(cert_hash);
String x509_cert_string = CodingHelper.encodeBase64(der);
verify_xml = verify_xml.replaceFirst(TemplateReplaces.X509_CERTIFICATE_REPLACE, x509_cert_string);
// Base64 content replace
String base64 = BKUHelper.prepareBase64Content(data);
verify_xml = verify_xml.replaceFirst(TemplateReplaces.BASE64_CONTENT_REPLACE, base64);
// Qualified Properties replaces
verify_xml = verify_xml.replaceFirst(TemplateReplaces.SIGNING_TIME_REPLACE, so.getDate());
verify_xml = verify_xml.replaceFirst(TemplateReplaces.DIGEST_VALUE_CERTIFICATE_REPLACE, certDigest);
verify_xml = verify_xml.replaceFirst(TemplateReplaces.X509_ISSUER_NAME_REPLACE, so.getIssuer());
verify_xml = verify_xml.replaceFirst(TemplateReplaces.X509_SERIAL_NUMBER_REPLACE, so.getSerialNumber());
// SigDataRefReplace already done above
// Signed Properties hash
{
final String ETSI_SIGNED_PROPERTIES_START_TAG = "= 0;
final int hash_end = verify_xml.indexOf(ETSI_SIGNED_PROPERTIES_END_TAG, hash_start) + ETSI_SIGNED_PROPERTIES_END_TAG.length();
assert hash_end - ETSI_SIGNED_PROPERTIES_END_TAG.length() >= 0;
assert hash_end > hash_start;
final String string_to_be_hashed = verify_xml.substring(hash_start, hash_end);
log.debug("etsi:SignedProperties string to be hashed: " + string_to_be_hashed); //$NON-NLS-1$
final byte[] bytes_to_be_hashed = string_to_be_hashed.getBytes("UTF-8"); //$NON-NLS-1$
byte[] sig_prop_code = CodingHelper.buildDigest(bytes_to_be_hashed);
String sig_prop_hash = CodingHelper.encodeBase64(sig_prop_code);
verify_xml = verify_xml.replaceFirst(TemplateReplaces.DIGEST_VALUE_SIGNED_PROPERTIES_REPLACE, sig_prop_hash);
}
log.debug("prepareXMLContent finished."); //$NON-NLS-1$
return verify_xml;
}
catch (Exception e)
{
log.debug(e);
throw new ConnectorException(310, e);
}
}
/**
* This method parses the MOA-Response string. It separates the
* SignatureValue, X509IssuerName, SigningTime, X509SerialNumber,
* X509Certificate, CertDigest and DigestValues. If the X509Certificate is
* extracted it would be stored in the certificates directory.
*
* @param xmlResponse
* the response string from the MOA sign-request
* @throws ConnectorException
* ErrorCode (303, 304)
* @see SignatureObject
* @see CodingHelper
* @see X509Cert
*/
public static SignSignatureObject parseCreateXMLResponse(String xmlResponse) throws ConnectorException
{
Pattern sig_val_p_s = Pattern.compile("<[\\w]*:?SignatureValue>"); //$NON-NLS-1$
Pattern sig_val_p_e = Pattern.compile("[\\w]*:?SignatureValue>"); //$NON-NLS-1$
Pattern iss_nam_p_s = Pattern.compile("<[\\w]*:?X509IssuerName>"); //$NON-NLS-1$
Pattern iss_nam_p_e = Pattern.compile("[\\w]*:?X509IssuerName>"); //$NON-NLS-1$
Pattern sig_tim_p_s = Pattern.compile("<[\\w]*:?SigningTime>"); //$NON-NLS-1$
Pattern sig_tim_p_e = Pattern.compile("[\\w]*:?SigningTime>"); //$NON-NLS-1$
Pattern ser_num_p_s = Pattern.compile("<[\\w]*:?X509SerialNumber>"); //$NON-NLS-1$
Pattern ser_num_p_e = Pattern.compile("[\\w]*:?X509SerialNumber>"); //$NON-NLS-1$
Pattern sig_cer_p_s = Pattern.compile("<[\\w]*:?X509Certificate>"); //$NON-NLS-1$
Pattern sig_cer_p_e = Pattern.compile("[\\w]*:?X509Certificate>"); //$NON-NLS-1$
// Pattern sig_cer_d_p_s = Pattern.compile("<[\\w]*:?CertDigest>");
// //$NON-NLS-1$
// Pattern sig_cer_d_p_e = Pattern.compile("[\\w]*:?CertDigest>");
// //$NON-NLS-1$
// Pattern dig_val_p_s = Pattern.compile("<[\\w]*:?DigestValue>");
// //$NON-NLS-1$
// Pattern dig_val_p_e = Pattern.compile("[\\w]*:?DigestValue>");
// //$NON-NLS-1$
Matcher sig_val_m_s = sig_val_p_s.matcher(xmlResponse);
Matcher sig_val_m_e = sig_val_p_e.matcher(xmlResponse);
Matcher iss_nam_m_s = iss_nam_p_s.matcher(xmlResponse);
Matcher iss_nam_m_e = iss_nam_p_e.matcher(xmlResponse);
Matcher sig_tim_m_s = sig_tim_p_s.matcher(xmlResponse);
Matcher sig_tim_m_e = sig_tim_p_e.matcher(xmlResponse);
Matcher ser_num_m_s = ser_num_p_s.matcher(xmlResponse);
Matcher ser_num_m_e = ser_num_p_e.matcher(xmlResponse);
Matcher sig_cer_m_s = sig_cer_p_s.matcher(xmlResponse);
Matcher sig_cer_m_e = sig_cer_p_e.matcher(xmlResponse);
// Matcher sig_cer_d_m_s = sig_cer_d_p_s.matcher(xmlResponse);
// Matcher sig_cer_d_m_e = sig_cer_d_p_e.matcher(xmlResponse);
// Matcher dig_val_m_s = dig_val_p_s.matcher(xmlResponse);
// Matcher dig_val_m_e = dig_val_p_e.matcher(xmlResponse);
// SignatureValue
String sig_val = null;
if (sig_val_m_s.find() && sig_val_m_e.find())
{
sig_val = BKUHelper.removeAllWhitespace(xmlResponse.substring(sig_val_m_s.end(), sig_val_m_e.start()));
}
log.debug("sig_val = " + sig_val); //$NON-NLS-1$
// X509IssuerName
String iss_nam = null;
if (iss_nam_m_s.find() && iss_nam_m_e.find())
{
iss_nam = xmlResponse.substring(iss_nam_m_s.end(), iss_nam_m_e.start());
}
log.debug("iss_nam = " + iss_nam); //$NON-NLS-1$
// X509SerialNumber
String ser_num = null;
if (ser_num_m_s.find() && ser_num_m_e.find())
{
ser_num = BKUHelper.removeAllWhitespace(xmlResponse.substring(ser_num_m_s.end(), ser_num_m_e.start()));
}
log.debug("ser_num = " + ser_num); //$NON-NLS-1$
// SigningTime
String sig_tim = null;
if (sig_tim_m_s.find() && sig_tim_m_e.find())
{
sig_tim = xmlResponse.substring(sig_tim_m_s.end(), sig_tim_m_e.start());
}
log.debug("sig_tim = " + sig_tim); //$NON-NLS-1$
// CertDigest
// if (sig_cer_d_m_s.find() && sig_cer_d_m_e.find())
// {
// String cert_digest = xmlResponse.substring(sig_cer_d_m_s.end(),
// sig_cer_d_m_e.start());
// if (dig_val_m_s.find() && dig_val_m_e.find())
// {
// sig_dig = cert_digest.substring(dig_val_m_s.end(), dig_val_m_e.start());
// //sigObj.setX509CertificateDigest(sig_dig);
// }
// }
// X509Certificate
X509Certificate cert = null;
if (sig_cer_m_s.find() && sig_cer_m_e.find())
{
String sig_cer = BKUHelper.removeAllWhitespace(xmlResponse.substring(sig_cer_m_s.end(), sig_cer_m_e.start()));
try
{
byte[] der = CodingHelper.decodeBase64(sig_cer);
ByteArrayInputStream bais = new ByteArrayInputStream(der);
CertificateFactory cf = CertificateFactory.getInstance("X.509"); //$NON-NLS-1$
cert = (X509Certificate) cf.generateCertificate(bais);
bais.close();
}
catch (UnsupportedEncodingException e)
{
log.error(e);
throw new ConnectorException(300, e);
}
catch (CertificateException e)
{
log.error(e);
throw new ConnectorException(300, e);
}
catch (IOException e)
{
log.error(e);
throw new ConnectorException(300, e);
}
}
log.debug("X509Certificate = " + cert); //$NON-NLS-1$
if (log.isDebugEnabled())
{
String cert_iss = cert.getIssuerDN().getName();
log.debug("certificate's issuer = " + cert_iss); //$NON-NLS-1$
log.debug("response's issuer = " + iss_nam); //$NON-NLS-1$
log.debug("issuer matches = " + cert_iss.equals(iss_nam)); //$NON-NLS-1$
log.debug("ser number matches = " + cert.getSerialNumber().toString().equals(ser_num)); //$NON-NLS-1$
}
SignSignatureObject so = new SignSignatureObject();
so.date = sig_tim;
so.issuer = iss_nam;
so.signatureValue = sig_val;
so.x509Certificate = cert;
so.id = null;
return so;
}
/**
* Holds environment configuration information like templates.
*
* @author wprinz
*/
public static class Environment
{
/**
* The configuration key of the sign keybox identifier.
*/
protected static final String SIGN_KEY_IDENTIFIER_KEY = "moa.sign.KeyIdentifier"; //$NON-NLS-1$
/**
* The configuration key of the sign request template.
*/
protected static final String SIGN_REQUEST_TEMPLATE_KEY = "moa.sign.request.base64"; //$NON-NLS-1$
/**
* The configuration key of the sign URL.
*/
protected static final String SIGN_URL_KEY = "moa.sign.url"; //$NON-NLS-1$
/**
* The configuration key of the verify request template.
*/
protected static final String VERIFY_REQUEST_TEMPLATE_KEY = "moa.verify.request.base64"; //$NON-NLS-1$
/**
* The configuration key of the verify template.
*/
protected static final String VERIFY_TEMPLATE_KEY = "moa.verify.template.base64"; //$NON-NLS-1$
/**
* The configuration key of the verify URL.
*/
protected static final String VERIFY_URL_KEY = "moa.verify.url"; //$NON-NLS-1$
/**
* The configuration key of the trust profile id.
*/
protected static final String VERIFY_TRUST_PROFILE_ID = "moa.verify.TrustProfileID"; //$NON-NLS-1$
/**
* The configuration key for the ECDSA cert alg property.
*/
protected static final String ECDSA_CERT_ALG_KEY = "cert.alg.ecdsa"; //$NON-NLS-1$
/**
* The configuration key for the RSA cert alg property.
*/
protected static final String RSA_CERT_ALG_KEY = "cert.alg.rsa"; //$NON-NLS-1$
protected String sign_key_identifier = null;
protected String sign_request_template = null;
protected String sign_url = null;
protected String verify_request_template = null;
protected String verify_template = null;
protected String verify_url = null;
protected String verify_trust_profile_id = null;
protected String cert_alg_ecdsa = null;
protected String cert_alg_rsa = null;
/**
* Initializes the environment with a given profile.
*
* @param profile
* The configuration profile.
* @throws ConnectorException
* f.e.
*/
public Environment(String profile) throws ConnectorException
{
SettingsReader settings = null;
try
{
settings = SettingsReader.getInstance();
}
catch (SettingsException e)
{
throw new ConnectorException(300, e);
}
this.sign_key_identifier = getConnectorValueFromProfile(settings, profile, SIGN_KEY_IDENTIFIER_KEY);
String sign_request_filename = getConnectorValueFromProfile(settings, profile, SIGN_REQUEST_TEMPLATE_KEY);
this.sign_request_template = FileHelper.readFromFile(SettingsReader.relocateFile(sign_request_filename));
if (this.sign_request_template == null)
{
throw new ConnectorException(300, "Can not read the create xml request template"); //$NON-NLS-1$
}
this.sign_url = getConnectorValueFromProfile(settings, profile, SIGN_URL_KEY);
String verify_request_filename = getConnectorValueFromProfile(settings, profile, VERIFY_REQUEST_TEMPLATE_KEY);
this.verify_request_template = FileHelper.readFromFile(SettingsReader.relocateFile(verify_request_filename));
if (this.verify_request_template == null)
{
throw new ConnectorException(300, "Can not read the verify xml request template"); //$NON-NLS-1$
}
String verify_filename = getConnectorValueFromProfile(settings, profile, VERIFY_TEMPLATE_KEY);
this.verify_template = FileHelper.readFromFile(SettingsReader.relocateFile(verify_filename));
if (this.verify_template == null)
{
throw new ConnectorException(300, "Can not read the verify template"); //$NON-NLS-1$
}
this.verify_url = getConnectorValueFromProfile(settings, profile, VERIFY_URL_KEY);
this.verify_trust_profile_id = settings.getValueFromKey(VERIFY_TRUST_PROFILE_ID);
this.cert_alg_ecdsa = settings.getValueFromKey(ECDSA_CERT_ALG_KEY);
this.cert_alg_rsa = settings.getValueFromKey(RSA_CERT_ALG_KEY);
}
/**
* Returns the sign key identifier.
*
* @return Returns the sign key identifier.
*/
public String getSignKeyIdentifier()
{
return this.sign_key_identifier;
}
/**
* Returns the sign request template.
*
* @return Returns the sign request template.
*/
public String getSignRequestTemplate()
{
return this.sign_request_template;
}
/**
* Returns the sign URL.
*
* @return Returns the sign URL.
*/
public String getSignURL()
{
return this.sign_url;
}
/**
* Returns the verify request template.
*
* @return Returns the verify request template.
*/
public String getVerifyRequestTemplate()
{
return this.verify_request_template;
}
/**
* Returns the verify template.
*
* @return Returns the verify template.
*/
public String getVerifyTemplate()
{
return this.verify_template;
}
/**
* Returns the verify URL.
*
* @return Returns the verify URL.
*/
public String getVerifyURL()
{
return this.verify_url;
}
/**
* Returns the verify trust profile id.
*
* @return Returns the verify trust profile id.
*/
public String getVerifyTrustProfileId()
{
return this.verify_trust_profile_id;
}
/**
* Returns the ecdsa cert alg property.
*
* @return Returns the ecdsa cert alg property.
*/
public String getCertAlgEcdsa()
{
return this.cert_alg_ecdsa;
}
/**
* Returns the rsa cert alg property.
*
* @return Returns the rsa cert alg property.
*/
public String getCertAlgRsa()
{
return this.cert_alg_rsa;
}
/**
* Reads the configuration entry given by the key, first from the given
* profile, if not found from the defaults.
*
* @param settings
* The settings.
* @param profile
* The profile.
* @param key
* The configuration key.
* @return Returns the configuration entry.
*/
public static String getConnectorValueFromProfile(SettingsReader settings,
String profile, String key)
{
String value = settings.getValueFromKey("sig_obj." + profile + "." + key); //$NON-NLS-1$//$NON-NLS-2$
if (value == null)
{
value = settings.getValueFromKey(key);
}
return value;
}
}
}