package at.knowcenter.wag.egov.egiz.sig.connectors.mocca;
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.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.exceptions.ConnectorException;
import at.knowcenter.wag.egov.egiz.sig.SignatureObject;
import at.knowcenter.wag.egov.egiz.sig.X509Cert;
import at.knowcenter.wag.egov.egiz.sig.connectors.bku.BKUHelper;
import at.knowcenter.wag.egov.egiz.sig.connectors.bku.SignSignatureObject;
import at.knowcenter.wag.egov.egiz.sig.sigid.IdFormatter;
import at.knowcenter.wag.egov.egiz.tools.CodingHelper;
/**
* Provides useful methods for the usage of the open source cce mocca.
*
* @author tknall
*/
public final class MOCCAHelper {
/**
* Prevents this plain util class from being instantiated.
*/
private MOCCAHelper() {
}
/**
* The logging implementation.
*/
private final static Log log = LogFactory.getLog(MOCCAHelper.class);
/**
* The pattern that identifies a mocca response (that matches the header value "Server" or "User-Agent").
*/
private final static Pattern MOCCA_PATTERN = Pattern.compile("(citizen-card-environment/\\d+(\\.\\d+) MOCCA[ /].*)|(Jetty(.*))");
/**
* Checks if the given header value for "Server" or "User-Agent" respectively indicates that
* the response was from a mocca cce.
* @param cceId The value of the http header "Server" or "User-Agent".
* @see http://www.buergerkarte.at/konzept/securitylayer/spezifikation/aktuell/bindings/bindings.html#http.kodierung
* @return true
if the id points to a mocca response, false
if not.
*/
public final static boolean isMOCCACCEId(String cceId) {
if (cceId == null) {
return false;
}
return MOCCA_PATTERN.matcher(cceId).matches();
}
/**
* This method parses the signature creation response of the signature
* device mocca.
*
* @param xmlResponse The response string.
* @return Returns the parsed signature object holding the data.
* @see SignatureObject
* @see CodingHelper
* @see X509Cert
*/
public final static SignSignatureObject parseCreateXMLResponse(String xmlResponse, IdFormatter id_formatter) throws ConnectorException {
if (log.isDebugEnabled()) {
log.debug("xmlResponse = " + xmlResponse);
}
Pattern iss_nam_p_s = Pattern.compile("<[\\w]*:?X509IssuerName[^>]*>");
Pattern iss_nam_p_e = Pattern.compile("[\\w]*:?X509IssuerName>");
Pattern sig_tim_p_s = Pattern.compile("<[\\w]*:?SigningTime>");
Pattern sig_tim_p_e = Pattern.compile("[\\w]*:?SigningTime>");
Pattern ser_num_p_s = Pattern.compile("<[\\w]*:?X509SerialNumber[^>]*>");
Pattern ser_num_p_e = Pattern.compile("[\\w]*:?X509SerialNumber>");
Pattern sig_cer_p_s = Pattern.compile("<[\\w]*:?X509Certificate>");
Pattern sig_cer_p_e = Pattern.compile("[\\w]*:?X509Certificate>");
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);
// SignatureValue
String sig_val = null;
Matcher signatureValueMatcher = Pattern.compile("<(\\w+:)?SignatureValue( Id=\"[\\w-]+\")?>\\s*(.*)\\s*(\\w+:)?SignatureValue>").matcher(xmlResponse);
if (signatureValueMatcher.find()) {
sig_val = signatureValueMatcher.group(3);
}
log.debug("sig_val = " + sig_val);
// 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);
// 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);
// 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);
// 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");
cert = (X509Certificate) cf.generateCertificate(bais);
bais.close();
} catch (UnsupportedEncodingException e) {
throw new ConnectorException(300, e);
} catch (CertificateException e) {
throw new ConnectorException(300, e);
} catch (IOException e) {
throw new ConnectorException(300, e);
}
}
log.debug("X509Certificate = " + cert);
if (log.isDebugEnabled()) {
String cert_iss = cert.getIssuerDN().getName();
log.debug("certificate's issuer = " + cert_iss);
log.debug("response's issuer = " + iss_nam);
log.debug("issuer matches = " + cert_iss.equals(iss_nam));
log.debug("ser number matches = " + cert.getSerialNumber().toString().equals(ser_num));
}
// extract Signature Id's
String[] ids = extractIds(xmlResponse);
String final_ids = id_formatter.formatIds(ids);
SignSignatureObject so = new SignSignatureObject();
so.date = sig_tim;
so.issuer = iss_nam;
so.signatureValue = sig_val;
so.x509Certificate = cert;
so.id = final_ids;
return so;
}
/**
* Extraction of the id attributes from the xml response.
*
* @param xmlResponse The xml response.
* @return The parsed id attributes.
*/
public final static String[] extractIds(String xmlResponse) {
return new String[] { extractId(xmlResponse) };
}
/**
* There is only one special common part of all id attributes of this
* connector that has to be stored. This method returns that single part.
*
* @param xmlResponse The xml response.
* @return The parsed common part of all id attributes.
*/
private final static String extractId(String xmlResponse) {
final Pattern ID_PATTERN = Pattern.compile("Id\\s*=\\s*\"\\s*Signature-([\\p{XDigit}]+)-\\d+\\s*\"");
Matcher matcher = ID_PATTERN.matcher(xmlResponse);
if (matcher.find() && matcher.groupCount() > 0) {
return matcher.group(1);
}
return null;
}
}