Skip to content

Commit

Permalink
Add support for TLSA certificate rollover
Browse files Browse the repository at this point in the history
Fixes #31.
  • Loading branch information
Flowdalic committed Apr 14, 2016
1 parent 83bbbd6 commit 2aa2fe4
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2015 the original author or authors
*
* This software is licensed under the Apache License, Version 2.0,
* the GNU Lesser General Public License version 2 or later ("LGPL")
* and the WTFPL.
* You may choose either license to govern your use of this software only
* upon the condition that you accept all of the terms of either
* the Apache License 2.0, the LGPL 2.1+ or the WTFPL.
*/
package de.measite.minidns.dane;

import java.security.cert.CertificateException;
import java.util.Collections;
import java.util.List;

import de.measite.minidns.record.TLSA;

public abstract class DaneCertificateException extends CertificateException {

/**
*
*/
private static final long serialVersionUID = 1L;

protected DaneCertificateException() {
}

protected DaneCertificateException(String message) {
super(message);
}

public static class CertificateMismatch extends DaneCertificateException {

/**
*
*/
private static final long serialVersionUID = 1L;

public final TLSA tlsa;
public final byte[] computed;

public CertificateMismatch(TLSA tlsa, byte[] computed) {
super("The TLSA RR does not match the certificate");
this.tlsa = tlsa;
this.computed = computed;
}
}

public static class MultipleCertificateMismatchExceptions extends DaneCertificateException {

/**
*
*/
private static final long serialVersionUID = 1L;

public final List<CertificateMismatch> certificateMismatchExceptions;

public MultipleCertificateMismatchExceptions(List<CertificateMismatch> certificateMismatchExceptions) {
super("There where multiple CertificateMismatch exceptions because none of the TLSA RR does match the certificate");
assert !certificateMismatchExceptions.isEmpty();
this.certificateMismatchExceptions = Collections.unmodifiableList(certificateMismatchExceptions);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand Down Expand Up @@ -120,15 +121,28 @@ public boolean verifyCertificateChain(X509Certificate[] chain, String hostName,
LOGGER.info(msg);
return false;
}

List<DaneCertificateException.CertificateMismatch> certificateMismatchExceptions = new LinkedList<>();
boolean verified = false;
for (Record record : res.getAnswers()) {
if (record.type == Record.TYPE.TLSA && record.name.equals(req)) {
TLSA tlsa = (TLSA) record.payloadData;
verified |= checkCertificateMatches(chain[0], tlsa, hostName);
try {
verified |= checkCertificateMatches(chain[0], tlsa, hostName);
} catch (DaneCertificateException.CertificateMismatch certificateMismatchException) {
// Record the mismatch and only throw an exception if no
// TLSA RR is able to verify the cert. This allows for TLSA
// certificate rollover.
certificateMismatchExceptions.add(certificateMismatchException);
}
if (verified) break;
}
}

if (!verified && !certificateMismatchExceptions.isEmpty()) {
throw new DaneCertificateException.MultipleCertificateMismatchExceptions(certificateMismatchExceptions);
}

return verified;
}

Expand Down Expand Up @@ -182,7 +196,7 @@ private static boolean checkCertificateMatches(X509Certificate cert, TLSA tlsa,

boolean matches = Arrays.equals(comp, tlsa.certificateAssociation);
if (!matches) {
throw new CertificateException("Verification using TLSA failed");
throw new DaneCertificateException.CertificateMismatch(tlsa, comp);
}

// domain issued certificate does not require further verification,
Expand Down

0 comments on commit 2aa2fe4

Please sign in to comment.