aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/sig/SignatureObject.java215
1 files changed, 211 insertions, 4 deletions
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/sig/SignatureObject.java b/src/main/java/at/knowcenter/wag/egov/egiz/sig/SignatureObject.java
index 25e996e..4d69e4c 100644
--- a/src/main/java/at/knowcenter/wag/egov/egiz/sig/SignatureObject.java
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/sig/SignatureObject.java
@@ -18,6 +18,7 @@
package at.knowcenter.wag.egov.egiz.sig;
import iaik.asn1.structures.Name;
+import iaik.asn1.structures.RDN;
import iaik.utils.RFC2253NameParser;
import iaik.utils.RFC2253NameParserException;
@@ -35,8 +36,10 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
+import java.util.StringTokenizer;
import java.util.Vector;
+import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import at.knowcenter.wag.egov.egiz.PdfASID;
@@ -343,8 +346,8 @@ public class SignatureObject implements Serializable
}
/**
- * This method adds an signaton value to the entry cache. If a key is not in
- * the cache a new signature entry is createad. Therefor the method return
+ * This method adds an sig value to the entry cache. If a key is not in
+ * the cache a new signature entry is created. Therefore the method return
* true. <br>
* The value that has to be set would be normalized! <br>
* <b>If the key equals to <code>SIG_VALUE</code> all whitespaces are
@@ -581,6 +584,190 @@ public class SignatureObject implements Serializable
}
/**
+ * This method removes whitespaces around RDNs. Whitespaces may be assumed by the algorithm that
+ * re-merges multiple lines from a binary signature when line breaks occur after commas. Without
+ * correction this will result in broken signatures.<br/>
+ * e.g this
+ * invalid IssuerName (note the space before the second RND CN):
+ * <code>serialNumber=863532247989, CN=BMUKK - Amtssignatur Schulen,OU=Abt. IT/2,O=Bundesministerium für Unterricht, Kunst und Kultur,C=AT</code>
+ * will be normalized to:
+ * <code>serialNumber=863532247989,CN=BMUKK - Amtssignatur Schulen,OU=Abt. IT/2,O=Bundesministerium für Unterricht, Kunst und Kultur,C=AT</code>
+ * @param The invalid RFC2253 name as string.
+ * @return The normalized RFC2253 name without spaces prior to RDNs.
+ */
+ public static String prepareRFC2253Name(String name) {
+ if (name == null) {
+ return null;
+ }
+ StringTokenizer tokenizer = new StringTokenizer(name, ",", false);
+ StringBuffer result = new StringBuffer();
+ // iterate over all alleged RND=value-pairs
+ while (tokenizer.hasMoreTokens()) {
+ String rdnExpression = tokenizer.nextToken();
+ try {
+ // try to parse RDN=value
+ new RFC2253NameParser(rdnExpression.trim()).parse();
+ // rdnExpression is a RDN=value pair -> remove whitespaces before and after RDN=value
+ rdnExpression = rdnExpression.trim();
+ } catch (RFC2253NameParserException e) {
+ // this is not a RDN=value pair
+ // e.g. " Kunst und Kultur" from the javadoc example
+ // do not trim, otherwise resulting RFC2253Name will be invalid
+ }
+ // re-insert delimiter
+ if (result.length() > 0) {
+ result.append(",");
+ }
+ // add token (either trimmed RND=value pair, or not trimmed text token)
+ result.append(rdnExpression);
+ }
+ String cleanedName = result.toString();
+ if (logger_.isDebugEnabled()) {
+ logger_.debug("Cleaning RFC2253 name: \"" + name + "\" -> \"" + cleanedName + "\".");
+ }
+ return cleanedName;
+ }
+
+ /**
+ * This method depicts a workaround for a bug with RFC2253 names with RDNs that have not been
+ * resolved from ObjectID at signing time (this results from a BKU that could not resolve
+ * the respective OID).<br/>
+ * e.g. <code>2.5.4.5=#1306323030383034, CN=ForeignerCA,C=BE</code><br/>
+ * The example above shows a RDN "2.5.4.5" which should have been resolved to "serialNumber" at
+ * signing time. We also recognize that the name shows spaces prior to RDNs and that the space
+ * which between "Foreigner" and "CA" is missing due to text extraction/reconstruction.
+ * The naive approach would be to take the complete RFC2253 name from the certificate, since that
+ * name has also been used for signature. But this does not work in some cases because while
+ * the bku was not able to resolve 2.5.4.5 on signing time, the entity invoking pdfas for
+ * verification might be, so that taking the name from certificate on verification time, may not
+ * result in the name we had at signing time.<br/>
+ * e.g. at signing time: <code>2.5.4.5=#1306323030383034,CN=Foreigner CA,C=BE</code><br/>
+ * after text extraction: <code>2.5.4.5=#1306323030383034, CN=ForeignerCA,C=BE</code><br/>
+ * from certificate: <code>serialNumber=863532247989,CN=Foreigner CA,C=BE</code><br/>
+ * This method provides a workaround for that problem, by merging information from text extraction
+ * with information from the certificate. The method takes all RDNs from the extracted text and
+ * merges them with the values from the certificate (considering the case where the textual
+ * version shows BER encoded values (e.g. <code>#1306323030383034</code>).
+ * @param nameFromText The extracted RFC2253 name from the text (e.g. <code>2.5.4.5=#1306323030383034, CN=ForeignerCA,C=BE</code>).
+ * @param nameFromCertificate The RFC2253 name from the certificate (e.g. <code>serialNumber=863532247989,CN=Foreigner CA,C=BE</code>)
+ * @return The RFC2253 name that was used for signature (e.g. <code>2.5.4.5=#1306323030383034,CN=Foreigner CA,C=BE</code>).
+ */
+ public static String prepareRFC2253Name(String nameFromText, String nameFromCertificate) {
+
+ // do not invoke the workaround for performance reasons when both the extracted name and the
+ // name from certificate are equal
+ if (StringUtils.equals(nameFromText, nameFromCertificate)) {
+ return nameFromText;
+ }
+
+ logger_.debug("Checking RFC2253 name.");
+
+ // if we do not have a name from certificate just return the name from text
+ if (nameFromCertificate == null) {
+ logger_.debug("No certificate RFC2253 name provided. Applying less sophisticated workaround (does not cover all cases) without certificate usage.");
+ return prepareRFC2253Name(nameFromText);
+ }
+
+ // no name from text extraction available, just return name from certificate
+ if (nameFromText == null) {
+ logger_.debug("No extracted/reconstructed name available. Just returning the name from certificate: \"" + nameFromCertificate + "\".");
+ return nameFromCertificate;
+ }
+
+ // helper class
+ final class RDNValuePair {
+
+ private String rdn;
+ private String value;
+
+ public RDNValuePair(String rdn, String value) {
+ this.rdn = rdn;
+ this.value = value;
+ }
+
+ public String getRdn() {
+ return this.rdn;
+ }
+
+ public String getValue() {
+ return this.value;
+ }
+
+ public String toString() {
+ return rdn + "=" + value;
+ }
+ }
+
+ // retrieve RDNs from text based name
+ List rdnList = new ArrayList();
+ StringTokenizer tokenizer = new StringTokenizer(nameFromText, ",", false);
+ while (tokenizer.hasMoreTokens()) {
+ String rdnExpression = tokenizer.nextToken().trim();
+ try {
+ new RFC2253NameParser(rdnExpression).parse();
+ // token is RDN=value pair
+ // split RDN from value
+ String[] split = rdnExpression.split("=", 2);
+ rdnList.add(new RDNValuePair(split[0].trim(), split[1].trim()));
+ } catch (RFC2253NameParserException e) {
+ // no RDN in token
+ }
+ }
+
+ // get values from certificate name
+ Name nCert;
+ try {
+ nCert = new RFC2253NameParser(nameFromCertificate).parse();
+ } catch (RFC2253NameParserException e) {
+ // should never happen
+ logger_.warn("Unable to parse RFC2253 name \"" + nameFromCertificate + "\". Applying less sophisticated workaround (does not cover all cases) without certificate usage.");
+ return prepareRFC2253Name(nameFromText);
+ }
+ RDN[] values = nCert.getRDNs();
+
+ // check if results are mergeable
+ if (values.length != rdnList.size()) {
+ // unable to merge names; returning nameFromCertificate (since this should be normal
+ // behavior)
+ logger_.warn("Number of parsed text based RDNs from \"" + nameFromText + "\" does not fit the number of RDN values from certificate name \"" + nameFromCertificate + "\". Returning name from certificate.");
+ return nameFromCertificate;
+ }
+
+ // merge textual based RDNs with values from certificate
+ StringBuffer result = new StringBuffer();
+ for (int i = 0; i < values.length; i++) {
+ if (i > 0) {
+ result.append(",");
+ }
+ // take rdn from textual representation
+ RDNValuePair rdnVP = (RDNValuePair) rdnList.get(i);
+ result.append(rdnVP.getRdn()).append("=");
+ // take value from certificate but make sure that we do not have a BER encoding
+ if (rdnVP.getValue().startsWith("#")) {
+ // BER encoding -> take value from text representation
+ result.append(rdnVP.getValue());
+ } else {
+ // no BER encoding -> take value from certificate
+ result.append(values[values.length - 1 - i].getAVA().getValueAsString());
+ }
+ }
+ String merged = result.toString();
+ if (logger_.isDebugEnabled()) {
+ if (merged.equals(nameFromText)) {
+ logger_.debug("Taking name from text: \"" + nameFromText + "\"");
+ } else if (merged.equals(nameFromCertificate)) {
+ logger_.debug("Taking name from certificate: \"" + nameFromText + "\"");
+ } else {
+ logger_.debug("Name has been fixed.");
+ logger_.debug("Name from text : \"" + nameFromText + "\"");
+ logger_.debug("Name from certificate : \"" + nameFromCertificate + "\"");
+ logger_.debug("Fixed name : \"" + merged + "\"");
+ }
+ }
+ return merged;
+ }
+
+ /**
* @return Returns the SignationIssuer.
*/
public String getSignationIssuer()
@@ -589,7 +776,17 @@ public class SignatureObject implements Serializable
X509Cert cert = loadCertificate(getSigValue(SignatureTypes.SIG_NUMBER), issuer);
if (cert != null)
{
- setSigValue(SignatureTypes.SIG_ISSUER, cert.getIssuerName());
+ // merge RDNs from file with values from certificate
+ if (getSigValue(SignatureTypes.SIG_ISSUER) != null) {
+ this.setSignationIssuer(prepareRFC2253Name(getSigValue(SignatureTypes.SIG_ISSUER), cert.getIssuerName()));
+ } else {
+ this.setSignationIssuer(cert.getIssuerName());
+ }
+ /*
+ if (getSigValue(SignatureTypes.SIG_ISSUER) == null) {
+ this.setSignationIssuer(cert.getIssuerName());
+ }
+ */
setSigValue(SIG_CER, cert.getCertString());
// setSigValue(SIG_CER_DIG, cert.getCertDigest());
x509Cert_ = cert;
@@ -689,7 +886,17 @@ public class SignatureObject implements Serializable
X509Cert cert = loadCertificate(getSignationSerialNumber(), getSignationIssuer());
if (cert != null)
{
- setSigValue(SignatureTypes.SIG_ISSUER, cert.getIssuerName());
+ // merge RDNs from file with values from certificate
+ if (getSigValue(SignatureTypes.SIG_ISSUER) != null) {
+ this.setSignationIssuer(prepareRFC2253Name(getSigValue(SignatureTypes.SIG_ISSUER), cert.getIssuerName()));
+ } else {
+ this.setSignationIssuer(cert.getIssuerName());
+ }
+ /*
+ if (getSigValue(SignatureTypes.SIG_ISSUER) == null) {
+ this.setSignationIssuer(cert.getIssuerName());
+ }
+ */
setSigValue(SIG_CER, cert.getCertString());
// setSigValue(SIG_CER_DIG, cert.getCertDigest());
x509Cert_ = cert;