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