/** * */ 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(""); //$NON-NLS-1$ Pattern iss_nam_p_s = Pattern.compile("<[\\w]*:?X509IssuerName>"); //$NON-NLS-1$ Pattern iss_nam_p_e = Pattern.compile(""); //$NON-NLS-1$ Pattern sig_tim_p_s = Pattern.compile("<[\\w]*:?SigningTime>"); //$NON-NLS-1$ Pattern sig_tim_p_e = Pattern.compile(""); //$NON-NLS-1$ Pattern ser_num_p_s = Pattern.compile("<[\\w]*:?X509SerialNumber>"); //$NON-NLS-1$ Pattern ser_num_p_e = Pattern.compile(""); //$NON-NLS-1$ Pattern sig_cer_p_s = Pattern.compile("<[\\w]*:?X509Certificate>"); //$NON-NLS-1$ Pattern sig_cer_p_e = Pattern.compile(""); //$NON-NLS-1$ // Pattern sig_cer_d_p_s = Pattern.compile("<[\\w]*:?CertDigest>"); // //$NON-NLS-1$ // Pattern sig_cer_d_p_e = Pattern.compile(""); // //$NON-NLS-1$ // Pattern dig_val_p_s = Pattern.compile("<[\\w]*:?DigestValue>"); // //$NON-NLS-1$ // Pattern dig_val_p_e = Pattern.compile(""); // //$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; } } }