|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #include "cert.h" |
|
6 #include "certt.h" |
|
7 #include "secder.h" |
|
8 #include "key.h" |
|
9 #include "secitem.h" |
|
10 #include "secasn1.h" |
|
11 #include "secerr.h" |
|
12 |
|
13 SEC_ASN1_MKSUB(SEC_AnyTemplate) |
|
14 |
|
15 const SEC_ASN1Template CERT_AttributeTemplate[] = { |
|
16 { SEC_ASN1_SEQUENCE, |
|
17 0, NULL, sizeof(CERTAttribute) }, |
|
18 { SEC_ASN1_OBJECT_ID, offsetof(CERTAttribute, attrType) }, |
|
19 { SEC_ASN1_SET_OF | SEC_ASN1_XTRN, offsetof(CERTAttribute, attrValue), |
|
20 SEC_ASN1_SUB(SEC_AnyTemplate) }, |
|
21 { 0 } |
|
22 }; |
|
23 |
|
24 const SEC_ASN1Template CERT_SetOfAttributeTemplate[] = { |
|
25 { SEC_ASN1_SET_OF, 0, CERT_AttributeTemplate }, |
|
26 }; |
|
27 |
|
28 const SEC_ASN1Template CERT_CertificateRequestTemplate[] = { |
|
29 { SEC_ASN1_SEQUENCE, |
|
30 0, NULL, sizeof(CERTCertificateRequest) }, |
|
31 { SEC_ASN1_INTEGER, |
|
32 offsetof(CERTCertificateRequest,version) }, |
|
33 { SEC_ASN1_INLINE, |
|
34 offsetof(CERTCertificateRequest,subject), |
|
35 CERT_NameTemplate }, |
|
36 { SEC_ASN1_INLINE, |
|
37 offsetof(CERTCertificateRequest,subjectPublicKeyInfo), |
|
38 CERT_SubjectPublicKeyInfoTemplate }, |
|
39 { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, |
|
40 offsetof(CERTCertificateRequest,attributes), |
|
41 CERT_SetOfAttributeTemplate }, |
|
42 { 0 } |
|
43 }; |
|
44 |
|
45 SEC_ASN1_CHOOSER_IMPLEMENT(CERT_CertificateRequestTemplate) |
|
46 |
|
47 CERTCertificate * |
|
48 CERT_CreateCertificate(unsigned long serialNumber, |
|
49 CERTName *issuer, |
|
50 CERTValidity *validity, |
|
51 CERTCertificateRequest *req) |
|
52 { |
|
53 CERTCertificate *c; |
|
54 int rv; |
|
55 PLArenaPool *arena; |
|
56 |
|
57 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
|
58 |
|
59 if ( !arena ) { |
|
60 return(0); |
|
61 } |
|
62 |
|
63 c = (CERTCertificate *)PORT_ArenaZAlloc(arena, sizeof(CERTCertificate)); |
|
64 |
|
65 if (!c) { |
|
66 PORT_FreeArena(arena, PR_FALSE); |
|
67 return 0; |
|
68 } |
|
69 |
|
70 c->referenceCount = 1; |
|
71 c->arena = arena; |
|
72 |
|
73 /* |
|
74 * Default is a plain version 1. |
|
75 * If extensions are added, it will get changed as appropriate. |
|
76 */ |
|
77 rv = DER_SetUInteger(arena, &c->version, SEC_CERTIFICATE_VERSION_1); |
|
78 if (rv) goto loser; |
|
79 |
|
80 rv = DER_SetUInteger(arena, &c->serialNumber, serialNumber); |
|
81 if (rv) goto loser; |
|
82 |
|
83 rv = CERT_CopyName(arena, &c->issuer, issuer); |
|
84 if (rv) goto loser; |
|
85 |
|
86 rv = CERT_CopyValidity(arena, &c->validity, validity); |
|
87 if (rv) goto loser; |
|
88 |
|
89 rv = CERT_CopyName(arena, &c->subject, &req->subject); |
|
90 if (rv) goto loser; |
|
91 rv = SECKEY_CopySubjectPublicKeyInfo(arena, &c->subjectPublicKeyInfo, |
|
92 &req->subjectPublicKeyInfo); |
|
93 if (rv) goto loser; |
|
94 |
|
95 return c; |
|
96 |
|
97 loser: |
|
98 CERT_DestroyCertificate(c); |
|
99 return 0; |
|
100 } |
|
101 |
|
102 /************************************************************************/ |
|
103 /* It's clear from the comments that the original author of this |
|
104 * function expected the template for certificate requests to treat |
|
105 * the attributes as a SET OF ANY. This function expected to be |
|
106 * passed an array of SECItems each of which contained an already encoded |
|
107 * Attribute. But the cert request template does not treat the |
|
108 * Attributes as a SET OF ANY, and AFAIK never has. Instead the template |
|
109 * encodes attributes as a SET OF xxxxxxx. That is, it expects to encode |
|
110 * each of the Attributes, not have them pre-encoded. Consequently an |
|
111 * array of SECItems containing encoded Attributes is of no value to this |
|
112 * function. But we cannot change the signature of this public function. |
|
113 * It must continue to take SECItems. |
|
114 * |
|
115 * I have recoded this function so that each SECItem contains an |
|
116 * encoded cert extension. The encoded cert extensions form the list for the |
|
117 * single attribute of the cert request. In this implementation there is at most |
|
118 * one attribute and it is always of type SEC_OID_PKCS9_EXTENSION_REQUEST. |
|
119 */ |
|
120 |
|
121 CERTCertificateRequest * |
|
122 CERT_CreateCertificateRequest(CERTName *subject, |
|
123 CERTSubjectPublicKeyInfo *spki, |
|
124 SECItem **attributes) |
|
125 { |
|
126 CERTCertificateRequest *certreq; |
|
127 PLArenaPool *arena; |
|
128 CERTAttribute * attribute; |
|
129 SECOidData * oidData; |
|
130 SECStatus rv; |
|
131 int i = 0; |
|
132 |
|
133 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
|
134 if ( arena == NULL ) { |
|
135 return NULL; |
|
136 } |
|
137 |
|
138 certreq = PORT_ArenaZNew(arena, CERTCertificateRequest); |
|
139 if (!certreq) { |
|
140 PORT_FreeArena(arena, PR_FALSE); |
|
141 return NULL; |
|
142 } |
|
143 /* below here it is safe to goto loser */ |
|
144 |
|
145 certreq->arena = arena; |
|
146 |
|
147 rv = DER_SetUInteger(arena, &certreq->version, |
|
148 SEC_CERTIFICATE_REQUEST_VERSION); |
|
149 if (rv != SECSuccess) |
|
150 goto loser; |
|
151 |
|
152 rv = CERT_CopyName(arena, &certreq->subject, subject); |
|
153 if (rv != SECSuccess) |
|
154 goto loser; |
|
155 |
|
156 rv = SECKEY_CopySubjectPublicKeyInfo(arena, |
|
157 &certreq->subjectPublicKeyInfo, |
|
158 spki); |
|
159 if (rv != SECSuccess) |
|
160 goto loser; |
|
161 |
|
162 certreq->attributes = PORT_ArenaZNewArray(arena, CERTAttribute*, 2); |
|
163 if(!certreq->attributes) |
|
164 goto loser; |
|
165 |
|
166 /* Copy over attribute information */ |
|
167 if (!attributes || !attributes[0]) { |
|
168 /* |
|
169 ** Invent empty attribute information. According to the |
|
170 ** pkcs#10 spec, attributes has this ASN.1 type: |
|
171 ** |
|
172 ** attributes [0] IMPLICIT Attributes |
|
173 ** |
|
174 ** Which means, we should create a NULL terminated list |
|
175 ** with the first entry being NULL; |
|
176 */ |
|
177 certreq->attributes[0] = NULL; |
|
178 return certreq; |
|
179 } |
|
180 |
|
181 /* allocate space for attributes */ |
|
182 attribute = PORT_ArenaZNew(arena, CERTAttribute); |
|
183 if (!attribute) |
|
184 goto loser; |
|
185 |
|
186 oidData = SECOID_FindOIDByTag( SEC_OID_PKCS9_EXTENSION_REQUEST ); |
|
187 PORT_Assert(oidData); |
|
188 if (!oidData) |
|
189 goto loser; |
|
190 rv = SECITEM_CopyItem(arena, &attribute->attrType, &oidData->oid); |
|
191 if (rv != SECSuccess) |
|
192 goto loser; |
|
193 |
|
194 for (i = 0; attributes[i] != NULL ; i++) |
|
195 ; |
|
196 attribute->attrValue = PORT_ArenaZNewArray(arena, SECItem *, i+1); |
|
197 if (!attribute->attrValue) |
|
198 goto loser; |
|
199 |
|
200 /* copy attributes */ |
|
201 for (i = 0; attributes[i]; i++) { |
|
202 /* |
|
203 ** Attributes are a SetOf Attribute which implies |
|
204 ** lexigraphical ordering. It is assumes that the |
|
205 ** attributes are passed in sorted. If we need to |
|
206 ** add functionality to sort them, there is an |
|
207 ** example in the PKCS 7 code. |
|
208 */ |
|
209 attribute->attrValue[i] = SECITEM_ArenaDupItem(arena, attributes[i]); |
|
210 if(!attribute->attrValue[i]) |
|
211 goto loser; |
|
212 } |
|
213 |
|
214 certreq->attributes[0] = attribute; |
|
215 |
|
216 return certreq; |
|
217 |
|
218 loser: |
|
219 CERT_DestroyCertificateRequest(certreq); |
|
220 return NULL; |
|
221 } |
|
222 |
|
223 void |
|
224 CERT_DestroyCertificateRequest(CERTCertificateRequest *req) |
|
225 { |
|
226 if (req && req->arena) { |
|
227 PORT_FreeArena(req->arena, PR_FALSE); |
|
228 } |
|
229 return; |
|
230 } |
|
231 |
|
232 static void |
|
233 setCRExt(void *o, CERTCertExtension **exts) |
|
234 { |
|
235 ((CERTCertificateRequest *)o)->attributes = (struct CERTAttributeStr **)exts; |
|
236 } |
|
237 |
|
238 /* |
|
239 ** Set up to start gathering cert extensions for a cert request. |
|
240 ** The list is created as CertExtensions and converted to an |
|
241 ** attribute list by CERT_FinishCRAttributes(). |
|
242 */ |
|
243 extern void *cert_StartExtensions(void *owner, PLArenaPool *ownerArena, |
|
244 void (*setExts)(void *object, CERTCertExtension **exts)); |
|
245 void * |
|
246 CERT_StartCertificateRequestAttributes(CERTCertificateRequest *req) |
|
247 { |
|
248 return (cert_StartExtensions ((void *)req, req->arena, setCRExt)); |
|
249 } |
|
250 |
|
251 /* |
|
252 ** At entry req->attributes actually contains an list of cert extensions-- |
|
253 ** req-attributes is overloaded until the list is DER encoded (the first |
|
254 ** ...EncodeItem() below). |
|
255 ** We turn this into an attribute list by encapsulating it |
|
256 ** in a PKCS 10 Attribute structure |
|
257 */ |
|
258 SECStatus |
|
259 CERT_FinishCertificateRequestAttributes(CERTCertificateRequest *req) |
|
260 { SECItem *extlist; |
|
261 SECOidData *oidrec; |
|
262 CERTAttribute *attribute; |
|
263 |
|
264 if (!req || !req->arena) { |
|
265 PORT_SetError(SEC_ERROR_INVALID_ARGS); |
|
266 return SECFailure; |
|
267 } |
|
268 if (req->attributes == NULL || req->attributes[0] == NULL) |
|
269 return SECSuccess; |
|
270 |
|
271 extlist = SEC_ASN1EncodeItem(req->arena, NULL, &req->attributes, |
|
272 SEC_ASN1_GET(CERT_SequenceOfCertExtensionTemplate)); |
|
273 if (extlist == NULL) |
|
274 return(SECFailure); |
|
275 |
|
276 oidrec = SECOID_FindOIDByTag(SEC_OID_PKCS9_EXTENSION_REQUEST); |
|
277 if (oidrec == NULL) |
|
278 return SECFailure; |
|
279 |
|
280 /* now change the list of cert extensions into a list of attributes |
|
281 */ |
|
282 req->attributes = PORT_ArenaZNewArray(req->arena, CERTAttribute*, 2); |
|
283 |
|
284 attribute = PORT_ArenaZNew(req->arena, CERTAttribute); |
|
285 |
|
286 if (req->attributes == NULL || attribute == NULL || |
|
287 SECITEM_CopyItem(req->arena, &attribute->attrType, &oidrec->oid) != 0) { |
|
288 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
289 return SECFailure; |
|
290 } |
|
291 attribute->attrValue = PORT_ArenaZNewArray(req->arena, SECItem*, 2); |
|
292 |
|
293 if (attribute->attrValue == NULL) |
|
294 return SECFailure; |
|
295 |
|
296 attribute->attrValue[0] = extlist; |
|
297 attribute->attrValue[1] = NULL; |
|
298 req->attributes[0] = attribute; |
|
299 req->attributes[1] = NULL; |
|
300 |
|
301 return SECSuccess; |
|
302 } |
|
303 |
|
304 SECStatus |
|
305 CERT_GetCertificateRequestExtensions(CERTCertificateRequest *req, |
|
306 CERTCertExtension ***exts) |
|
307 { |
|
308 if (req == NULL || exts == NULL) { |
|
309 PORT_SetError(SEC_ERROR_INVALID_ARGS); |
|
310 return SECFailure; |
|
311 } |
|
312 |
|
313 if (req->attributes == NULL || *req->attributes == NULL) |
|
314 return SECSuccess; |
|
315 |
|
316 if ((*req->attributes)->attrValue == NULL) { |
|
317 PORT_SetError(SEC_ERROR_INVALID_ARGS); |
|
318 return SECFailure; |
|
319 } |
|
320 |
|
321 return(SEC_ASN1DecodeItem(req->arena, exts, |
|
322 SEC_ASN1_GET(CERT_SequenceOfCertExtensionTemplate), |
|
323 (*req->attributes)->attrValue[0])); |
|
324 } |