From af5d23c30f773e275bfbe630d51fbcc7464ded9b Mon Sep 17 00:00:00 2001 From: ferbas Date: Tue, 27 Oct 2009 10:47:09 +0000 Subject: added support for dynamic algorithm suites / acos04 git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@348 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c --- .../egiz/pdfas/algorithmSuite/AlgorithmMapper.java | 120 +++++++++++++ .../pdfas/algorithmSuite/AlgorithmSuiteObject.java | 141 +++++++++++++++ .../pdfas/algorithmSuite/AlgorithmSuiteUtil.java | 193 +++++++++++++++++++++ 3 files changed, 454 insertions(+) create mode 100644 src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmMapper.java create mode 100644 src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmSuiteObject.java create mode 100644 src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmSuiteUtil.java (limited to 'src') diff --git a/src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmMapper.java b/src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmMapper.java new file mode 100644 index 0000000..8af0db2 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmMapper.java @@ -0,0 +1,120 @@ +package at.gv.egiz.pdfas.algorithmSuite; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Utility for mapping signature and hash algorithms between URI and abbreviation.
+ * Supported algorithms are defined in external resource {@value #SUPPORTED_ALGORITHMS_RESOURCE}
+ * + * e.g. sha1 <-> http://www.w3.org/2000/09/xmldsig#sha1 + * + * @author dferbas + * + */ + +public class AlgorithmMapper { + private static final String SUPPORTED_ALGORITHMS_RESOURCE = "supportedAlgorithms.txt"; + private static Map algorithmMap = new HashMap(); + private static Log log = LogFactory.getLog(AlgorithmMapper.class); + + /** + * read available algorithms from resource + */ + static { + try { + log.debug("reading supported algorithms from " + SUPPORTED_ALGORITHMS_RESOURCE); + List lines = IOUtils.readLines(AlgorithmMapper.class.getResourceAsStream(SUPPORTED_ALGORITHMS_RESOURCE)); + for (int i = 0; i < lines.size(); i++) { + String uri = (String)lines.get(i); + if (isValidLine(uri)) { + String abbr = parseAbbreviation(uri); + algorithmMap.put(abbr, uri); + if (log.isDebugEnabled()) { + log.debug("added supported algorithm: " + abbr + "=" + uri); + } + } + } + } catch (IOException e) { + throw new RuntimeException("error loading supportetAlgorithms.properties file", e); + } + } + + /** + * Get algorithm abbreviation (equals last part after namespace#) from uri
+ * + * e.g. sha1 from http://www.w3.org/2000/09/xmldsig#sha1 + * + * @param algorithmUri algorithm URI + * @return algorithm abbreviation + */ + public static String getAbbreviation(String algorithmUri) { + if (!algorithmMap.containsValue(algorithmUri)) { + throw new RuntimeException("unsupported Algorithm " + algorithmUri); + } + return parseAbbreviation(algorithmUri); + } + + private static boolean isValidLine(String uri) { + if (uri == null || uri.length() == 0) return false; + if (uri.trim().startsWith("#")) return false; + return true; + } + + /** + * Get valid URI from algorithm abbreviation
+ * + * e.g. http://www.w3.org/2000/09/xmldsig#sha1 from sha1 + * + * @param algorithmShort + * @return URI + */ + public static String getUri(String algorithmShort) { + if (!algorithmMap.containsKey(algorithmShort)) { + throw new RuntimeException("unsupported Algorithm " + algorithmShort); + } + return (String)algorithmMap.get(algorithmShort); + } + + /** + * Get hash (abbreviation) from signature method (eg. sha1 from ecdsa-sha1). + * @param suite abbreviation or full qualified signature method + * @return digest method abbreviation + */ + public static String getHashAbbrFromSuite(String suite) { + try { + int pos = suite.lastIndexOf('-'); + return suite.substring(pos + 1); + } catch (Exception e) { + throw new RuntimeException("cannot parse algorithm string for hash method: " + suite); + } + } + + /** + * + * Get hash method (URI) from signature method + * @param suite abbreviation or full qualified signature method + * @return digest method URI + * + */ + public static String getHashUriFromSuite(String suite) { + return getUri(getHashAbbrFromSuite(suite)); + } + + private static String parseAbbreviation(String algorithmUri) { + try { + int pos = algorithmUri.lastIndexOf('#'); + return algorithmUri.substring(pos + 1); + } catch (Exception e) { + throw new RuntimeException("invalid algorithm entry in " + SUPPORTED_ALGORITHMS_RESOURCE + + ": " + algorithmUri); + } + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmSuiteObject.java b/src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmSuiteObject.java new file mode 100644 index 0000000..d95516e --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmSuiteObject.java @@ -0,0 +1,141 @@ +package at.gv.egiz.pdfas.algorithmSuite; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Defines a used algorithm suite. Contains a {@link #signatureMethod} a + * {@link #dataDigestMethod} a {@link #propertiesDigestMethod} and a + * {@link #certDigestMethod}.
+ * + * This structured object is representing a string like + * ecdsa-sha256:sha256:sha256:ripemd160
+ * meaning + * signatureMethod:dataDigestMethod:propertiesDigestMethod:certDigestMethod + * + * @author dferbas + * + */ +public class AlgorithmSuiteObject { + private static Log log = LogFactory.getLog(AlgorithmSuiteObject.class); + + private String signatureMethod; + private String dataDigestMethod; + private String propertiesDigestMethod; + private String certDigestMethod; + + /** + * Create emtpy algorithm suite object + */ + public AlgorithmSuiteObject() { + + } + + /** + * Create object from parameter string like + * etsi-moc-1.2:ecdsa-sha1:ripemd160@207c44ff + * + * @param parameterString + */ + public AlgorithmSuiteObject(String parameterString) { + parseFrom(parameterString); + } + + /** + * Initializes object from parameter string like + * etsi-moc-1.2:ecdsa-sha1:ripemd160@207c44ff + * + * @param parameterString + */ + public void parseFrom(String parameterString) { + log.debug("parsing algorithmSuite from " + parameterString); + if (parameterString != null) { + parameterString = parameterString.split("@")[0]; + String[] arr = parameterString.split(":"); + if (arr.length > 1) { + this.signatureMethod = arr[1]; + this.dataDigestMethod = this.propertiesDigestMethod = this.certDigestMethod = AlgorithmMapper + .getHashAbbrFromSuite(arr[1]); + + if (arr.length > 2) { + this.dataDigestMethod = this.propertiesDigestMethod = this.certDigestMethod = arr[2]; + } + if (arr.length > 3) { + this.propertiesDigestMethod = this.certDigestMethod = arr[3]; + } + if (arr.length > 4) { + this.certDigestMethod = arr[4]; + } + log.debug("successfully parsed to: " + this.toString()); + } + } + } + + /** + * Returns if algorithm object is valid specified (values are available) + * + * @return + */ + public boolean isSpecified() { + return this.signatureMethod != null; + } + + /** + * Signature method abbreviation. E.g. rsa-sha1, ecdsa-sha256 + * + * @return + */ + public String getSignatureMethod() { + return this.signatureMethod; + } + + /** + * Digest method for data (document). E.g. sha1, md5 + * + * @return + */ + public String getDataDigestMethod() { + return this.dataDigestMethod; + } + + /** + * Digest method for properties. E.g. sha1, md5 + * + * @return + */ + public String getPropertiesDigestMethod() { + return this.propertiesDigestMethod; + } + + /** + * Digest method for certificate digest. E.g. sha1, md5 + * + * @return + */ + public String getCertDigestMethod() { + return this.certDigestMethod; + } + + public void setSignatureMethod(String signatureMethod) { + this.signatureMethod = signatureMethod; + } + + public void setDataDigestMethod(String dataDigestMethod) { + this.dataDigestMethod = dataDigestMethod; + } + + public void setPropertiesDigestMethod(String propertiesDigestMethod) { + this.propertiesDigestMethod = propertiesDigestMethod; + } + + public void setCertDigestMethod(String certDigestMethod) { + this.certDigestMethod = certDigestMethod; + } + + public String toString() { + return "AlgorithmSuiteObject [certDigestMethod=" + this.certDigestMethod + + ", dataDigestMethod=" + this.dataDigestMethod + ", propertiesDigestMethod=" + + this.propertiesDigestMethod + ", signatureMethod=" + this.signatureMethod + "]"; + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmSuiteUtil.java b/src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmSuiteUtil.java new file mode 100644 index 0000000..0a1a5e4 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmSuiteUtil.java @@ -0,0 +1,193 @@ +package at.gv.egiz.pdfas.algorithmSuite; + +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +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.sig.connectors.ConnectorEnvironment; +import at.knowcenter.wag.egov.egiz.sig.connectors.TemplateReplaces; +import at.knowcenter.wag.egov.egiz.sig.connectors.bku.SignSignatureObject; + +/** + * Utility class for handling dynamic algorithm suites + * + * @author dferbas + * + */ +public class AlgorithmSuiteUtil { + private static Log log = LogFactory.getLog(AlgorithmSuiteUtil.class); + + /** + * Creates new verify_xml, evaluates algorithm suite and replaces methods in verify_xml + * + * @param algSuite out-param empty algorithm suite object + * @param environment connector environment to load verify template + * @param so signsignator object + * @return verify xml + */ + public static String evaluateReplaceAlgs(AlgorithmSuiteObject algSuite, ConnectorEnvironment environment, SignSignatureObject so) { + + String verify_template = environment.getVerifyTemplate(); + + String cert_alg; + String verify_xml = null; + X509Certificate cert = so.getX509Certificate(); + String ids_string = so.getSigID(); + + //AlgorithmSuiteObject algSuite = new AlgorithmSuiteObject(ids_string); + algSuite.parseFrom(ids_string); + if (algSuite.isSpecified()) { + log.debug("Algorithm suite defined as parameter. Using new dynamic template replacement."); + cert_alg = AlgorithmMapper.getUri(algSuite.getSignatureMethod()); + + } else { + log.debug("NO algorithm suite defined as parameter. Using old static algorithm."); + + cert_alg = environment.getCertAlgEcdsa(); + if (cert.getPublicKey().getAlgorithm().indexOf("RSA") >= 0) //$NON-NLS-1$ + { + cert_alg = environment.getCertAlgRsa(); + } + + // fix digest methods to sha1 for old algorithm + String oldDigest = "sha1"; + algSuite.setCertDigestMethod(oldDigest); + algSuite.setDataDigestMethod(oldDigest); + algSuite.setPropertiesDigestMethod(oldDigest); + + } + + // cert alg replace + verify_xml = verify_template.replaceFirst(TemplateReplaces.CERT_ALG_REPLACE, cert_alg); + + // digest method replaces + verify_xml = verify_xml.replaceFirst(TemplateReplaces.DATA_DIGEST_REPLACE, + AlgorithmMapper.getUri(algSuite.getDataDigestMethod())); + verify_xml = verify_xml.replaceFirst(TemplateReplaces.PROPERTIES_DIGEST_REPLACE, + AlgorithmMapper.getUri(algSuite.getPropertiesDigestMethod())); + verify_xml = verify_xml.replaceFirst(TemplateReplaces.CERT_DIGEST_REPLACE, + AlgorithmMapper.getUri(algSuite.getCertDigestMethod())); + return verify_xml; + } + + + /** + * Extract algorithm suite as optimized string from CreateXMLSignatureResponse + * + * @param xmlResponse + * @return algorith suite string + */ + public static String extractAlgorithmSuiteString(String xmlResponse) { + + String elem = findFirstElement(xmlResponse, "SignatureMethod"); + System.err.println(elem); + String alg = findAttributeValue(elem, "Algorithm"); + System.err.println(alg); + + List digAlgs = findAllAttributeValues(xmlResponse, "DigestMethod", "Algorithm"); + + digAlgs.add(0, AlgorithmMapper.getHashUriFromSuite(alg)); + + reduceDigestAlgs(digAlgs); + + digAlgs.remove(0); + + return createSigDevString(alg, digAlgs); + + } + + public static boolean isDefaultCertAlg(String algsString, String defaultCertAlg) { + return AlgorithmMapper.getAbbreviation(defaultCertAlg).equals(algsString); + } + + private static String createSigDevString(String suite, List digAlgs) { + StringBuilder sb = new StringBuilder(AlgorithmMapper.getAbbreviation(suite)); + for (Iterator iterator = digAlgs.iterator(); iterator.hasNext();) { + String dig = (String) iterator.next(); + sb.append(":").append(AlgorithmMapper.getAbbreviation(dig)); + } + return sb.toString(); + } + + private static String findAttributeValue(String elemContent, String attrName) { + + String sig_alg = removeAllWhitespace(elemContent); + + attrName += "=\""; + int start = sig_alg.indexOf(attrName) + attrName.length(); + int end = sig_alg.indexOf("\"", start); + return sig_alg.substring(start, end); + + } + + private static String findFirstElement(String xml, String elemName) { + Pattern p = Pattern.compile("<[\\w]*:?" + elemName); + Matcher m = p.matcher(xml); + if (m.find()) { + int start = m.start(); + int end = xml.indexOf("/>", start) + 2; + return xml.substring(start, end); + } + return null; + } + + private static List findElements(String xml, String elemName) { + Pattern p = Pattern.compile("<[\\w]*:?" + elemName); + Matcher m = p.matcher(xml); + ArrayList res = new ArrayList(); + while (m.find()) { + int start = m.start(); + int end = xml.indexOf("/>", start) + 2; + res.add(xml.substring(start, end)); + } + return res; + } + + private static String findAttributeValue(String xmlContent, String elemName, String attrName) { + String elemCont = findFirstElement(xmlContent, elemName); + return findAttributeValue(elemCont, attrName); + } + + private static List findAllAttributeValues(String xmlContent, String elemName, String attrName) { + List elemConts = findElements(xmlContent, elemName); + ArrayList res = new ArrayList(); + for (int i = 0; i < elemConts.size(); i++) { + res.add(findAttributeValue((String) elemConts.get(i), attrName)); + } + return res; + } + + /** + * Helper method to remove whitespaces from a string + * @param str + * @return + */ + public static String removeAllWhitespace(String str) { + return str.replaceAll("\\s", ""); //$NON-NLS-1$ //$NON-NLS-2$ + } + + private static void reduceDigestAlgs(List digestAlgs) { + if (digestAlgs == null) + return; + reduceDigestAlgsRec(digestAlgs, digestAlgs.size() - 1); + } + + private static void reduceDigestAlgsRec(List digestAlgs, int act) { + if (act <= 0) + return; + else if (digestAlgs.get(act - 1).equals(digestAlgs.get(act))) { + // reduce + digestAlgs.remove(act); + reduceDigestAlgsRec(digestAlgs, act - 1); + } else { + return; + } + } + +} -- cgit v1.2.3