]> WPIA git - gigi.git/blob - src/club/wpia/gigi/crypto/OCSPResponse.java
add: comments with ASN.1 Syntax for various objects in an OCSP Response
[gigi.git] / src / club / wpia / gigi / crypto / OCSPResponse.java
1 package club.wpia.gigi.crypto;
2
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;
9 import java.util.Date;
10
11 import javax.security.auth.x500.X500Principal;
12
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;
18
19 public class OCSPResponse {
20
21     private Extension nonceExt;
22
23     public static class SingleResponse {
24
25         private final CertId target;
26
27         private final Date thisUpdate;
28
29         private final Date nextUpdate;
30
31         private final Date revoked;
32
33         private final CRLReason res;
34
35         private final boolean unknown;
36
37         public SingleResponse(CertId target, Date thisUpdate, Date nextUpdate) {
38             this(target, thisUpdate, nextUpdate, null);
39         }
40
41         public SingleResponse(CertId target, Date thisUpdate, Date nextUpdate, Date revoked) {
42             this(target, thisUpdate, nextUpdate, revoked, null);
43         }
44
45         public SingleResponse(CertId target, Date thisUpdate, Date nextUpdate, Date revoked, CRLReason res) {
46             this.target = target;
47             this.thisUpdate = thisUpdate;
48             this.nextUpdate = nextUpdate;
49             this.revoked = revoked;
50             this.res = res;
51             unknown = false;
52         }
53
54         public SingleResponse(CertId target, Date thisUpdate, Date nextUpdate, boolean unkown) {
55             this.target = target;
56             this.thisUpdate = thisUpdate;
57             this.nextUpdate = nextUpdate;
58             this.revoked = null;
59             this.res = null;
60             this.unknown = unkown;
61         }
62
63         // @formatter:off
64         // from: https://tools.ietf.org/html/rfc6960#appendix-B.1
65         // SingleResponse ::= SEQUENCE {
66         //     certID                  CertID,
67         //     certStatus              CertStatus,
68         //     thisUpdate              GeneralizedTime,
69         //     nextUpdate          [0] EXPLICIT GeneralizedTime OPTIONAL,
70         //     singleExtensions    [1] EXPLICIT Extensions OPTIONAL }
71         // 
72         //  CertStatus ::= CHOICE {
73         //     good                [0] IMPLICIT NULL,
74         //     revoked             [1] IMPLICIT RevokedInfo,
75         //     unknown             [2] IMPLICIT UnknownInfo }
76         // 
77         //  RevokedInfo ::= SEQUENCE {
78         //     revocationTime          GeneralizedTime,
79         //     revocationReason    [0] EXPLICIT CRLReason OPTIONAL }
80         // @formatter:on
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);
87                         target.write(0);
88                     } else if (revoked == null && unknown) {
89                         target.putTag(DerValue.TAG_CONTEXT, false, (byte) 2);
90                         target.write(0);
91                     } else {
92                         try (DerOutputStream gt = new DerOutputStream()) {
93                             gt.putGeneralizedTime(revoked);
94                             // revocationReason [0] EXPLICIT CRLReason OPTIONAL
95                             if (res != null) {
96                                 try (DerOutputStream crlr = new DerOutputStream()) {
97                                     crlr.putEnumerated(res.ordinal());
98                                     gt.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0), crlr);
99                                 }
100                             }
101
102                             target.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 1), gt);
103                         }
104
105                     }
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);
110                     }
111
112                     r.write(DerValue.tag_Sequence, target);
113                 }
114                 return new DerValue(r.toByteArray());
115             }
116         }
117     }
118
119     private final SingleResponse[] res;
120
121     private X509Certificate[] signers;
122
123     private final X500Principal dn;
124
125     private final byte[] keyHash;
126
127     public OCSPResponse(X500Principal dn, SingleResponse[] res) {
128         this.dn = dn;
129         keyHash = null;
130         this.res = res;
131     }
132
133     public OCSPResponse(byte[] keyHash, SingleResponse[] res) {
134         dn = null;
135         this.keyHash = keyHash;
136         this.res = res;
137     }
138
139     private OCSPResponse() {
140         dn = null;
141         res = null;
142         keyHash = null;
143     }
144
145     public void setSigners(X509Certificate[] signers) {
146         this.signers = signers;
147     }
148
149     /**
150      * Produce possibly signed binary data for this OCSPResponse
151      * 
152      * @param s
153      *            the signature to sign the data with. Always required for
154      *            publicly visible instance.
155      * @return the binary representation
156      * @throws IOException
157      *             if IO fails.
158      * @throws GeneralSecurityException
159      *             if signing fails.
160      */
161     // @formatter:off
162     // from: https://tools.ietf.org/html/rfc6960#appendix-B.1
163     // OCSPResponse ::= SEQUENCE {
164     //    responseStatus          OCSPResponseStatus,
165     //    responseBytes       [0] EXPLICIT ResponseBytes OPTIONAL }
166     // 
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
175     // }
176     // 
177     // ResponseBytes ::= SEQUENCE {
178     //    responseType            OBJECT IDENTIFIER,
179     //    response                OCTET STRING }
180     // @formatter:on
181     public byte[] produceResponce(Signature s) throws IOException, GeneralSecurityException {
182         try (DerOutputStream dos2 = new DerOutputStream()) {
183             try (DerOutputStream dos = new DerOutputStream()) {
184                 if (res != null) {
185                     dos.putEnumerated(0); // successful
186                     ObjectIdentifier ocspBasic = new ObjectIdentifier(new int[] {
187                             1, 3, 6, 1, 5, 5, 7, 48, 1, 1
188                     });
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);
194                         }
195                         dos.write((byte) 0xA0, tagS);
196                     }
197                 } else {
198                     dos.putEnumerated(1); // malformed request
199                 }
200
201                 dos2.write(DerValue.tag_Sequence, dos);
202             }
203             return dos2.toByteArray();
204         }
205
206     }
207
208     // @formatter:off
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 }
215     // @formatter:on
216     private byte[] produceBasicOCSPResponse(Signature s) throws IOException, GeneralSecurityException {
217
218         try (DerOutputStream o = new DerOutputStream()) {
219             try (DerOutputStream basicReponse = new DerOutputStream()) {
220                 produceResponseData(basicReponse);
221                 byte[] toSign = basicReponse.toByteArray();
222
223                 AlgorithmId.get(s.getAlgorithm()).encode(basicReponse);
224                 s.update(toSign);
225                 basicReponse.putBitString(s.sign());
226
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());
232                             }
233                             certSeq.write(DerValue.tag_Sequence, certs);
234                         }
235                         basicReponse.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0), certSeq);
236                     }
237                 }
238
239                 o.write(DerValue.tag_Sequence, basicReponse.toByteArray());
240             }
241             return o.toByteArray();
242         }
243
244     }
245
246     // @formatter:off
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 }
254     //  
255     //  ResponderID ::= CHOICE {
256     //     byName              [1] Name,
257     //     byKey               [2] KeyHash }
258     //
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)
264     // @formatter:on
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];
270             int i = 0;
271             for (SingleResponse c : res) {
272                 tgt[i++] = c.produceSingleResponse();
273             }
274             tbsResp.putSequence(tgt);
275
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);
282                     }
283                 }
284             }
285             basicReponse.write(DerValue.tag_Sequence, tbsResp.toByteArray());
286         }
287     }
288
289     private void produceResponderId(DerOutputStream tbsResp) throws IOException {
290         if (dn != null) {
291             tbsResp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 1), dn.getEncoded());
292         } else {
293             try (DerOutputStream dos = new DerOutputStream()) {
294                 dos.putOctetString(keyHash);
295                 tbsResp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 2), dos);
296             }
297             // by hash
298         }
299     }
300
301     public void updateNonce(OCSPRequest or) {
302         nonceExt = or.getNonceExt();
303     }
304
305     public static byte[] invalid() throws IOException, GeneralSecurityException {
306         return new OCSPResponse().produceResponce(null);
307     }
308 }