org.bouncycastle.tls.CertificateStatus Maven / Gradle / Ivy
package org.bouncycastle.tls;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Vector;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ocsp.OCSPResponse;
public class CertificateStatus
{
protected short statusType;
protected Object response;
public CertificateStatus(short statusType, Object response)
{
if (!isCorrectType(statusType, response))
{
throw new IllegalArgumentException("'response' is not an instance of the correct type");
}
this.statusType = statusType;
this.response = response;
}
public short getStatusType()
{
return statusType;
}
public Object getResponse()
{
return response;
}
public OCSPResponse getOCSPResponse()
{
if (!isCorrectType(CertificateStatusType.ocsp, response))
{
throw new IllegalStateException("'response' is not an OCSPResponse");
}
return (OCSPResponse)response;
}
/**
* @return a {@link Vector} of (possibly null) {@link OCSPResponse}.
*/
public Vector getOCSPResponseList()
{
if (!isCorrectType(CertificateStatusType.ocsp_multi, response))
{
throw new IllegalStateException("'response' is not an OCSPResponseList");
}
return (Vector)response;
}
/**
* Encode this {@link CertificateStatus} to an {@link OutputStream}.
*
* @param output
* the {@link OutputStream} to encode to.
* @throws IOException
*/
public void encode(OutputStream output) throws IOException
{
TlsUtils.writeUint8(statusType, output);
switch (statusType)
{
case CertificateStatusType.ocsp:
{
OCSPResponse ocspResponse = (OCSPResponse)response;
byte[] derEncoding = ocspResponse.getEncoded(ASN1Encoding.DER);
TlsUtils.writeOpaque24(derEncoding, output);
break;
}
case CertificateStatusType.ocsp_multi:
{
Vector ocspResponseList = (Vector)response;
int count = ocspResponseList.size();
Vector derEncodings = new Vector(count);
long totalLength = 0;
for (int i = 0; i < count; ++i)
{
OCSPResponse ocspResponse = (OCSPResponse)ocspResponseList.elementAt(i);
if (ocspResponse == null)
{
derEncodings.addElement(TlsUtils.EMPTY_BYTES);
}
else
{
byte[] derEncoding = ocspResponse.getEncoded(ASN1Encoding.DER);
derEncodings.addElement(derEncoding);
totalLength += derEncoding.length;
}
totalLength += 3;
}
TlsUtils.checkUint24(totalLength);
TlsUtils.writeUint24((int)totalLength, output);
for (int i = 0; i < count; ++i)
{
byte[] derEncoding = (byte[])derEncodings.elementAt(i);
TlsUtils.writeOpaque24(derEncoding, output);
}
break;
}
default:
throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
/**
* Parse a {@link CertificateStatus} from an {@link InputStream}.
*
* @param context
* the {@link TlsContext} of the current connection.
* @param input
* the {@link InputStream} to parse from.
* @return a {@link CertificateStatus} object.
* @throws IOException
*/
public static CertificateStatus parse(TlsContext context, InputStream input) throws IOException
{
SecurityParameters securityParameters = context.getSecurityParametersHandshake();
Certificate peerCertificate = securityParameters.getPeerCertificate();
if (null == peerCertificate || peerCertificate.isEmpty()
|| CertificateType.X509 != peerCertificate.getCertificateType())
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
final int certificateCount = peerCertificate.getLength();
final int statusRequestVersion = securityParameters.getStatusRequestVersion();
short status_type = TlsUtils.readUint8(input);
Object response;
switch (status_type)
{
case CertificateStatusType.ocsp:
{
requireStatusRequestVersion(1, statusRequestVersion);
byte[] derEncoding = TlsUtils.readOpaque24(input, 1);
response = parseOCSPResponse(derEncoding);
break;
}
case CertificateStatusType.ocsp_multi:
{
requireStatusRequestVersion(2, statusRequestVersion);
byte[] ocsp_response_list = TlsUtils.readOpaque24(input, 1);
ByteArrayInputStream buf = new ByteArrayInputStream(ocsp_response_list);
Vector ocspResponseList = new Vector();
while (buf.available() > 0)
{
if (ocspResponseList.size() >= certificateCount)
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
int length = TlsUtils.readUint24(buf);
if (length < 1)
{
ocspResponseList.addElement(null);
}
else
{
byte[] derEncoding = TlsUtils.readFully(length, buf);
ocspResponseList.addElement(parseOCSPResponse(derEncoding));
}
}
ocspResponseList.trimToSize();
response = ocspResponseList;
break;
}
default:
throw new TlsFatalAlert(AlertDescription.decode_error);
}
return new CertificateStatus(status_type, response);
}
protected static boolean isCorrectType(short statusType, Object response)
{
switch (statusType)
{
case CertificateStatusType.ocsp:
return response instanceof OCSPResponse;
case CertificateStatusType.ocsp_multi:
return isOCSPResponseList(response);
default:
throw new IllegalArgumentException("'statusType' is an unsupported CertificateStatusType");
}
}
protected static boolean isOCSPResponseList(Object response)
{
if (!(response instanceof Vector))
{
return false;
}
Vector v = (Vector)response;
int count = v.size();
if (count < 1)
{
return false;
}
for (int i = 0; i < count; ++i)
{
Object e = v.elementAt(i);
if (null != e && !(e instanceof OCSPResponse))
{
return false;
}
}
return true;
}
protected static OCSPResponse parseOCSPResponse(byte[] derEncoding) throws IOException
{
ASN1Primitive asn1 = TlsUtils.readASN1Object(derEncoding);
OCSPResponse ocspResponse = OCSPResponse.getInstance(asn1);
TlsUtils.requireDEREncoding(ocspResponse, derEncoding);
return ocspResponse;
}
protected static void requireStatusRequestVersion(int minVersion, int statusRequestVersion)
throws IOException
{
if (statusRequestVersion < minVersion)
{
throw new TlsFatalAlert(AlertDescription.decode_error);
}
}
}