1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty5.example.ocsp;
18
19 import io.netty5.util.CharsetUtil;
20 import org.bouncycastle.asn1.ASN1Encodable;
21 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
22 import org.bouncycastle.asn1.ASN1Primitive;
23 import org.bouncycastle.asn1.BERTags;
24 import org.bouncycastle.asn1.DERTaggedObject;
25 import org.bouncycastle.asn1.DLSequence;
26 import org.bouncycastle.asn1.x509.Extension;
27 import org.bouncycastle.cert.ocsp.OCSPReq;
28 import org.bouncycastle.cert.ocsp.OCSPResp;
29 import org.bouncycastle.x509.extension.X509ExtensionUtil;
30
31 import javax.net.ssl.HttpsURLConnection;
32 import java.io.ByteArrayOutputStream;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.io.OutputStream;
36 import java.net.HttpURLConnection;
37 import java.net.URI;
38 import java.net.URL;
39 import java.security.cert.X509Certificate;
40 import java.util.concurrent.TimeUnit;
41
42 public final class OcspUtils {
43
44
45
46
47
48 private static final ASN1ObjectIdentifier OCSP_RESPONDER_OID
49 = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1").intern();
50
51 private static final String OCSP_REQUEST_TYPE = "application/ocsp-request";
52
53 private static final String OCSP_RESPONSE_TYPE = "application/ocsp-response";
54
55 private OcspUtils() {
56 }
57
58
59
60
61 public static URI ocspUri(X509Certificate certificate) throws IOException {
62 byte[] value = certificate.getExtensionValue(Extension.authorityInfoAccess.getId());
63 if (value == null) {
64 return null;
65 }
66
67 ASN1Primitive authorityInfoAccess = X509ExtensionUtil.fromExtensionValue(value);
68 if (!(authorityInfoAccess instanceof DLSequence)) {
69 return null;
70 }
71
72 DLSequence aiaSequence = (DLSequence) authorityInfoAccess;
73 DERTaggedObject taggedObject = findObject(aiaSequence, OCSP_RESPONDER_OID, DERTaggedObject.class);
74 if (taggedObject == null) {
75 return null;
76 }
77
78 if (taggedObject.getTagNo() != BERTags.OBJECT_IDENTIFIER) {
79 return null;
80 }
81
82 byte[] encoded = taggedObject.getEncoded();
83 int length = (int) encoded[1] & 0xFF;
84 String uri = new String(encoded, 2, length, CharsetUtil.UTF_8);
85 return URI.create(uri);
86 }
87
88 private static <T> T findObject(DLSequence sequence, ASN1ObjectIdentifier oid, Class<T> type) {
89 for (ASN1Encodable element : sequence) {
90 if (!(element instanceof DLSequence)) {
91 continue;
92 }
93
94 DLSequence subSequence = (DLSequence) element;
95 if (subSequence.size() != 2) {
96 continue;
97 }
98
99 ASN1Encodable key = subSequence.getObjectAt(0);
100 ASN1Encodable value = subSequence.getObjectAt(1);
101
102 if (key.equals(oid) && type.isInstance(value)) {
103 return type.cast(value);
104 }
105 }
106
107 return null;
108 }
109
110
111
112
113
114
115 public static OCSPResp request(URI uri, OCSPReq request, long timeout, TimeUnit unit) throws IOException {
116 byte[] encoded = request.getEncoded();
117
118 URL url = uri.toURL();
119 HttpURLConnection connection = (HttpURLConnection) url.openConnection();
120 try {
121 connection.setConnectTimeout((int) unit.toMillis(timeout));
122 connection.setReadTimeout((int) unit.toMillis(timeout));
123 connection.setDoOutput(true);
124 connection.setDoInput(true);
125 connection.setRequestMethod("POST");
126 connection.setRequestProperty("host", uri.getHost());
127 connection.setRequestProperty("content-type", OCSP_REQUEST_TYPE);
128 connection.setRequestProperty("accept", OCSP_RESPONSE_TYPE);
129 connection.setRequestProperty("content-length", String.valueOf(encoded.length));
130
131 try (OutputStream out = connection.getOutputStream()) {
132 out.write(encoded);
133 out.flush();
134
135 try (InputStream in = connection.getInputStream()) {
136 int code = connection.getResponseCode();
137 if (code != HttpsURLConnection.HTTP_OK) {
138 throw new IOException("Unexpected status-code=" + code);
139 }
140
141 String contentType = connection.getContentType();
142 if (!contentType.equalsIgnoreCase(OCSP_RESPONSE_TYPE)) {
143 throw new IOException("Unexpected content-type=" + contentType);
144 }
145
146 int contentLength = connection.getContentLength();
147 if (contentLength == -1) {
148
149 contentLength = Integer.MAX_VALUE;
150 }
151
152 try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
153 byte[] buffer = new byte[8192];
154 int length;
155
156 while ((length = in.read(buffer)) != -1) {
157 baos.write(buffer, 0, length);
158
159 if (baos.size() >= contentLength) {
160 break;
161 }
162 }
163 return new OCSPResp(baos.toByteArray());
164 }
165 }
166 }
167 } finally {
168 connection.disconnect();
169 }
170 }
171 }