1 package club.wpia.gigi.crypto;
3 import java.io.IOException;
4 import java.security.GeneralSecurityException;
5 import java.security.Signature;
6 import java.security.cert.CRLReason;
7 import java.security.cert.Extension;
8 import java.security.cert.X509Certificate;
11 import javax.security.auth.x500.X500Principal;
13 import sun.security.provider.certpath.CertId;
14 import sun.security.util.DerOutputStream;
15 import sun.security.util.DerValue;
16 import sun.security.util.ObjectIdentifier;
17 import sun.security.x509.AlgorithmId;
19 public class OCSPResponse {
21 private Extension nonceExt;
23 public static class SingleResponse {
25 private final CertId target;
27 private final Date thisUpdate;
29 private final Date nextUpdate;
31 private final Date revoked;
33 private final CRLReason res;
35 private final boolean unknown;
37 public SingleResponse(CertId target, Date thisUpdate, Date nextUpdate) {
38 this(target, thisUpdate, nextUpdate, null);
41 public SingleResponse(CertId target, Date thisUpdate, Date nextUpdate, Date revoked) {
42 this(target, thisUpdate, nextUpdate, revoked, null);
45 public SingleResponse(CertId target, Date thisUpdate, Date nextUpdate, Date revoked, CRLReason res) {
47 this.thisUpdate = thisUpdate;
48 this.nextUpdate = nextUpdate;
49 this.revoked = revoked;
54 public SingleResponse(CertId target, Date thisUpdate, Date nextUpdate, boolean unkown) {
56 this.thisUpdate = thisUpdate;
57 this.nextUpdate = nextUpdate;
60 this.unknown = unkown;
64 // from: https://tools.ietf.org/html/rfc6960#appendix-B.1
65 // SingleResponse ::= SEQUENCE {
67 // certStatus CertStatus,
68 // thisUpdate GeneralizedTime,
69 // nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
70 // singleExtensions [1] EXPLICIT Extensions OPTIONAL }
72 // CertStatus ::= CHOICE {
73 // good [0] IMPLICIT NULL,
74 // revoked [1] IMPLICIT RevokedInfo,
75 // unknown [2] IMPLICIT UnknownInfo }
77 // RevokedInfo ::= SEQUENCE {
78 // revocationTime GeneralizedTime,
79 // revocationReason [0] EXPLICIT CRLReason OPTIONAL }
81 private DerValue produceSingleResponse() throws IOException {
82 try (DerOutputStream r = new DerOutputStream()) {
83 try (DerOutputStream target = new DerOutputStream()) {
84 this.target.encode(target);
85 if (revoked == null && !unknown) {
86 target.putTag(DerValue.TAG_CONTEXT, false, (byte) 0);
88 } else if (revoked == null && unknown) {
89 target.putTag(DerValue.TAG_CONTEXT, false, (byte) 2);
92 try (DerOutputStream gt = new DerOutputStream()) {
93 gt.putGeneralizedTime(revoked);
94 // revocationReason [0] EXPLICIT CRLReason OPTIONAL
96 try (DerOutputStream crlr = new DerOutputStream()) {
97 crlr.putEnumerated(res.ordinal());
98 gt.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0), crlr);
102 target.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 1), gt);
106 target.putGeneralizedTime(thisUpdate);
107 try (DerOutputStream gt = new DerOutputStream()) {
108 gt.putGeneralizedTime(nextUpdate);
109 target.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0), gt);
112 r.write(DerValue.tag_Sequence, target);
114 return new DerValue(r.toByteArray());
119 private final SingleResponse[] res;
121 private X509Certificate[] signers;
123 private final X500Principal dn;
125 private final byte[] keyHash;
127 public OCSPResponse(X500Principal dn, SingleResponse[] res) {
133 public OCSPResponse(byte[] keyHash, SingleResponse[] res) {
135 this.keyHash = keyHash;
139 private OCSPResponse() {
145 public void setSigners(X509Certificate[] signers) {
146 this.signers = signers;
150 * Produce possibly signed binary data for this OCSPResponse
153 * the signature to sign the data with. Always required for
154 * publicly visible instance.
155 * @return the binary representation
156 * @throws IOException
158 * @throws GeneralSecurityException
162 // from: https://tools.ietf.org/html/rfc6960#appendix-B.1
163 // OCSPResponse ::= SEQUENCE {
164 // responseStatus OCSPResponseStatus,
165 // responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
167 // OCSPResponseStatus ::= ENUMERATED {
168 // successful (0), -- Response has valid confirmations
169 // malformedRequest (1), -- Illegal confirmation request
170 // internalError (2), -- Internal error in issuer
171 // tryLater (3), -- Try again later
172 // -- (4) is not used
173 // sigRequired (5), -- Must sign the request
174 // unauthorized (6) -- Request unauthorized
177 // ResponseBytes ::= SEQUENCE {
178 // responseType OBJECT IDENTIFIER,
179 // response OCTET STRING }
181 public byte[] produceResponce(Signature s) throws IOException, GeneralSecurityException {
182 try (DerOutputStream dos2 = new DerOutputStream()) {
183 try (DerOutputStream dos = new DerOutputStream()) {
185 dos.putEnumerated(0); // successful
186 ObjectIdentifier ocspBasic = new ObjectIdentifier(new int[] {
187 1, 3, 6, 1, 5, 5, 7, 48, 1, 1
189 try (DerOutputStream tagS = new DerOutputStream()) {
190 try (DerOutputStream responseBytes = new DerOutputStream()) {
191 responseBytes.putOID(ocspBasic);
192 responseBytes.putOctetString(produceBasicOCSPResponse(s));
193 tagS.write(DerValue.tag_Sequence, responseBytes);
195 dos.write((byte) 0xA0, tagS);
198 dos.putEnumerated(1); // malformed request
201 dos2.write(DerValue.tag_Sequence, dos);
203 return dos2.toByteArray();
209 // from: https://tools.ietf.org/html/rfc6960#appendix-B.1
210 // BasicOCSPResponse ::= SEQUENCE {
211 // tbsResponseData ResponseData,
212 // signatureAlgorithm AlgorithmIdentifier,
213 // signature BIT STRING,
214 // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
216 private byte[] produceBasicOCSPResponse(Signature s) throws IOException, GeneralSecurityException {
218 try (DerOutputStream o = new DerOutputStream()) {
219 try (DerOutputStream basicReponse = new DerOutputStream()) {
220 produceResponseData(basicReponse);
221 byte[] toSign = basicReponse.toByteArray();
223 AlgorithmId.get(s.getAlgorithm()).encode(basicReponse);
225 basicReponse.putBitString(s.sign());
227 if (signers != null) {
228 try (DerOutputStream certSeq = new DerOutputStream()) {
229 try (DerOutputStream certs = new DerOutputStream()) {
230 for (X509Certificate signer : signers) {
231 certs.write(signer.getEncoded());
233 certSeq.write(DerValue.tag_Sequence, certs);
235 basicReponse.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0), certSeq);
239 o.write(DerValue.tag_Sequence, basicReponse.toByteArray());
241 return o.toByteArray();
247 // from: https://tools.ietf.org/html/rfc6960#appendix-B.1
248 // ResponseData ::= SEQUENCE {
249 // version [0] EXPLICIT Version DEFAULT v1,
250 // responderID ResponderID,
251 // producedAt GeneralizedTime,
252 // responses SEQUENCE OF SingleResponse,
253 // responseExtensions [1] EXPLICIT Extensions OPTIONAL }
255 // ResponderID ::= CHOICE {
257 // byKey [2] KeyHash }
259 // KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
260 // -- (i.e., the SHA-1 hash of the value of the
261 // -- BIT STRING subjectPublicKey [excluding
262 // -- the tag, length, and number of unused
263 // -- bits] in the responder's certificate)
265 private void produceResponseData(DerOutputStream basicReponse) throws IOException {
266 try (DerOutputStream tbsResp = new DerOutputStream()) {
267 produceResponderId(tbsResp);
268 tbsResp.putGeneralizedTime(new Date(System.currentTimeMillis()));
269 DerValue[] tgt = new DerValue[res.length];
271 for (SingleResponse c : res) {
272 tgt[i++] = c.produceSingleResponse();
274 tbsResp.putSequence(tgt);
276 if (nonceExt != null) {
277 try (DerOutputStream extsSeq = new DerOutputStream()) {
278 try (DerOutputStream extsOut = new DerOutputStream()) {
279 nonceExt.encode(extsOut);
280 extsSeq.write(DerValue.tag_Sequence, extsOut);
281 tbsResp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 1), extsSeq);
285 basicReponse.write(DerValue.tag_Sequence, tbsResp.toByteArray());
289 private void produceResponderId(DerOutputStream tbsResp) throws IOException {
291 tbsResp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 1), dn.getEncoded());
293 try (DerOutputStream dos = new DerOutputStream()) {
294 dos.putOctetString(keyHash);
295 tbsResp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 2), dos);
301 public void updateNonce(OCSPRequest or) {
302 nonceExt = or.getNonceExt();
305 public static byte[] invalid() throws IOException, GeneralSecurityException {
306 return new OCSPResponse().produceResponce(null);