]> WPIA git - gigi.git/blob - src/club/wpia/gigi/crypto/OCSPResponse.java
add: implement OCSP serving
[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         private DerValue produceSingleResponse() throws IOException {
64             try (DerOutputStream r = new DerOutputStream()) {
65                 try (DerOutputStream target = new DerOutputStream()) {
66                     this.target.encode(target);
67                     if (revoked == null && !unknown) {
68                         target.putTag(DerValue.TAG_CONTEXT, false, (byte) 0);
69                         target.write(0);
70                     } else if (revoked == null && unknown) {
71                         target.putTag(DerValue.TAG_CONTEXT, false, (byte) 2);
72                         target.write(0);
73                     } else {
74                         try (DerOutputStream gt = new DerOutputStream()) {
75                             gt.putGeneralizedTime(revoked);
76                             // revocationReason [0] EXPLICIT CRLReason OPTIONAL
77                             if (res != null) {
78                                 try (DerOutputStream crlr = new DerOutputStream()) {
79                                     crlr.putEnumerated(res.ordinal());
80                                     gt.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0), crlr);
81                                 }
82                             }
83
84                             target.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 1), gt);
85                         }
86
87                     }
88                     target.putGeneralizedTime(thisUpdate);
89                     try (DerOutputStream gt = new DerOutputStream()) {
90                         gt.putGeneralizedTime(nextUpdate);
91                         target.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0), gt);
92                     }
93
94                     r.write(DerValue.tag_Sequence, target);
95                 }
96                 return new DerValue(r.toByteArray());
97             }
98         }
99     }
100
101     private final SingleResponse[] res;
102
103     private X509Certificate[] signers;
104
105     private final X500Principal dn;
106
107     private final byte[] keyHash;
108
109     public OCSPResponse(X500Principal dn, SingleResponse[] res) {
110         this.dn = dn;
111         keyHash = null;
112         this.res = res;
113     }
114
115     public OCSPResponse(byte[] keyHash, SingleResponse[] res) {
116         dn = null;
117         this.keyHash = keyHash;
118         this.res = res;
119     }
120
121     private OCSPResponse() {
122         dn = null;
123         res = null;
124         keyHash = null;
125     }
126
127     public void setSigners(X509Certificate[] signers) {
128         this.signers = signers;
129     }
130
131     /**
132      * Produce possibly signed binary data for this OCSPResponse
133      * 
134      * @param s
135      *            the signature to sign the data with. Always required for
136      *            publicly visible instance.
137      * @return the binary representation
138      * @throws IOException
139      *             if IO fails.
140      * @throws GeneralSecurityException
141      *             if signing fails.
142      */
143     public byte[] produceResponce(Signature s) throws IOException, GeneralSecurityException {
144         try (DerOutputStream dos2 = new DerOutputStream()) {
145             try (DerOutputStream dos = new DerOutputStream()) {
146                 if (res != null) {
147                     dos.putEnumerated(0); // successful
148                     ObjectIdentifier ocspBasic = new ObjectIdentifier(new int[] {
149                             1, 3, 6, 1, 5, 5, 7, 48, 1, 1
150                     });
151                     try (DerOutputStream tagS = new DerOutputStream()) {
152                         try (DerOutputStream responseBytes = new DerOutputStream()) {
153                             responseBytes.putOID(ocspBasic);
154                             responseBytes.putOctetString(produceBasicOCSPResponse(s));
155                             tagS.write(DerValue.tag_Sequence, responseBytes);
156                         }
157                         dos.write((byte) 0xA0, tagS);
158                     }
159                 } else {
160                     dos.putEnumerated(1); // malformed request
161                 }
162
163                 dos2.write(DerValue.tag_Sequence, dos);
164             }
165             return dos2.toByteArray();
166         }
167
168     }
169
170     private byte[] produceBasicOCSPResponse(Signature s) throws IOException, GeneralSecurityException {
171
172         try (DerOutputStream o = new DerOutputStream()) {
173             try (DerOutputStream basicReponse = new DerOutputStream()) {
174                 produceResponseData(basicReponse);
175                 byte[] toSign = basicReponse.toByteArray();
176
177                 AlgorithmId.get(s.getAlgorithm()).encode(basicReponse);
178                 s.update(toSign);
179                 basicReponse.putBitString(s.sign());
180
181                 if (signers != null) {
182                     try (DerOutputStream certSeq = new DerOutputStream()) {
183                         try (DerOutputStream certs = new DerOutputStream()) {
184                             for (X509Certificate signer : signers) {
185                                 certs.write(signer.getEncoded());
186                             }
187                             certSeq.write(DerValue.tag_Sequence, certs);
188                         }
189                         basicReponse.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0), certSeq);
190                     }
191                 }
192
193                 o.write(DerValue.tag_Sequence, basicReponse.toByteArray());
194             }
195             return o.toByteArray();
196         }
197
198     }
199
200     private void produceResponseData(DerOutputStream basicReponse) throws IOException {
201         try (DerOutputStream tbsResp = new DerOutputStream()) {
202             produceResponderId(tbsResp);
203             tbsResp.putGeneralizedTime(new Date(System.currentTimeMillis()));
204             DerValue[] tgt = new DerValue[res.length];
205             int i = 0;
206             for (SingleResponse c : res) {
207                 tgt[i++] = c.produceSingleResponse();
208             }
209             tbsResp.putSequence(tgt);
210
211             if (nonceExt != null) {
212                 try (DerOutputStream extsSeq = new DerOutputStream()) {
213                     try (DerOutputStream extsOut = new DerOutputStream()) {
214                         nonceExt.encode(extsOut);
215                         extsSeq.write(DerValue.tag_Sequence, extsOut);
216                         tbsResp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 1), extsSeq);
217                     }
218                 }
219             }
220             basicReponse.write(DerValue.tag_Sequence, tbsResp.toByteArray());
221         }
222     }
223
224     private void produceResponderId(DerOutputStream tbsResp) throws IOException {
225         if (dn != null) {
226             tbsResp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 1), dn.getEncoded());
227         } else {
228             try (DerOutputStream dos = new DerOutputStream()) {
229                 dos.putOctetString(keyHash);
230                 tbsResp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 2), dos);
231             }
232             // by hash
233         }
234     }
235
236     public void updateNonce(OCSPRequest or) {
237         nonceExt = or.getNonceExt();
238     }
239
240     public static byte[] invalid() throws IOException, GeneralSecurityException {
241         return new OCSPResponse().produceResponce(null);
242     }
243 }