/* * Copyright (c) 2015, 2021, 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.testlibrary; import java.io.*; import java.util.*; import java.security.*; import java.security.cert.X509Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.Extension; import javax.security.auth.x500.X500Principal; import java.math.BigInteger; import sun.security.util.DerOutputStream; import sun.security.util.DerValue; import sun.security.util.ObjectIdentifier; import sun.security.util.SignatureUtil; import sun.security.x509.AccessDescription; import sun.security.x509.AlgorithmId; import sun.security.x509.AuthorityInfoAccessExtension; import sun.security.x509.AuthorityKeyIdentifierExtension; import sun.security.x509.SubjectKeyIdentifierExtension; import sun.security.x509.BasicConstraintsExtension; import sun.security.x509.ExtendedKeyUsageExtension; import sun.security.x509.DNSName; import sun.security.x509.GeneralName; import sun.security.x509.GeneralNames; import sun.security.x509.KeyUsageExtension; import sun.security.x509.SerialNumber; import sun.security.x509.SubjectAlternativeNameExtension; import sun.security.x509.URIName; import sun.security.x509.KeyIdentifier; /** * Helper class that builds and signs X.509 certificates. * * A CertificateBuilder is created with a default constructor, and then * uses additional public methods to set the public key, desired validity * dates, serial number and extensions. It is expected that the caller will * have generated the necessary key pairs prior to using a CertificateBuilder * to generate certificates. * * The following methods are mandatory before calling build(): *
* Certificate ::= SEQUENCE { * tbsCertificate TBSCertificate, * signatureAlgorithm AlgorithmIdentifier, * signatureValue BIT STRING } ** * @param issuerCert The certificate of the issuing authority, or * {@code null} if the resulting certificate is self-signed. * @param issuerKey The private key of the issuing authority * @param signAlg The signature algorithm object * * @return The DER-encoded X.509 certificate * * @throws CertificateException If an error occurs during the * signing process. * @throws IOException if an encoding error occurs. */ private byte[] encodeTopLevel(X509Certificate issuerCert, PrivateKey issuerKey, String algName) throws CertificateException, IOException, NoSuchAlgorithmException { AlgorithmId signAlg = AlgorithmId.get(algName); DerOutputStream outerSeq = new DerOutputStream(); DerOutputStream topLevelItems = new DerOutputStream(); try { Signature sig = SignatureUtil.fromKey(signAlg.getName(), issuerKey, (Provider)null); // Rewrite signAlg, RSASSA-PSS needs some parameters. signAlg = SignatureUtil.fromSignature(sig, issuerKey); tbsCertBytes = encodeTbsCert(issuerCert, signAlg); sig.update(tbsCertBytes); signatureBytes = sig.sign(); } catch (GeneralSecurityException ge) { throw new CertificateException(ge); } topLevelItems.write(tbsCertBytes); signAlg.encode(topLevelItems); topLevelItems.putBitString(signatureBytes); outerSeq.write(DerValue.tag_Sequence, topLevelItems); return outerSeq.toByteArray(); } /** * Encode the bytes for the TBSCertificate structure: *
* TBSCertificate ::= SEQUENCE { * version [0] EXPLICIT Version DEFAULT v1, * serialNumber CertificateSerialNumber, * signature AlgorithmIdentifier, * issuer Name, * validity Validity, * subject Name, * subjectPublicKeyInfo SubjectPublicKeyInfo, * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, * -- If present, version MUST be v2 or v3 * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, * -- If present, version MUST be v2 or v3 * extensions [3] EXPLICIT Extensions OPTIONAL * -- If present, version MUST be v3 * } * * @param issuerCert The certificate of the issuing authority, or * {@code null} if the resulting certificate is self-signed. * @param signAlg The signature algorithm object * * @return The DER-encoded bytes for the TBSCertificate structure * * @throws IOException if an encoding error occurs. */ private byte[] encodeTbsCert(X509Certificate issuerCert, AlgorithmId signAlg) throws IOException { DerOutputStream tbsCertSeq = new DerOutputStream(); DerOutputStream tbsCertItems = new DerOutputStream(); // Hardcode to V3 byte[] v3int = {0x02, 0x01, 0x02}; tbsCertItems.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0), v3int); // Serial Number SerialNumber sn = new SerialNumber(serialNumber); sn.encode(tbsCertItems); // Algorithm ID signAlg.encode(tbsCertItems); // Issuer Name if (issuerCert != null) { tbsCertItems.write( issuerCert.getSubjectX500Principal().getEncoded()); } else { // Self-signed tbsCertItems.write(subjectName.getEncoded()); } // Validity period (set as UTCTime) DerOutputStream valSeq = new DerOutputStream(); valSeq.putUTCTime(notBefore); valSeq.putUTCTime(notAfter); tbsCertItems.write(DerValue.tag_Sequence, valSeq); // Subject Name tbsCertItems.write(subjectName.getEncoded()); // SubjectPublicKeyInfo tbsCertItems.write(publicKey.getEncoded()); // TODO: Extensions! encodeExtensions(tbsCertItems); // Wrap it all up in a SEQUENCE and return the bytes tbsCertSeq.write(DerValue.tag_Sequence, tbsCertItems); return tbsCertSeq.toByteArray(); } /** * Encode the extensions segment for an X.509 Certificate: * ** Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension * * Extension ::= SEQUENCE { * extnID OBJECT IDENTIFIER, * critical BOOLEAN DEFAULT FALSE, * extnValue OCTET STRING * -- contains the DER encoding of an ASN.1 value * -- corresponding to the extension type identified * -- by extnID * } ** * @param tbsStream The {@code DerOutputStream} that holds the * TBSCertificate contents. * * @throws IOException if an encoding error occurs. */ private void encodeExtensions(DerOutputStream tbsStream) throws IOException { DerOutputStream extSequence = new DerOutputStream(); DerOutputStream extItems = new DerOutputStream(); for (Extension ext : extensions.values()) { ext.encode(extItems); } extSequence.write(DerValue.tag_Sequence, extItems); tbsStream.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)3), extSequence); } }