All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bouncycastle.tls.Certificate Maven / Gradle / Ivy

There is a newer version: 1.0.7
Show newest version
package org.bouncycastle.tls;

import org.bouncycastle.tls.crypto.TlsCertificate;
import org.bouncycastle.tls.crypto.TlsCrypto;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import java.util.Vector;

/**
 * Parsing and encoding of a Certificate struct from RFC 4346.
 * 
 * opaque ASN.1Cert<2^24-1>;
 *
 * struct {
 *     ASN.1Cert certificate_list<0..2^24-1>;
 * } Certificate;
 * 
* * @see org.bouncycastle.asn1.x509.Certificate */ public class Certificate { private static final TlsCertificate[] EMPTY_CERTS = new TlsCertificate[0]; private static final CertificateEntry[] EMPTY_CERT_ENTRIES = new CertificateEntry[0]; public static final Certificate EMPTY_CHAIN = new Certificate(EMPTY_CERTS); public static final Certificate EMPTY_CHAIN_TLS13 = new Certificate(TlsUtils.EMPTY_BYTES, EMPTY_CERT_ENTRIES); public static class ParseOptions { private int maxChainLength = Integer.MAX_VALUE; private short certificateType = CertificateType.X509; public int getMaxChainLength() { return maxChainLength; } public ParseOptions setMaxChainLength(int maxChainLength) { this.maxChainLength = maxChainLength; return this; } public short getCertificateType() { return certificateType; } /** * Set the {@link CertificateType} according to the IANA TLS Certificate Types registry */ public ParseOptions setCertificateType(short certificateType) { this.certificateType = certificateType; return this; } } private static CertificateEntry[] convert(TlsCertificate[] certificateList) { if (TlsUtils.isNullOrContainsNull(certificateList)) { throw new NullPointerException("'certificateList' cannot be null or contain any nulls"); } int count = certificateList.length; CertificateEntry[] result = new CertificateEntry[count]; for (int i = 0; i < count; ++i) { result[i] = new CertificateEntry(certificateList[i], null); } return result; } protected final byte[] certificateRequestContext; protected final CertificateEntry[] certificateEntryList; protected final short certificateType; public Certificate(TlsCertificate[] certificateList) { this(null, convert(certificateList)); } public Certificate(byte[] certificateRequestContext, CertificateEntry[] certificateEntryList) { this(CertificateType.X509, certificateRequestContext, certificateEntryList); } // TODO[tls13] Prefer to manage the certificateRequestContext internally only? public Certificate(short certificateType, byte[] certificateRequestContext, CertificateEntry[] certificateEntryList) { if (null != certificateRequestContext && !TlsUtils.isValidUint8(certificateRequestContext.length)) { throw new IllegalArgumentException("'certificateRequestContext' cannot be longer than 255"); } if (TlsUtils.isNullOrContainsNull(certificateEntryList)) { throw new NullPointerException("'certificateEntryList' cannot be null or contain any nulls"); } this.certificateRequestContext = TlsUtils.clone(certificateRequestContext); this.certificateEntryList = certificateEntryList; this.certificateType = certificateType; } public byte[] getCertificateRequestContext() { return TlsUtils.clone(certificateRequestContext); } /** * @return an array of {@link org.bouncycastle.asn1.x509.Certificate} representing a certificate * chain. */ public TlsCertificate[] getCertificateList() { return cloneCertificateList(); } public TlsCertificate getCertificateAt(int index) { return certificateEntryList[index].getCertificate(); } public CertificateEntry getCertificateEntryAt(int index) { return certificateEntryList[index]; } public CertificateEntry[] getCertificateEntryList() { return cloneCertificateEntryList(); } public short getCertificateType() { return certificateType; } public int getLength() { return certificateEntryList.length; } /** * @return true if this certificate chain contains no certificates, or * false otherwise. */ public boolean isEmpty() { return certificateEntryList.length == 0; } /** * Encode this {@link Certificate} to an {@link OutputStream}, and optionally calculate the * "end point hash" (per RFC 5929's tls-server-end-point binding). * * @param messageOutput the {@link OutputStream} to encode to. * @param endPointHashOutput the {@link OutputStream} to write the "end point hash" (or null). * @throws IOException */ public void encode(TlsContext context, OutputStream messageOutput, OutputStream endPointHashOutput) throws IOException { final boolean isTLSv13 = TlsUtils.isTLSv13(context); if ((null != certificateRequestContext) != isTLSv13) { throw new IllegalStateException(); } if (isTLSv13) { TlsUtils.writeOpaque8(certificateRequestContext, messageOutput); } int count = certificateEntryList.length; Vector certEncodings = new Vector(count); Vector extEncodings = isTLSv13 ? new Vector(count) : null; long totalLength = 0; for (int i = 0; i < count; ++i) { CertificateEntry entry = certificateEntryList[i]; TlsCertificate cert = entry.getCertificate(); byte[] derEncoding = cert.getEncoded(); if (i == 0 && endPointHashOutput != null) { calculateEndPointHash(context, cert, derEncoding, endPointHashOutput); } certEncodings.addElement(derEncoding); totalLength += derEncoding.length; totalLength += 3; if (isTLSv13) { Map extensions = entry.getExtensions(); byte[] extEncoding = (null == extensions) ? TlsUtils.EMPTY_BYTES : TlsProtocol.writeExtensionsData(extensions); extEncodings.addElement(extEncoding); totalLength += extEncoding.length; totalLength += 2; } } // RFC 7250 indicates the raw key is not wrapped in a cert list like X509 is // but RFC 8446 wraps it in a CertificateEntry, which is inside certificate_list if (isTLSv13 || certificateType != CertificateType.RawPublicKey) { TlsUtils.checkUint24(totalLength); TlsUtils.writeUint24((int)totalLength, messageOutput); } for (int i = 0; i < count; ++i) { byte[] certEncoding = (byte[])certEncodings.elementAt(i); TlsUtils.writeOpaque24(certEncoding, messageOutput); if (isTLSv13) { byte[] extEncoding = (byte[])extEncodings.elementAt(i); TlsUtils.writeOpaque16(extEncoding, messageOutput); } } } /** * Parse a {@link Certificate} from an {@link InputStream}. * * @param context * the {@link TlsContext} of the current connection. * @param messageInput * the {@link InputStream} to parse from. * @param endPointHashOutput the {@link OutputStream} to write the "end point hash" (or null). * @return a {@link Certificate} object. * @throws IOException * @deprecated Use version taking a {@link ParseOptions} argument instead. */ public static Certificate parse(TlsContext context, InputStream messageInput, OutputStream endPointHashOutput) throws IOException { return parse(new ParseOptions(), context, messageInput, endPointHashOutput); } /** * Parse a {@link Certificate} from an {@link InputStream}. * * @param options * the {@link ParseOptions} to apply during parsing. * @param context * the {@link TlsContext} of the current connection. * @param messageInput * the {@link InputStream} to parse from. * @param endPointHashOutput the {@link OutputStream} to write the "end point hash" (or null). * @return a {@link Certificate} object. * @throws IOException */ public static Certificate parse(ParseOptions options, TlsContext context, InputStream messageInput, OutputStream endPointHashOutput) throws IOException { final SecurityParameters securityParameters = context.getSecurityParameters(); final boolean isTLSv13 = TlsUtils.isTLSv13(securityParameters.getNegotiatedVersion()); final short certType = options.getCertificateType(); byte[] certificateRequestContext = null; if (isTLSv13) { certificateRequestContext = TlsUtils.readOpaque8(messageInput); } int totalLength = TlsUtils.readUint24(messageInput); if (totalLength == 0) { return !isTLSv13 ? EMPTY_CHAIN : certificateRequestContext.length < 1 ? EMPTY_CHAIN_TLS13 : new Certificate(certType, certificateRequestContext, EMPTY_CERT_ENTRIES); } byte[] certListData = TlsUtils.readFully(totalLength, messageInput); ByteArrayInputStream buf = new ByteArrayInputStream(certListData); TlsCrypto crypto = context.getCrypto(); int maxChainLength = Math.max(1, options.getMaxChainLength()); Vector certificate_list = new Vector(); while (buf.available() > 0) { if (certificate_list.size() >= maxChainLength) { throw new TlsFatalAlert(AlertDescription.internal_error, "Certificate chain longer than maximum (" + maxChainLength + ")"); } // RFC 7250 indicates the raw key is not wrapped in a cert list like X509 is // but RFC 8446 wraps it in a CertificateEntry, which is inside certificate_list byte[] derEncoding; if (isTLSv13 || certType != CertificateType.RawPublicKey) { derEncoding = TlsUtils.readOpaque24(buf, 1); } else { derEncoding = certListData; buf.skip(totalLength); } TlsCertificate cert = crypto.createCertificate(certType, derEncoding); if (certificate_list.isEmpty() && endPointHashOutput != null) { calculateEndPointHash(context, cert, derEncoding, endPointHashOutput); } Map extensions = null; if (isTLSv13) { byte[] extEncoding = TlsUtils.readOpaque16(buf); extensions = TlsProtocol.readExtensionsData13(HandshakeType.certificate, extEncoding); } certificate_list.addElement(new CertificateEntry(cert, extensions)); } CertificateEntry[] certificateList = new CertificateEntry[certificate_list.size()]; for (int i = 0; i < certificate_list.size(); i++) { certificateList[i] = (CertificateEntry)certificate_list.elementAt(i); } return new Certificate(certType, certificateRequestContext, certificateList); } protected static void calculateEndPointHash(TlsContext context, TlsCertificate cert, byte[] encoding, OutputStream output) throws IOException { byte[] endPointHash = TlsUtils.calculateEndPointHash(context, cert, encoding); if (endPointHash != null && endPointHash.length > 0) { output.write(endPointHash); } } protected TlsCertificate[] cloneCertificateList() { int count = certificateEntryList.length; if (0 == count) { return EMPTY_CERTS; } TlsCertificate[] result = new TlsCertificate[count]; for (int i = 0; i < count; ++i) { result[i] = certificateEntryList[i].getCertificate(); } return result; } protected CertificateEntry[] cloneCertificateEntryList() { int count = certificateEntryList.length; if (0 == count) { return EMPTY_CERT_ENTRIES; } CertificateEntry[] result = new CertificateEntry[count]; System.arraycopy(certificateEntryList, 0, result, 0, count); return result; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy