Author: bago
Date: Sun Oct 11 20:04:57 2009
New Revision: 824132
URL: http://svn.apache.org/viewvc?rev=824132&view=rev
Log:
Refactored SignatureRecord and its Impl to better support signing/verification.
It now supports toString representation and "unsigned" representation.
Modified:
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/DKIMSign.java
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/DKIMCommon.java
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/DKIMSigner.java
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/DKIMVerifier.java
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/api/SignatureRecord.java
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/PublicKeyRecordImpl.java
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/TagValue.java
Modified:
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/DKIMSign.java
URL:
http://svn.apache.org/viewvc/james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/DKIMSign.java?rev=824132&r1=824131&r2=824132&view=diff
==============================================================================
---
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/DKIMSign.java
(original)
+++
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/DKIMSign.java
Sun Oct 11 20:04:57 2009
@@ -120,7 +120,7 @@
public void service(Mail mail) throws MessagingException {
DKIMSigner signer = new DKIMSigner(signatureTemplate, privateKey);
SignatureRecord signRecord = signer
- .newSignatureRecord(signatureTemplate);
+ .newSignatureRecordTemplate(signatureTemplate);
try {
BodyHasher bhj = signer.newBodyHasher(signRecord);
MimeMessage message = mail.getMessage();
Modified:
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/DKIMCommon.java
URL:
http://svn.apache.org/viewvc/james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/DKIMCommon.java?rev=824132&r1=824131&r2=824132&view=diff
==============================================================================
--- james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/DKIMCommon.java
(original)
+++ james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/DKIMCommon.java
Sun Oct 11 20:04:57 2009
@@ -60,7 +60,7 @@
}
protected static void signatureCheck(Headers h, SignatureRecord sign,
- List headers, String signatureStub, Signature signature)
+ List headers, Signature signature)
throws SignatureException, PermFailException {
boolean relaxedHeaders = SignatureRecord.RELAXED.equals(sign
@@ -97,6 +97,7 @@
}
}
+ String signatureStub = "DKIM-Signature:" + sign.toUnsignedString();
updateSignature(signature, relaxedHeaders, "dkim-signature",
signatureStub);
}
Modified:
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/DKIMSigner.java
URL:
http://svn.apache.org/viewvc/james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/DKIMSigner.java?rev=824132&r1=824131&r2=824132&view=diff
==============================================================================
--- james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/DKIMSigner.java
(original)
+++ james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/DKIMSigner.java
Sun Oct 11 20:04:57 2009
@@ -52,7 +52,7 @@
this.signatureRecordTemplate = signatureRecordTemplate;
}
- public SignatureRecord newSignatureRecord(String record) {
+ public SignatureRecord newSignatureRecordTemplate(String record) {
return new SignatureRecordImpl(record);
}
@@ -70,7 +70,7 @@
throw new PermFailException("MIME parsing exception: "
+ e1.getMessage(), e1);
}
- SignatureRecord srt = newSignatureRecord(signatureRecordTemplate);
+ SignatureRecord srt =
newSignatureRecordTemplate(signatureRecordTemplate);
try {
BodyHasher bhj = newBodyHasher(srt);
@@ -92,10 +92,9 @@
public String sign(Headers message, BodyHasher bhj)
throws PermFailException {
byte[] computedHash = bhj.getDigest();
- String newField = "DKIM-Signature: "
- + signatureRecordTemplate.replaceAll("bh=[^;]*", "bh="
- + new String(Base64.encodeBase64(computedHash)));
-
+
+ bhj.getSignatureRecord().setBodyHash(computedHash);
+
List headers = bhj.getSignatureRecord().getHeaders();
try {
// TODO handle b= in SignatureRecord.
@@ -105,11 +104,12 @@
// with the right test representation.
// we need a method to "regenerate the text representation" and to
// retrieve it when it is valid.
- byte[] signatureHash = signatureSign(message, newField, bhj
+ byte[] signatureHash = signatureSign(message, bhj
.getSignatureRecord(), privateKey, headers);
- newField = newField.replaceAll("b=[^;]*", "b="
- + new String(Base64.encodeBase64(signatureHash)));
- return newField;
+
+ bhj.getSignatureRecord().setSignature(signatureHash);
+
+ return "DKIM-Signature:"+bhj.getSignatureRecord().toString();
} catch (InvalidKeyException e) {
throw new PermFailException("Invalid key: " + e.getMessage(), e);
} catch (NoSuchAlgorithmException e) {
@@ -121,8 +121,7 @@
}
}
- private byte[] signatureSign(Headers h, String signatureStub,
- SignatureRecord sign, PrivateKey key, List headers)
+ private byte[] signatureSign(Headers h, SignatureRecord sign, PrivateKey
key, List headers)
throws NoSuchAlgorithmException, InvalidKeyException,
SignatureException, PermFailException {
@@ -130,7 +129,8 @@
.toString().toUpperCase()
+ "with" + sign.getHashKeyType().toString().toUpperCase());
signature.initSign(key);
- signatureCheck(h, sign, headers, signatureStub, signature);
+
+ signatureCheck(h, sign, headers, signature);
return signature.sign();
}
Modified:
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/DKIMVerifier.java
URL:
http://svn.apache.org/viewvc/james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/DKIMVerifier.java?rev=824132&r1=824131&r2=824132&view=diff
==============================================================================
---
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/DKIMVerifier.java
(original)
+++
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/DKIMVerifier.java
Sun Oct 11 20:04:57 2009
@@ -247,7 +247,8 @@
try {
int pos = signatureField.indexOf(':');
if (pos > 0) {
- String v = signatureField.substring(pos + 1,
signatureField.length());
+ String v = signatureField.substring(pos + 1, signatureField
+ .length());
SignatureRecord signatureRecord;
try {
signatureRecord = newSignatureRecord(v);
@@ -265,7 +266,9 @@
List signedHeadersList = signatureRecord.getHeaders();
- signatureVerify(messageHeaders, signatureField,
signatureRecord, publicKeyRecord, signedHeadersList);
+ byte[] decoded = signatureRecord.getSignature();
+ signatureVerify(messageHeaders, signatureRecord, decoded,
+ publicKeyRecord, signedHeadersList);
// we track all canonicalizations+limit+bodyHash we
// see so to be able to check all of them in a single
@@ -299,7 +302,8 @@
if (bodyHashJobs.size() == 0) {
throw prepareException(signatureExceptions);
} else if (bodyHashJobs.size() == 1) {
- o = ((BodyHasher)
bodyHashJobs.values().iterator().next()).getOutputStream();
+ o = ((BodyHasher) bodyHashJobs.values().iterator().next())
+ .getOutputStream();
} else {
o = new CompoundOutputStream(outputStreams);
}
@@ -308,9 +312,8 @@
DKIMCommon.streamCopy(bodyInputStream, o);
List/* SignatureRecord */verifiedSignatures = new LinkedList();
- for (Iterator i = bodyHashJobs.keySet().iterator(); i.hasNext();) {
- String fval = (String) i.next();
- BodyHasher bhj = (BodyHasher) bodyHashJobs.get(fval);
+ for (Iterator i = bodyHashJobs.values().iterator(); i.hasNext();) {
+ BodyHasher bhj = (BodyHasher) i.next();
byte[] computedHash = bhj.getDigest();
byte[] expectedBodyHash = bhj.getSignatureRecord().getBodyHash();
@@ -318,7 +321,7 @@
if (!Arrays.equals(expectedBodyHash, computedHash)) {
signatureExceptions
.put(
- fval,
+
"DKIM-Signature:"+bhj.getSignatureRecord().toString(),
new PermFailException(
"Computed bodyhash is different from
the expected one"));
} else {
@@ -354,29 +357,25 @@
// TODO loops signatureExceptions to give a more complete
// response, using nested exception or a compound exception.
// System.out.println(signatureExceptions);
- return new PermFailException("found "
- + signatureExceptions.size() + " invalid signatures");
+ return new PermFailException("found " + signatureExceptions.size()
+ + " invalid signatures");
}
}
- private void signatureVerify(Headers h, String dkimSignature,
- SignatureRecord sign, PublicKeyRecord key, List headers)
+ private void signatureVerify(Headers h, SignatureRecord sign,
+ byte[] decoded, PublicKeyRecord key, List headers)
throws NoSuchAlgorithmException, InvalidKeyException,
SignatureException, PermFailException {
- byte[] decoded = sign.getSignature();
-
- String signatureStub = dkimSignature.replaceAll("b=[^;]*", "b=");
Signature signature = Signature.getInstance(sign.getHashMethod()
.toString().toUpperCase()
+ "with" + sign.getHashKeyType().toString().toUpperCase());
signature.initVerify(key.getPublicKey());
- signatureCheck(h, sign, headers, signatureStub, signature);
+ signatureCheck(h, sign, headers, signature);
if (!signature.verify(decoded))
- throw new PermFailException(
- "Header signature does not verify");
+ throw new PermFailException("Header signature does not verify");
}
}
Modified:
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/api/SignatureRecord.java
URL:
http://svn.apache.org/viewvc/james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/api/SignatureRecord.java?rev=824132&r1=824131&r2=824132&view=diff
==============================================================================
---
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/api/SignatureRecord.java
(original)
+++
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/api/SignatureRecord.java
Sun Oct 11 20:04:57 2009
@@ -21,15 +21,16 @@
import java.util.List;
+
+/**
+ * A complete SignatureRecord, including the signature and "formatted".
+ */
public interface SignatureRecord {
public final static String RELAXED = "relaxed";
public final static String SIMPLE = "simple";
-
public final static String ALL = ";all;";
- public abstract void validate();
-
public abstract List/* CharSequence */getHeaders();
public abstract CharSequence getIdentityLocalPart();
@@ -50,12 +51,20 @@
public abstract int getBodyHashLimit();
- public abstract byte[] getSignature();
-
public abstract String getHeaderCanonicalisationMethod();
public abstract String getBodyCanonicalisationMethod();
public abstract List getRecordLookupMethods();
+ public abstract void validate();
+
+ public abstract byte[] getSignature();
+
+ public abstract void setSignature(byte[] newSignature);
+
+ public abstract void setBodyHash(byte[] newBodyHash);
+
+ public abstract String toUnsignedString();
+
}
\ No newline at end of file
Modified:
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/PublicKeyRecordImpl.java
URL:
http://svn.apache.org/viewvc/james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/PublicKeyRecordImpl.java?rev=824132&r1=824131&r2=824132&view=diff
==============================================================================
---
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/PublicKeyRecordImpl.java
(original)
+++
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/PublicKeyRecordImpl.java
Sun Oct 11 20:04:57 2009
@@ -28,6 +28,7 @@
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
import java.util.regex.Pattern;
import org.apache.commons.codec.binary.Base64;
@@ -50,10 +51,13 @@
super(data);
}
- protected void init() {
+ protected Map newTagValue() {
// extensions may override this to use TreeMaps in order to keep track
// of orders
- tagValues = new LinkedHashMap();
+ return new LinkedHashMap();
+ }
+
+ protected void init() {
mandatoryTags.add("p");
defaults.put("v", "DKIM1");
defaults.put("g", "*");
@@ -68,10 +72,9 @@
// in the same way?
public void validate() {
super.validate();
- if (tagValues.containsKey("v")) {
+ if (containsTag("v")) {
// if "v" is specified it must be the first tag
- String firstKey = (String) ((LinkedHashMap) tagValues).keySet()
- .iterator().next();
+ String firstKey = (String) tagSet().iterator().next();
if (!"v".equals(firstKey))
throw new IllegalStateException(
"Existing v= tag MUST be the first in the record list
("
Modified:
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java
URL:
http://svn.apache.org/viewvc/james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java?rev=824132&r1=824131&r2=824132&view=diff
==============================================================================
---
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java
(original)
+++
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java
Sun Oct 11 20:04:57 2009
@@ -38,8 +38,6 @@
}
protected void init() {
- super.init();
-
mandatoryTags.add("v");
mandatoryTags.add("a");
mandatoryTags.add("b");
@@ -48,7 +46,7 @@
mandatoryTags.add("h");
mandatoryTags.add("s");
- defaults.put("c", "simple/simple");
+ defaults.put("c", SIMPLE+"/"+SIMPLE);
defaults.put("l", ALL);
defaults.put("q", "dns/txt");
}
@@ -282,7 +280,7 @@
if (pSlash != -1) {
return c.substring(pSlash + 1);
} else {
- return "simple";
+ return SIMPLE;
}
}
@@ -309,4 +307,19 @@
return res;
}
+ public void setSignature(byte[] newSignature) {
+ String signature = new String(Base64.encodeBase64(newSignature));
+ setValue("b", signature);
+ }
+
+ public void setBodyHash(byte[] newBodyHash) {
+ String bodyHash = new String(Base64.encodeBase64(newBodyHash));
+ setValue("bh", bodyHash);
+ }
+
+ public String toUnsignedString() {
+ return toString().replaceFirst("b=[^;]*", "b=");
+ }
+
+
}
Modified:
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/TagValue.java
URL:
http://svn.apache.org/viewvc/james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/TagValue.java?rev=824132&r1=824131&r2=824132&view=diff
==============================================================================
---
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/TagValue.java
(original)
+++
james/jdkim/trunk/main/src/main/java/org/apache/james/jdkim/tagvalue/TagValue.java
Sun Oct 11 20:04:57 2009
@@ -45,11 +45,19 @@
+ "((\r\n[\t ]|[\t ])+" + tval + ")*)?$");
// we may use a TreeMap because we may need to know original order.
- protected Map/* String, CharSequence */tagValues;
+ private Map/* String, CharSequence */tagValues;
protected Set/* String */mandatoryTags = new HashSet();
protected Map/* String, CharSequence */defaults = new HashMap();
+ private String stringRepresentation = null;
+ protected Set tagSet() {
+ return tagValues.keySet();
+ }
+ protected boolean containsTag(String tag) {
+ return tagValues.containsKey(tag);
+ }
+
protected CharSequence trimFWS(CharSequence data, int tStart, int tStop,
boolean trimWSP) {
if (DEBUG)
@@ -108,14 +116,18 @@
}
public TagValue(String data) {
+ tagValues = newTagValue();
init();
parse(data);
}
- protected void init() {
+ protected Map newTagValue() {
// extensions may override this to use TreeMaps in order to keep track
// of orders
- tagValues = new HashMap();
+ return new HashMap();
+ }
+
+ protected void init() {
}
/**
@@ -171,6 +183,7 @@
tagValues.put(tagString, value);
i = next;
}
+ this.stringRepresentation = data;
}
public int hashCode() {
@@ -208,6 +221,12 @@
else
return val;
}
+
+ protected void setValue(String tag, String value) {
+ stringRepresentation = null;
+ tagValues.put(tag, value);
+ }
+
protected CharSequence getDefault(String key) {
return (CharSequence) defaults.get(key);
@@ -250,6 +269,14 @@
}
public String toString() {
+ if (stringRepresentation == null) {
+ updateStringRepresentation();
+ }
+ return stringRepresentation;
+ }
+
+ private void updateStringRepresentation() {
+ // calculate a new string representation
StringBuffer res = new StringBuffer();
Set s = getTags();
for (Iterator i = s.iterator(); i.hasNext();) {
@@ -259,7 +286,8 @@
res.append(getValue(tag));
res.append("; ");
}
- return res.toString();
+ // TODO add folding
+ stringRepresentation = res.toString();
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]