/* * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.util; import java.security.CodeSigner; import java.security.Key; import java.security.Timestamp; import java.security.cert.CertPath; import java.security.cert.X509Certificate; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import sun.security.util.AnchorCertificates; import sun.security.util.ConstraintsParameters; import sun.security.validator.Validator; /** * This class contains parameters for checking signed JARs against * constraints specified in the jdk.jar.disabledAlgorithms security * property. */ public class JarConstraintsParameters implements ConstraintsParameters { // true if chain is anchored by a JDK root CA private boolean anchorIsJdkCA; private boolean anchorIsJdkCASet; // The timestamp of the signed JAR file, if timestamped private Date timestamp; // The keys of the signers private final Set keys; // The certs in the signers' chains that are issued by the trust anchor private final Set certsIssuedByAnchor; // The extended exception message private String message; /** * Create a JarConstraintsParameters. * * @param signers the CodeSigners that signed the JAR */ public JarConstraintsParameters(CodeSigner[] signers) { this.keys = new HashSet<>(); this.certsIssuedByAnchor = new HashSet<>(); Date latestTimestamp = null; boolean skipTimestamp = false; // Iterate over the signers and extract the keys, the latest // timestamp, and the last certificate of each chain which can be // used for checking if the signer's certificate chains back to a // JDK root CA for (CodeSigner signer : signers) { init(signer.getSignerCertPath()); Timestamp timestamp = signer.getTimestamp(); if (timestamp == null) { // this means one of the signers doesn't have a timestamp // and the JAR should be treated as if it isn't timestamped latestTimestamp = null; skipTimestamp = true; } else { // add the key and last cert of TSA too init(timestamp.getSignerCertPath()); if (!skipTimestamp) { Date timestampDate = timestamp.getTimestamp(); if (latestTimestamp == null) { latestTimestamp = timestampDate; } else { if (latestTimestamp.before(timestampDate)) { latestTimestamp = timestampDate; } } } } } this.timestamp = latestTimestamp; } // extract last certificate and key from chain private void init(CertPath cp) { @SuppressWarnings("unchecked") List chain = (List)cp.getCertificates(); if (!chain.isEmpty()) { this.certsIssuedByAnchor.add(chain.get(chain.size() - 1)); this.keys.add(chain.get(0).getPublicKey()); } } @Override public String getVariant() { return Validator.VAR_GENERIC; } /** * Since loading the cacerts keystore can be an expensive operation, * this is only performed if this method is called during a "jdkCA" * constraints check of a disabled algorithm, and the result is cached. * * @return true if at least one of the certificates are issued by a * JDK root CA */ @Override public boolean anchorIsJdkCA() { if (anchorIsJdkCASet) { return anchorIsJdkCA; } for (X509Certificate cert : certsIssuedByAnchor) { if (AnchorCertificates.issuerOf(cert)) { anchorIsJdkCA = true; break; } } anchorIsJdkCASet = true; return anchorIsJdkCA; } @Override public Date getDate() { return timestamp; } @Override public Set getKeys() { return keys; } /** * Sets the extended error message. Note: this should be used * carefully as it is specific to the attribute/entry/file being checked. * * @param file the name of the signature related file being verified * @param target the attribute containing the algorithm that is being * checked */ public void setExtendedExceptionMsg(String file, String target) { message = " used" + (target != null ? " with " + target : "") + " in " + file + " file."; } @Override public String extendedExceptionMsg() { return message; } @Override public String toString() { StringBuilder sb = new StringBuilder("[\n"); sb.append("\n Variant: ").append(getVariant()); sb.append("\n Certs Issued by Anchor:"); for (X509Certificate cert : certsIssuedByAnchor) { sb.append("\n Cert Issuer: ") .append(cert.getIssuerX500Principal()); sb.append("\n Cert Subject: ") .append(cert.getSubjectX500Principal()); } for (Key key : keys) { sb.append("\n Key: ").append(key.getAlgorithm()); } if (timestamp != null) { sb.append("\n Timestamp: ").append(timestamp); } sb.append("\n]"); return sb.toString(); } }