Author: bago Date: Sun Oct 11 17:00:59 2009 New Revision: 824109 URL: http://svn.apache.org/viewvc?rev=824109&view=rev Log: Verified refactoring. Now returns a list of succesfully verified headers and publishes more internals as public methods.
Modified: 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 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=824109&r1=824108&r2=824109&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 17:00:59 2009 @@ -80,7 +80,7 @@ for (Iterator i = headers.iterator(); i.hasNext();) { CharSequence header = (CharSequence) i.next(); - // TODO check this getter is case insensitive + // NOTE check this getter is case insensitive List hl = h.getFields(header.toString()); if (hl != null && hl.size() > 0) { Integer done = (Integer) processedHeader.get(header.toString()); 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=824109&r1=824108&r2=824109&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 17:00:59 2009 @@ -74,7 +74,7 @@ try { BodyHasher bhj = newBodyHasher(srt); - // simultaneous computation of all the hashes. + // computation of the body hash. DKIMCommon.streamCopy(message.getBodyInputStream(), bhj .getOutputStream()); 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=824109&r1=824108&r2=824109&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 17:00:59 2009 @@ -189,160 +189,177 @@ return key; } - public void verify(InputStream is) throws IOException, FailException { + /** + * Verifies all of the DKIM-Signature records declared in the supplied input + * stream + * + * @param is + * inputStream + * @return a list of verified signature records. + * @throws IOException + * @throws FailException + * if no signature can be verified + */ + public List/* SignatureRecord */verify(InputStream is) throws IOException, + FailException { Message message; try { message = new Message(is); + return verify(message, message.getBodyInputStream()); } catch (MimeException e1) { throw new PermFailException("Mime parsing exception: " + e1.getMessage(), e1); + } finally { + is.close(); } + } + + /** + * Verifies all of the DKIM-Signature records declared in the Headers + * object. + * + * @param messageHeaders + * parsed headers + * @param bodyInputStream + * input stream for the body. + * @return a list of verified signature records + * @throws IOException + * @throws FailException + * if no signature can be verified + */ + public List/* SignatureRecord */verify(Headers messageHeaders, + InputStream bodyInputStream) throws IOException, FailException { // System.out.println(message.getFields("DKIM-Signature")); - List fields = message.getFields("DKIM-Signature"); + List fields = messageHeaders.getFields("DKIM-Signature"); // if (fields.size() > 1) throw new RuntimeException("here we are!"); - if (fields.size() > 0) { - // For each DKIM-signature we prepare an hashjob. - // We calculate all hashes concurrently so to read - // the inputstream only once. - Map/* String, BodyHashJob */bodyHashJobs = new HashMap(); - List/* OutputStream */outputStreams = new LinkedList(); - Map/* String, Exception */signatureExceptions = new Hashtable(); - for (Iterator i = fields.iterator(); i.hasNext();) { - String fval = (String) i.next(); - try { - int pos = fval.indexOf(':'); - if (pos > 0) { - String v = fval.substring(pos + 1, fval.length()); - SignatureRecord sign; - try { - sign = newSignatureRecord(v); - // validate - sign.validate(); - } catch (IllegalStateException e) { - throw new PermFailException(e.getMessage()); - } - - // TODO here we could check more parameters for - // validation - // before running a network operation like the dns - // lookup. - // e.g: the canonicalization method could be checked - // now. - - PublicKeyRecord key = publicRecordLookup(sign); - - List headers = sign.getHeaders(); - - boolean verified = signatureVerify(message, fval, sign, - key, headers); - - if (!verified) - throw new PermFailException( - "Header signature does not verify"); - - // we track all canonicalizations+limit+bodyHash we - // see so to be able to check all of them in a single - // stream run. - BodyHasher bhj = newBodyHasher(sign); - - bodyHashJobs.put(fval, bhj); - outputStreams.add(bhj.getOutputStream()); - - } else { - throw new PermFailException( - "unexpected bad signature field"); + if (fields.size() == 0) { + throw new PermFailException("DKIM-Signature field not found"); + } + + // For each DKIM-signature we prepare an hashjob. + // We calculate all hashes concurrently so to read + // the inputstream only once. + Map/* String, BodyHashJob */bodyHashJobs = new HashMap(); + List/* OutputStream */outputStreams = new LinkedList(); + Map/* String, Exception */signatureExceptions = new Hashtable(); + for (Iterator i = fields.iterator(); i.hasNext();) { + String signatureField = (String) i.next(); + try { + int pos = signatureField.indexOf(':'); + if (pos > 0) { + String v = signatureField.substring(pos + 1, signatureField.length()); + SignatureRecord signatureRecord; + try { + signatureRecord = newSignatureRecord(v); + // validate + signatureRecord.validate(); + } catch (IllegalStateException e) { + throw new PermFailException(e.getMessage()); } - } catch (TempFailException e) { - signatureExceptions.put(fval, e); - } catch (PermFailException e) { - signatureExceptions.put(fval, e); - } catch (InvalidKeyException e) { - signatureExceptions.put(fval, new PermFailException(e - .getMessage(), e)); - } catch (NoSuchAlgorithmException e) { - signatureExceptions.put(fval, new PermFailException(e - .getMessage(), e)); - } catch (SignatureException e) { - signatureExceptions.put(fval, new PermFailException(e - .getMessage(), e)); - } - } - OutputStream o; - if (bodyHashJobs.size() == 0) { - // TODO loops signatureExceptions to give a more complete - // response. - if (signatureExceptions.size() == 1) { - throw (FailException) signatureExceptions.values() - .iterator().next(); - } else { - // System.out.println(signatureExceptions); - throw new PermFailException("found " - + signatureExceptions.size() - + " invalid signatures"); - } - } else if (bodyHashJobs.size() == 1) { - o = (OutputStream) outputStreams.get(0); - } else { - o = new CompoundOutputStream(outputStreams); - } + // TODO here we could check more parameters for + // validation before running a network operation like the + // dns lookup. + // e.g: the canonicalization method could be checked now. + PublicKeyRecord publicKeyRecord = publicRecordLookup(signatureRecord); + + List signedHeadersList = signatureRecord.getHeaders(); + + signatureVerify(messageHeaders, signatureField, signatureRecord, publicKeyRecord, signedHeadersList); + + // we track all canonicalizations+limit+bodyHash we + // see so to be able to check all of them in a single + // stream run. + BodyHasher bhj = newBodyHasher(signatureRecord); - // simultaneous computation of all the hashes. - DKIMCommon.streamCopy(message.getBodyInputStream(), o); + bodyHashJobs.put(signatureField, bhj); + outputStreams.add(bhj.getOutputStream()); - List/* BodyHashJob */verifiedSignatures = new LinkedList(); - for (Iterator i = bodyHashJobs.keySet().iterator(); i.hasNext();) { - String fval = (String) i.next(); - BodyHasher bhj = (BodyHasher) bodyHashJobs.get(fval); - - byte[] computedHash = bhj.getDigest(); - byte[] expectedBodyHash = bhj.getSignatureRecord() - .getBodyHash(); - - if (!Arrays.equals(expectedBodyHash, computedHash)) { - signatureExceptions - .put( - fval, - new PermFailException( - "Computed bodyhash is different from the expected one")); } else { - verifiedSignatures.add(bhj); + throw new PermFailException( + "unexpected bad signature field"); } + } catch (TempFailException e) { + signatureExceptions.put(signatureField, e); + } catch (PermFailException e) { + signatureExceptions.put(signatureField, e); + } catch (InvalidKeyException e) { + signatureExceptions.put(signatureField, new PermFailException(e + .getMessage(), e)); + } catch (NoSuchAlgorithmException e) { + signatureExceptions.put(signatureField, new PermFailException(e + .getMessage(), e)); + } catch (SignatureException e) { + signatureExceptions.put(signatureField, new PermFailException(e + .getMessage(), e)); } + } - if (verifiedSignatures.size() == 0) { - if (signatureExceptions.size() == 1) { - throw (FailException) signatureExceptions.values() - .iterator().next(); - } else { - throw new PermFailException("found " - + signatureExceptions.size() - + " non verifying signatures"); - } + OutputStream o; + if (bodyHashJobs.size() == 0) { + throw prepareException(signatureExceptions); + } else if (bodyHashJobs.size() == 1) { + o = ((BodyHasher) bodyHashJobs.values().iterator().next()).getOutputStream(); + } else { + o = new CompoundOutputStream(outputStreams); + } + + // simultaneous computation of all the hashes. + 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); + + byte[] computedHash = bhj.getDigest(); + byte[] expectedBodyHash = bhj.getSignatureRecord().getBodyHash(); + + if (!Arrays.equals(expectedBodyHash, computedHash)) { + signatureExceptions + .put( + fval, + new PermFailException( + "Computed bodyhash is different from the expected one")); } else { - // TODO list good and bad signatures. - for (Iterator i = signatureExceptions.keySet().iterator(); i - .hasNext();) { - String f = (String) i.next(); - System.out.println("DKIM-Error: " - + ((FailException) signatureExceptions.get(f)) - .getMessage() + " FIELD: " + f); - } - for (Iterator i = verifiedSignatures.iterator(); i.hasNext();) { - BodyHasher bhj = (BodyHasher) i.next(); - System.out - .println("DKIM-Pass: " + bhj.getSignatureRecord()); - } + verifiedSignatures.add(bhj.getSignatureRecord()); } + } + if (verifiedSignatures.size() == 0) { + throw prepareException(signatureExceptions); } else { - throw new PermFailException("DKIM-Signature field not found"); + // TODO list good and bad signatures. + for (Iterator i = signatureExceptions.keySet().iterator(); i + .hasNext();) { + String f = (String) i.next(); + System.out.println("DKIM-Error: " + + ((FailException) signatureExceptions.get(f)) + .getMessage() + " FIELD: " + f); + } + for (Iterator i = verifiedSignatures.iterator(); i.hasNext();) { + SignatureRecord sr = (SignatureRecord) i.next(); + System.out.println("DKIM-Pass: " + sr); + } + return verifiedSignatures; } - is.close(); } - private boolean signatureVerify(Headers h, String dkimSignature, + private FailException prepareException(Map signatureExceptions) { + if (signatureExceptions.size() == 1) { + return (FailException) signatureExceptions.values().iterator() + .next(); + } else { + // 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"); + } + } + + private void signatureVerify(Headers h, String dkimSignature, SignatureRecord sign, PublicKeyRecord key, List headers) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, PermFailException { @@ -357,7 +374,9 @@ signatureCheck(h, sign, headers, signatureStub, signature); - return signature.verify(decoded); + if (!signature.verify(decoded)) + throw new PermFailException( + "Header signature does not verify"); } } --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org