|
26 | 26 | import java.util.ArrayList;
|
27 | 27 | import java.util.Collections;
|
28 | 28 | import java.util.List;
|
| 29 | +import java.util.Optional; |
29 | 30 | import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
|
30 | 31 |
|
31 | 32 | public class Certificates {
|
32 | 33 |
|
| 34 | + private static final String SCT_X509_OID = "1.3.6.1.4.1.11129.2.4.2"; |
| 35 | + |
33 | 36 | /** Convert a certificate to a PEM encoded certificate. */
|
34 | 37 | public static String toPemString(Certificate cert) throws IOException {
|
35 | 38 | var certWriter = new StringWriter();
|
@@ -137,15 +140,69 @@ public static CertPath toCertPath(Certificate certificate) throws CertificateExc
|
137 | 140 | return cf.generateCertPath(Collections.singletonList(certificate));
|
138 | 141 | }
|
139 | 142 |
|
140 |
| - /** Appends an X509Certificate to a {@link CertPath} as a leaf. */ |
141 |
| - public static CertPath appendCertPath(CertPath root, Certificate certificate) |
| 143 | + /** Appends a CertPath to another {@link CertPath} as children. */ |
| 144 | + public static CertPath appendCertPath(CertPath parent, Certificate child) |
142 | 145 | throws CertificateException {
|
143 | 146 | CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
144 | 147 | List<Certificate> certs =
|
145 |
| - ImmutableList.<Certificate>builder() |
146 |
| - .add(certificate) |
147 |
| - .addAll(root.getCertificates()) |
148 |
| - .build(); |
| 148 | + ImmutableList.<Certificate>builder().add(child).addAll(parent.getCertificates()).build(); |
149 | 149 | return cf.generateCertPath(certs);
|
150 | 150 | }
|
| 151 | + |
| 152 | + /** |
| 153 | + * Trims a parent CertPath from a provided CertPath. This is intended to be used to trim trusted |
| 154 | + * root and intermediates from a full CertPath to reveal just the untrusted parts which can be |
| 155 | + * distributed as part of a signature tuple or bundle. |
| 156 | + * |
| 157 | + * @param certPath a certificate path to trim from |
| 158 | + * @param parentPath the parent certPath to trim off the full certPath |
| 159 | + * @return a trimmed path |
| 160 | + * @throws IllegalArgumentException if the trimPath is not a parent of the certPath or if they are |
| 161 | + * the same length |
| 162 | + * @throws CertificateException if an error occurs during CertPath construction |
| 163 | + */ |
| 164 | + public static CertPath trimParent(CertPath certPath, CertPath parentPath) |
| 165 | + throws CertificateException { |
| 166 | + if (!containsParent(certPath, parentPath)) { |
| 167 | + throw new IllegalArgumentException("trim path was not the parent of the provider chain"); |
| 168 | + } |
| 169 | + var certs = certPath.getCertificates(); |
| 170 | + var parent = parentPath.getCertificates(); |
| 171 | + CertificateFactory cf = CertificateFactory.getInstance("X.509"); |
| 172 | + return cf.generateCertPath(certs.subList(0, certs.size() - parent.size())); |
| 173 | + } |
| 174 | + |
| 175 | + /** Check if a parent certpath is the suffix of a certpath */ |
| 176 | + public static boolean containsParent(CertPath certPath, CertPath parentPath) { |
| 177 | + var certs = certPath.getCertificates(); |
| 178 | + var parent = parentPath.getCertificates(); |
| 179 | + return parent.size() <= certs.size() |
| 180 | + && certs.subList(certs.size() - parent.size(), certs.size()).equals(parent); |
| 181 | + } |
| 182 | + |
| 183 | + /** |
| 184 | + * Find and return any SCTs embedded in a certificate. |
| 185 | + * |
| 186 | + * @param certificate the certificate with embedded scts |
| 187 | + * @return a byte array containing any number of embedded scts |
| 188 | + */ |
| 189 | + public static Optional<byte[]> getEmbeddedSCTs(Certificate certificate) { |
| 190 | + return Optional.ofNullable(((X509Certificate) certificate).getExtensionValue(SCT_X509_OID)); |
| 191 | + } |
| 192 | + |
| 193 | + /** Check if a certificate is self-signed. */ |
| 194 | + public static boolean isSelfSigned(Certificate certificate) { |
| 195 | + return ((X509Certificate) certificate) |
| 196 | + .getIssuerX500Principal() |
| 197 | + .equals(((X509Certificate) certificate).getSubjectX500Principal()); |
| 198 | + } |
| 199 | + |
| 200 | + /** Check if the root of a CertPath is self-signed */ |
| 201 | + public static boolean isSelfSigned(CertPath certPath) { |
| 202 | + return isSelfSigned(certPath.getCertificates().get(certPath.getCertificates().size() - 1)); |
| 203 | + } |
| 204 | + |
| 205 | + public static X509Certificate getLeaf(CertPath certPath) { |
| 206 | + return (X509Certificate) certPath.getCertificates().get(0); |
| 207 | + } |
151 | 208 | }
|
0 commit comments