cert-crypto: remove hack for certificate expiration
[siplcs.git] / src / core / sipe-cert-crypto-openssl.c
blob5c82976ef6764189071baa12775d95927bd7d9fc
1 /**
2 * @file sipe-cert-crypto-openssl.c
4 * pidgin-sipe
6 * Copyright (C) 2013 SIPE Project <http://sipe.sourceforge.net/>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 /**
24 * Certificate routines implementation based on OpenSSL.
27 /* needed for strptime() */
28 #define _XOPEN_SOURCE
30 #include <openssl/evp.h>
31 #include <openssl/rsa.h>
32 #include <openssl/x509.h>
34 #include <string.h>
35 #include <time.h>
37 #include <glib.h>
39 #include "sipe-backend.h"
40 #include "sipe-cert-crypto.h"
42 struct sipe_cert_crypto {
43 RSA *key;
47 * This data structure is used in two different modes
49 * a) certificate generated by the server from our Certificate Request
51 * key - reference to client RSA key, don't free!
52 * decoded - certificate as OpenSSL data structure, must be freed
53 * raw - certificate as DER encoded binary, must be freed
54 * length - length of DER binary
56 * b) server certificate
58 * key - reference to server public key, must be freed
59 * decoded - certificate as OpenSSL data structure, must be freed
60 * raw - NULL
61 * length - modulus length of server public key
63 struct certificate_openssl {
64 RSA *key;
65 EVP_PKEY *public;
66 X509 *decoded;
67 guchar *raw;
68 gsize length;
71 struct sipe_cert_crypto *sipe_cert_crypto_init(void)
73 struct sipe_cert_crypto *scc = g_new0(struct sipe_cert_crypto, 1);
75 /* RSA parameters - should those be configurable? */
76 SIPE_DEBUG_INFO_NOFORMAT("sipe_cert_crypto_init: generate key pair, this might take a while...");
77 scc->key = RSA_generate_key(2048, 65537, NULL, NULL);
79 if (scc->key) {
80 SIPE_DEBUG_INFO_NOFORMAT("sipe_cert_crypto_init: key pair generated");
81 return(scc);
84 SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_init: key generation failed");
85 g_free(scc);
86 return(NULL);
89 void sipe_cert_crypto_free(struct sipe_cert_crypto *scc)
91 if (scc) {
92 if (scc->key)
93 RSA_free(scc->key);
94 g_free(scc);
99 gchar *sipe_cert_crypto_request(struct sipe_cert_crypto *scc,
100 const gchar *subject)
102 gchar *base64 = NULL;
103 EVP_PKEY *pkey;
105 if (!scc || !subject)
106 return(NULL);
108 if ((pkey = EVP_PKEY_new()) != NULL) {
109 X509_REQ *x509_req;
111 if ((x509_req = X509_REQ_new()) != NULL) {
112 X509_NAME *name;
114 EVP_PKEY_set1_RSA(pkey, scc->key);
116 X509_REQ_set_version(x509_req, 2);
117 X509_REQ_set_pubkey(x509_req, pkey);
119 name = X509_REQ_get_subject_name(x509_req);
120 X509_NAME_add_entry_by_txt(name,
121 "CN",
122 MBSTRING_ASC,
123 (guchar *) subject,
124 -1, -1, 0);
126 if (X509_REQ_sign(x509_req, pkey, EVP_sha1())) {
127 gsize length;
128 guchar *buf, *tmp;
131 * Encode into DER format
133 * NOTE: i2d_X509(a, b) autoincrements b!
135 length = i2d_X509_REQ(x509_req, NULL);
136 tmp = buf = g_malloc(length);
137 i2d_X509_REQ(x509_req, &tmp);
139 base64 = g_base64_encode(buf, length);
140 g_free(buf);
142 } else {
143 SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_request: can't sign certificate request");
146 X509_REQ_free(x509_req);
147 } else {
148 SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_request: can't create x509 request data structure");
151 EVP_PKEY_free(pkey);
152 } else {
153 SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_request: can't create private key data structure");
156 return(base64);
159 void sipe_cert_crypto_destroy(gpointer certificate)
161 struct certificate_openssl *co = certificate;
163 if (co) {
164 /* imported server certificate - mode (b) */
165 if (!co->raw && co->key)
166 RSA_free(co->key);
167 if (co->decoded)
168 X509_free(co->decoded);
169 g_free(co->raw);
170 g_free(co);
174 /* generates certificate_openssl in mode (a) */
175 gpointer sipe_cert_crypto_decode(struct sipe_cert_crypto *scc,
176 const gchar *base64)
178 struct certificate_openssl *co = g_new0(struct certificate_openssl, 1);
179 const guchar *tmp;
181 /* NOTE: d2i_X509(NULL, &in, len) autoincrements "in" */
182 tmp = co->raw = g_base64_decode(base64, &co->length);
183 co->decoded = d2i_X509(NULL, &tmp, co->length);
185 if (!co->decoded) {
186 sipe_cert_crypto_destroy(co);
187 return(NULL);
190 co->key = scc->key;
192 return(co);
195 /* generates certificate_openssl in mode (b) */
196 gpointer sipe_cert_crypto_import(const guchar *raw,
197 gsize length)
199 struct certificate_openssl *co = g_new0(struct certificate_openssl, 1);
200 EVP_PKEY *pkey;
202 /* co->raw not needed as this is a server certificate */
203 /* NOTE: d2i_X509(NULL, in, len) autoincrements "in" */
204 co->decoded = d2i_X509(NULL, &raw, length);
206 if (!co->decoded) {
207 sipe_cert_crypto_destroy(co);
208 return(NULL);
211 pkey = X509_get_pubkey(co->decoded);
213 if (!pkey) {
214 sipe_cert_crypto_destroy(co);
215 return(NULL);
218 co->key = EVP_PKEY_get1_RSA(pkey);
219 co->length = EVP_PKEY_size(pkey);
220 EVP_PKEY_free(pkey);
222 if (!co->key) {
223 sipe_cert_crypto_destroy(co);
224 return(NULL);
227 return(co);
230 gboolean sipe_cert_crypto_valid(gpointer certificate,
231 guint offset)
233 struct certificate_openssl *co = certificate;
234 time_t compare = time(NULL) + offset;
236 return(co &&
237 (X509_cmp_time(X509_get_notAfter(co->decoded),
238 &compare) > 0));
241 guint sipe_cert_crypto_expires(gpointer certificate)
243 struct certificate_openssl *co = certificate;
244 guint min;
245 guint max;
247 /* make sure certificate hasn't expired already */
248 if (!sipe_cert_crypto_valid(co, 0))
249 return(0);
252 * I can't believe this, but it's true...
254 * OpenSSL doesn't have a public API to convert an ASN1_TIME
255 * to seconds since epoch :-(
257 * @TODO: latest OpenSSL API has ASN1_TIME_diff()
259 * <30000 seconds (~8 hours) seems to be the most common expiration
260 * value. Run a bisect to determine the real expiration value.
262 min = 0;
263 max = 30000;
264 while (1) {
265 guint offset = (max - min) / 2 + min;
267 if (offset == min) {
268 break;
269 } else if (sipe_cert_crypto_valid(co, offset)) {
270 min = offset;
271 } else {
272 max = offset;
276 return(min);
279 gsize sipe_cert_crypto_raw_length(gpointer certificate)
281 return(((struct certificate_openssl *) certificate)->length);
284 const guchar *sipe_cert_crypto_raw(gpointer certificate)
286 return(((struct certificate_openssl *) certificate)->raw);
289 gpointer sipe_cert_crypto_public_key(gpointer certificate)
291 return(((struct certificate_openssl *) certificate)->key);
294 gsize sipe_cert_crypto_modulus_length(gpointer certificate)
296 return(((struct certificate_openssl *) certificate)->length);
299 gpointer sipe_cert_crypto_private_key(gpointer certificate)
301 return(((struct certificate_openssl *) certificate)->key);
304 /* Create test certificate for internal key pair (ONLY USE FOR TEST CODE!!!) */
305 gpointer sipe_cert_crypto_test_certificate(struct sipe_cert_crypto *scc)
307 struct certificate_openssl *co = NULL;
308 EVP_PKEY *pkey;
310 if ((pkey = EVP_PKEY_new()) != NULL) {
311 X509 *x509;
313 if ((x509 = X509_new()) != NULL) {
314 X509_NAME *name;
316 EVP_PKEY_set1_RSA(pkey, scc->key);
318 X509_set_version(x509, 2);
319 ASN1_INTEGER_set(X509_get_serialNumber(x509), 0);
320 X509_gmtime_adj(X509_get_notBefore(x509), 0);
321 X509_gmtime_adj(X509_get_notAfter(x509), (long) 60*60*24);
322 X509_set_pubkey(x509, pkey);
324 name = X509_get_subject_name(x509);
325 X509_NAME_add_entry_by_txt(name,
326 "CN",
327 MBSTRING_ASC,
328 (guchar *) "test@test.com",
329 -1, -1, 0);
330 X509_set_issuer_name(x509, name);
332 if (X509_sign(x509, pkey, EVP_sha1())) {
333 guchar *buf;
335 co = g_new0(struct certificate_openssl, 1);
336 co->decoded = x509;
337 co->key = scc->key;
340 * Encode into DER format
342 * NOTE: i2d_X509(a, b) autoincrements b!
344 co->length = i2d_X509(x509, NULL);
345 co->raw = buf = g_malloc(co->length);
346 i2d_X509(x509, &buf);
348 } else {
349 SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_test_certificate: can't sign certificate");
350 X509_free(x509);
352 } else {
353 SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_test_certificate: can't create x509 data structure");
356 EVP_PKEY_free(pkey);
357 } else {
358 SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_test_certificate: can't create private key data structure");
361 return(co);
365 Local Variables:
366 mode: c
367 c-file-style: "bsd"
368 indent-tabs-mode: t
369 tab-width: 8
370 End: