diff options
author | tknall <tknall@7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c> | 2010-12-01 15:55:48 +0000 |
---|---|---|
committer | tknall <tknall@7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c> | 2010-12-01 15:55:48 +0000 |
commit | 11f710e10173eed45c7c4aa3b929aa890ee8a850 (patch) | |
tree | e6896c0946d5fa243f1c9cb0df5c44998efd9ed0 /src/main | |
parent | a214f9a279fc764da26f2c79505ef2835b375d57 (diff) | |
download | pdf-as-3-11f710e10173eed45c7c4aa3b929aa890ee8a850.tar.gz pdf-as-3-11f710e10173eed45c7c4aa3b929aa890ee8a850.tar.bz2 pdf-as-3-11f710e10173eed45c7c4aa3b929aa890ee8a850.zip |
Workaround implemented regarding a bug with RFC2253 names where RDNs were not resolved at signing time (resulting from a BKU that could not resolve the respective OIDs):
e.g. 2.5.4.5=#1306323030383034,CN=Foreigner CA,C=BE
The example shows a RDN "2.5.4.5" which should have been resolved to "serialNumber" at signing time.
Imagine a line break between "Foreigner" and "CA" within the signature block. After text extraction the Name looks like as follows:
2.5.4.5=#1306323030383034, CN=ForeignerCA,C=BE
We recognize that the name shows spaces prior to RDNs and that the space 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 at verification time, so that taking the name from certificate on verification time, may not result in the name we had at signing time.
This workaround merges 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>).
git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@665 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c
Diffstat (limited to 'src/main')
-rw-r--r-- | src/main/java/at/knowcenter/wag/egov/egiz/sig/SignatureObject.java | 215 |
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;
|