Skip to content

Commit

Permalink
Added Cert.sortCertificateChain()
Browse files Browse the repository at this point in the history
The sortCertificateChain() has been moved from
com.netscape.cmsutil.crypto.CryptoUtil in PKI to
org.mozilla.jss.netscape.security.util.Cert in
JSS for reusability.

The version number has been updated to 4.5.3 such
that the JSS dependency can be set properly in
other packages.
  • Loading branch information
edewata committed Mar 1, 2019
1 parent 9ba2467 commit fc5d152
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 2 deletions.
2 changes: 1 addition & 1 deletion jss.spec
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Summary: Java Security Services (JSS)
URL: http://www.dogtagpki.org/wiki/JSS
License: MPLv1.1 or GPLv2+ or LGPLv2+

Version: 4.5.2
Version: 4.5.3
Release: 1%{?_timestamp}%{?_commit_id}%{?dist}
# global _phase -a1

Expand Down
138 changes: 137 additions & 1 deletion org/mozilla/jss/netscape/security/util/Cert.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,25 @@
import java.security.cert.CertificateException;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.ArrayUtils;
import org.mozilla.jss.crypto.SignatureAlgorithm;

import org.mozilla.jss.netscape.security.pkcs.PKCS7;
import org.mozilla.jss.netscape.security.x509.X509CRLImpl;
import org.mozilla.jss.netscape.security.x509.X509CertImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Cert {

private static Logger logger = LoggerFactory.getLogger(Cert.class);

public static final String HEADER = "-----BEGIN CERTIFICATE-----";
public static final String FOOTER = "-----END CERTIFICATE-----";

Expand Down Expand Up @@ -204,4 +214,130 @@ public static byte[] parseCertificate(String cert) {
String b64 = stripBrackets(encoded);
return Utils.base64decode(b64);
}

/**
* Sorts certificate chain from root to leaf.
*
* This method sorts an array of certificates (e.g. from a PKCS #7
* data) that represents a certificate chain from root to leaf
* according to the subject DNs and issuer DNs.
*
* The input array is a set of certificates that are part of a
* chain but not in specific order.
*
* The result is a new array that contains the certificate chain
* sorted from root to leaf. The input array is unchanged.
*
* @param certs input array of certificates
* @return new array containing sorted certificates
*/
public static java.security.cert.X509Certificate[] sortCertificateChain(java.security.cert.X509Certificate[] certs) throws Exception {

// lookup map: subject DN -> cert
Map<String, java.security.cert.X509Certificate> certMap = new LinkedHashMap<>();

// hierarchy map: subject DN -> issuer DN
Map<String, String> parentMap = new HashMap<>();

// reverse hierarchy map: issuer DN -> subject DN
Map<String, String> childMap = new HashMap<>();

// build maps
for (java.security.cert.X509Certificate cert : certs) {

String subjectDN = cert.getSubjectDN().toString();
String issuerDN = cert.getIssuerDN().toString();

if (certMap.containsKey(subjectDN)) {
throw new Exception("Duplicate certificate: " + subjectDN);
}

certMap.put(subjectDN, cert);

// ignore self-signed certificate
if (subjectDN.equals(issuerDN)) continue;

if (childMap.containsKey(issuerDN)) {
throw new Exception("Branched chain: " + issuerDN);
}

parentMap.put(subjectDN, issuerDN);
childMap.put(issuerDN, subjectDN);
}

if (logger.isDebugEnabled()) {
logger.debug("Certificates:");
for (String subjectDN : certMap.keySet()) {
logger.debug(" - " + subjectDN);

String parent = parentMap.get(subjectDN);
if (parent != null) logger.debug(" parent: " + parent);

String child = childMap.get(subjectDN);
if (child != null) logger.debug(" child: " + child);
}
}

// find leaf cert
List<String> leafCerts = new ArrayList<>();

for (String subjectDN : certMap.keySet()) {

// if cert has a child, skip
if (childMap.containsKey(subjectDN)) continue;

// found leaf cert
leafCerts.add(subjectDN);
}

if (leafCerts.isEmpty()) {
throw new Exception("Unable to find leaf certificate");
}

if (leafCerts.size() > 1) {
StringBuilder sb = new StringBuilder();
for (String subjectDN : leafCerts) {
if (sb.length() > 0) sb.append(", ");
sb.append("[" + subjectDN + "]");
}
throw new Exception("Multiple leaf certificates: " + sb);
}

// build sorted chain
LinkedList<java.security.cert.X509Certificate> chain = new LinkedList<>();

// start from leaf
String current = leafCerts.get(0);

while (current != null) {

java.security.cert.X509Certificate cert = certMap.get(current);

if (cert == null) {
// incomplete chain
break;
}

// add to the beginning of chain
chain.addFirst(cert);

// follow parent to root
current = parentMap.get(current);
}

return chain.toArray(new java.security.cert.X509Certificate[chain.size()]);
}

public static java.security.cert.X509Certificate[] sortCertificateChain(
java.security.cert.X509Certificate[] certs,
boolean reverse) throws Exception {

certs = sortCertificateChain(certs);

if (reverse) {
ArrayUtils.reverse(certs);
}

return certs;
}
}

0 comments on commit fc5d152

Please sign in to comment.