1 /*******************************************************************************
2 
3         copyright:      Copyright (c) 2008 Jeff Davey. All rights reserved
4 
5         license:        BSD style: $(LICENSE)
6 
7         author:         Jeff Davey <j@submersion.com>
8 
9 *******************************************************************************/
10 
11 module tango.net.util.PKI;
12 
13 private import tango.time.Time;
14 
15 private import tango.stdc.stringz;
16 
17 private import tango.net.util.c.OpenSSL;
18 
19 /*******************************************************************************
20 
21   PKI provides Public Key Infrastructure. 
22 
23   Specifically, it provides the ability to:
24 
25   - Make a X509 Certificate (SSL Certificate)
26 
27   - Make a Public and Private key pair
28 
29   - Validate a X509 Certificate against a Certificate Authority
30 
31   - Generate a SSLCtx for SSLSocket and SSLServerSocket
32 
33   - Wrap a SSLVerifyCallback so that retrieving the peer cert is easier
34 
35   PKI requires the OpenSSL library, and uses a dynamic binding to the library.
36   You can find the library at http://www.openssl.org and a Win32 specific port 
37   at http://www.slproweb.com/products/Win32OpenSSL.html.
38 
39 *******************************************************************************/
40 
41 
42 /*******************************************************************************
43 
44   Do not verify the peer certificate. Nor fail if it's not provided (server 
45   only).
46 
47 *******************************************************************************/
48 
49 const int SSL_VERIFY_NONE = 0x00;
50 
51 /*******************************************************************************
52 
53   Ask for a peer certificate, but do not fail if it is not provided.
54 
55 *******************************************************************************/
56 
57 const int SSL_VERIFY_PEER = 0x01;
58 
59 /*******************************************************************************
60 
61   Ask for a peer certificate, however, fail if it is not provided
62 
63 *******************************************************************************/
64 
65 const int SSL_VERIFY_FAIL_IF_NO_PEER_CERT = 0x02;
66 
67 /*******************************************************************************
68 
69   Only validate once, do not re-validate during handshake renegotiation.
70 
71 *******************************************************************************/
72 
73 const int SSL_VERIFY_CLIENT_ONCE = 0x04;
74 
75 const int SSL_SESS_CACHE_SERVER = 0x0002;
76 
77 /*******************************************************************************
78 
79   SSLVerifyCallback is passed into SSLCtx and is called during handshake
80   when OpenSSL is doing certificate validation.
81 
82   Wrapping the X509_STORE_CTX in the CertificateStoreCtx utility class
83   gives the ability to access the peer certificate, and reason for error.
84 
85 *******************************************************************************/
86 
87 extern (C) alias int function(int, X509_STORE_CTX *ctx) SSLVerifyCallback;
88 
89 
90 /*******************************************************************************
91 
92     SSLCtx is provided to SSLSocket and SSLServerSocket.
93 
94     It contains the public/private keypair, and some additional options that
95     control how the SSL streams work.
96 
97     Example
98     ---
99     auto cert = new Certificate(cast(char[])File("public.pem").read);
100     auto pkey = new PrivateKey(cast(char[])File("private.pem").read);;
101     auto ctx = new SSLCtx();
102     ctx.certificate = cert;
103     ctx.pkey = pkey;
104     ctx.checkKey();
105     ---
106 
107 *******************************************************************************/
108 
109 class SSLCtx
110 {
111     private SSL_CTX *_ctx = null;
112     private Certificate _cert = null;
113     private PrivateKey _key = null;
114     private CertificateStore _store = null;
115 
116     /*******************************************************************************
117 
118         Creates a new SSLCtx supporting SSLv3 and TLSv1 methods.
119 
120     *******************************************************************************/
121 
122     this()
123     {
124         if ((_ctx = SSL_CTX_new(SSLv23_method())) is null)
125             throwOpenSSLError();
126     }
127 
128     ~this()
129     {
130         if (_ctx)
131         {
132             SSL_CTX_free(_ctx);
133             _ctx = null;
134         }
135         _cert = null;
136         _key = null;
137         _store = null;
138     }
139 
140     /*******************************************************************************
141 
142         Return the native context from OpenSSL
143                 
144     *******************************************************************************/
145 
146     @property SSL_CTX* native()
147     {   
148         return _ctx;
149     }
150 
151     /*******************************************************************************
152 
153         Assigns a X509 Certificate to the SSLCtx.
154 
155         This is required for SSL
156         
157     *******************************************************************************/
158 
159     SSLCtx certificate(Certificate cert)
160     {
161         if (SSL_CTX_use_certificate(_ctx, cert._cert))
162             _cert = cert;
163         else
164             throwOpenSSLError();
165         return this;
166     }
167 
168     /*******************************************************************************
169 
170         Assigns a PrivateKey (public/private keypair to the SSLCtx.
171 
172         This is required for SSL.
173                 
174     *******************************************************************************/
175 
176 
177     SSLCtx privateKey(PrivateKey key)
178     {
179         if (SSL_CTX_use_PrivateKey(_ctx, key._evpKey))
180             _key = key;
181         else
182             throwOpenSSLError();
183         return this;
184     }
185 
186     /*******************************************************************************
187 
188         Validates that the X509 certificate was signed with the provided
189         public/private keypair. Throws an exception if this is not the case.
190                 
191     *******************************************************************************/
192 
193     @property SSLCtx checkKey()
194     {
195         if (!SSL_CTX_check_private_key(_ctx))
196             throwOpenSSLError();
197         return this;
198     }
199 
200     /*******************************************************************************
201 
202         Sets a SSLVerifyCallback function using the SSL_VERIFY_(NONE|PEER|etc) flags
203         to control how verification is handled.
204                 
205     *******************************************************************************/
206 
207     SSLCtx setVerification(int flags, SSLVerifyCallback cb)
208     {
209         SSL_CTX_set_verify(_ctx, flags, cb);
210         return this;
211     }
212 
213     /*******************************************************************************
214 
215         Sets a CertificateStore of certs that are valid and trust Certificate
216         Authorities during verification.
217                 
218     *******************************************************************************/
219 
220 
221     SSLCtx store(CertificateStore store) // warning this will free the existing one.. not sure if it frees on close yet ( so don't set it twice! ?!)
222     {
223         SSL_CTX_set_cert_store(_ctx, store._store);
224         _store = store;
225         return this;
226     }
227 
228     /*******************************************************************************
229 
230         Loads valid Certificate Authorities from the specified path.
231 
232         From the SSL_CTX_load_verify_locations manpage:
233 
234         Each file must contain only one CA certificate. Also, the files are
235         looked up by the CA subject name hash value, which must be available. If
236         more than one CA certificate with the same name hash value exists, the
237         extension must be different. (ie: 9d66eef0.0, 9d66eef0.1, etc). The search 
238         is performed in the ordering of the extension, regardless of other properties
239         of the certificates. Use the c_rehash utility to create the necessary symlinks
240                 
241     *******************************************************************************/
242 
243     SSLCtx caCertsPath(const(char)[] path)
244     {
245         if (!SSL_CTX_load_verify_locations(_ctx, null, toStringz(path)))
246             throwOpenSSLError();
247         return this;
248     }
249 
250     // TODO need to finish adding Session handling functionality
251 /*    void sessionCacheMode(int mode)
252     {
253         if (!SSL_CTX_set_session_cache_mode(_ctx, mode))
254             throwOpenSSLError();
255     }
256 
257     void sessionId(ubyte[] id)
258     {
259         if (!SSL_CTX_set_session_id_context(_ctx, id.ptr, id.length))
260             throwOpenSSLError();
261     } */
262 }
263 
264 /*******************************************************************************
265 
266     The CertificateStoreCtx is a wrapper to the SSLVerifyCallback X509_STORE_CTX
267     parameter.
268 
269     It allows retrieving the peer certificate, and examining any errors during
270     validation.
271 
272 
273     The following example will probably change sometime soon.
274 
275     Example
276     ---
277     extern (C)
278     {
279         int myCallback(int code, X509_STORE_CTX *ctx)
280         {
281             auto myCtx = new CertificateStoreCtx(ctx);
282             Certificate cert = myCtx.cert;
283             Stdout(cert.subject).newline;
284             return 0; // BAD CERT! (1 is good)
285         }
286     }
287     ---
288 
289 *******************************************************************************/
290 
291 class CertificateStoreCtx
292 {
293     private X509_STORE_CTX *_ctx = null;
294 
295     /*******************************************************************************
296 
297         This constructor takes a X509_STORE_CTX as provided by the SSLVerifyCallback
298         function.
299                 
300     *******************************************************************************/
301 
302     this(X509_STORE_CTX *ctx)
303     {
304         _ctx = ctx;
305     }
306 
307     /*******************************************************************************
308 
309         Returns the peer certificate.
310                 
311     *******************************************************************************/
312 
313     Certificate cert()
314     {
315         X509 *cert = X509_STORE_CTX_get_current_cert(_ctx);
316         if (cert is null)
317             throwOpenSSLError();
318         return new Certificate(cert);
319     }
320 
321     // TODO need more research on what used for
322     int error()
323     {
324         return X509_STORE_CTX_get_error(_ctx);
325     }
326 
327     // TODO need more research on what used for
328     int errorDepth()
329     {
330         return X509_STORE_CTX_get_error_depth(_ctx);
331     }
332 
333 }
334 
335 /*******************************************************************************
336 
337     CertificateStore stores numerous X509 Certificates for use in CRL lists,
338     CA lists, etc.
339 
340     Example
341     ---
342     auto store = new CertificateStore();
343     auto caCert = new Certificate(cast(char[])File("cacert.pem").read);
344     store.add(caCert);
345     auto untrustedCert = new Certificate(cast(char[])File("cert.pem").read);
346     if (untrustedCert.verify(store))
347         Stdout("The untrusted cert was signed by our caCert and is valid.").newline;
348     else
349         Stdout("The untrusted cert was expired, or not signed by the caCert").newline;
350     ---
351             
352 *******************************************************************************/
353 
354 class CertificateStore
355 {
356     package X509_STORE *_store = null;
357     Certificate[] _certs;
358 
359 
360     this()
361     {
362         if ((_store = X509_STORE_new()) is null)
363             throwOpenSSLError();
364     }
365 
366     ~this()
367     {
368         if (_store)
369         {
370             X509_STORE_free(_store);
371             _store = null;
372         }
373     }
374 
375     /*******************************************************************************
376 
377         Add a Certificate to the store.
378             
379     *******************************************************************************/
380 
381     CertificateStore add(Certificate cert)
382     {
383         if (X509_STORE_add_cert(_store, cert._cert))
384             _certs ~= cert; // just in case it gets GC'd?
385         else
386             throwOpenSSLError();
387         return this;
388     }
389 }
390 
391 /*******************************************************************************
392 
393     PublicKey contains the RSA public key from a private/public keypair.
394 
395     It also allows extraction of the public key from a keypair.
396 
397     This is useful for encryption, you can encrypt data with someone's public key
398     and they can decrypt it with their private key.
399 
400     Example
401     ---
402     auto public = new PublicKey(cast(char[])File.get("public.pem"));
403     auto encrypted = public.encrypt(cast(ubyte[])"Hello, how are you today?");
404     auto pemData = public.pemFormat;
405     ---
406 
407 *******************************************************************************/
408 
409 class PublicKey
410 {
411     package EVP_PKEY *_evpKey = null;
412     private PrivateKey _existingKey = null;
413 
414     /*******************************************************************************
415 
416         Generate a PublicKey object from the passed PEM formatted data
417 
418         Params:
419             publicPemData = pem encoded data containing the public key 
420             
421     *******************************************************************************/
422     this (const(char)[] publicPemData)
423     {
424         BIO *bp = BIO_new_mem_buf(cast(void*)publicPemData.ptr, cast(int) publicPemData.length);
425         if (bp)
426         {
427             _evpKey = PEM_read_bio_PUBKEY(bp, null, null, null);
428             BIO_free_all(bp);
429         }
430 
431         if (_evpKey is null)
432             throwOpenSSLError();
433     }
434     
435     package this(PrivateKey key) 
436     {        
437         this._evpKey = key._evpKey;
438         this._existingKey = key;
439     }
440 
441     ~this()
442     {
443         if (_existingKey !is null)
444         {
445             _existingKey = null;
446             _evpKey = null;
447         }
448         else if (_evpKey)
449         {
450             EVP_PKEY_free(_evpKey);
451             _evpKey = null;
452         }
453     }
454 
455     /*******************************************************************************
456 
457         Return a PublicKey in the PEM format.
458             
459     *******************************************************************************/
460 
461     string pemFormat()
462     {
463         string rtn = null;
464         BIO *bp = BIO_new(BIO_s_mem());
465         if (bp)
466         {
467             if (PEM_write_bio_PUBKEY(bp, _evpKey))
468             {
469                 char *pemData = null;
470                 int pemSize = BIO_get_mem_data(bp, &pemData);
471                 rtn = pemData[0..pemSize].idup;
472             }
473             BIO_free_all(bp);
474         }
475         if (rtn is null)
476             throwOpenSSLError();
477         return rtn;
478     }
479 
480     /*******************************************************************************
481 
482         Verify the data passed was signed with the public key.
483 
484         Params:
485         data = the data to verify
486         signature = the digital signature
487     *******************************************************************************/
488 
489     bool verify(const(ubyte)[] data, const(ubyte)[] signature)
490     {
491         ubyte[MD5_DIGEST_LENGTH] digest;
492         MD5_CTX c;
493         MD5_Init(&c);
494         MD5_Update(&c, data.ptr, data.length);
495         MD5_Final(digest.ptr, &c);
496 
497         EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(_evpKey, null);
498         if (!ctx)
499             throwOpenSSLError();
500         scope(exit) EVP_PKEY_CTX_free(ctx);
501         
502         
503         if (EVP_PKEY_verify_init(ctx) <= 0)
504             throwOpenSSLError();
505 
506         auto code = EVP_PKEY_verify(ctx, signature.ptr, signature.length, digest.ptr, digest.length);
507         if (code < 0)
508             throwOpenSSLError();
509 
510         return code == 1;
511     }
512 
513     /*******************************************************************************
514 
515         Encrypt the passed data using the PublicKey 
516         
517         Notes:
518         This is size limited based off the key
519         Not recommended for general encryption, use RSA for encrypting a 
520         random key instead and switch to a block cipher.
521 
522         Params:
523         data = the data to encrypt
524             
525     *******************************************************************************/
526 
527     ubyte[] encrypt(const(ubyte)[] data)
528     {
529         ubyte[] rtn;
530         EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(_evpKey, null);
531         if (!ctx)
532             throwOpenSSLError();
533         scope(exit) EVP_PKEY_CTX_free(ctx);
534         
535         if (EVP_PKEY_encrypt_init(ctx) <= 0)
536             throwOpenSSLError();
537         
538         size_t out_len = 0;
539         if (EVP_PKEY_encrypt(ctx, null, &out_len, data.ptr, data.length) <= 0) //determine a out data size
540             throwOpenSSLError();  
541         rtn = new ubyte[out_len];
542         if (EVP_PKEY_encrypt(ctx, rtn.ptr, &out_len, data.ptr, data.length) <= 0)
543             throwOpenSSLError();    
544         return rtn;
545     }
546 
547      /*******************************************************************************
548 
549         Decrypts data previously encrypted with the matching PrivateKey
550 
551         Please see the encrypt notes.
552 
553         Params:
554             data = the data to encrypt
555 
556     *******************************************************************************/
557        
558     ubyte[] decrypt(const(ubyte)[] data)
559     {
560         ubyte[] rtn;
561         EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(_evpKey, null);
562         if (!ctx)
563             throwOpenSSLError();
564         scope(exit) EVP_PKEY_CTX_free(ctx);
565         
566         if (EVP_PKEY_decrypt_init(ctx) <= 0)
567             throwOpenSSLError();
568         
569         size_t out_len = 0;
570         if (EVP_PKEY_decrypt(ctx, null, &out_len, data.ptr, data.length) <= 0) //determine a out data size
571             throwOpenSSLError();  
572         rtn = new ubyte[out_len];
573         if (EVP_PKEY_decrypt(ctx, rtn.ptr, &out_len, data.ptr, data.length) <= 0)
574             throwOpenSSLError();    
575         return rtn;
576     }
577 
578 }
579 
580 /*******************************************************************************
581 
582     Generates a RSA public/private key pair for use with X509 Certificates
583     and other applications search as S/MIME, DomainKeys, etc.
584 
585     Example
586     ---
587     auto newPkey = new PrivateKey(2048); // create new keypair
588     Stdout(newPkey.pemFormat("password")); // dumps in pemFormat with encryption
589     Stdout(newPkey.pemFormat()); // dumps in pemFormat without encryption
590     Stdout(newPkey.publicKey.pemFormat); // dump out just the public key portion
591     auto data = newPkey.decrypt(someData); // decrypt data encrypted with public Key
592     ---
593 
594 *******************************************************************************/
595 
596 class PrivateKey
597 {
598     package EVP_PKEY *_evpKey = null;
599 
600     /*******************************************************************************
601 
602         Reads in the provided PEM data, with an optional password to decrypt
603         the private key.
604 
605         Params:
606             privatePemData = the PEM encoded data of the private key
607             certPass = an optional password to decrypt the key.
608         
609     *******************************************************************************/
610 
611     this (const(char)[] privatePemData, const(char)[] certPass = null)
612     {
613         BIO *bp = BIO_new_mem_buf(cast(void*)privatePemData.ptr, cast(int) privatePemData.length);
614         if (bp)
615         {
616             _evpKey = PEM_read_bio_PrivateKey(bp, null, null, certPass ? toStringz(certPass) : null);
617             BIO_free_all(bp);
618         }
619 
620         if (_evpKey is null)
621             throwOpenSSLError();
622     }
623 
624     /*******************************************************************************
625 
626         Generates a new private/public key at the specified bit leve.
627 
628         Params:
629             bits = Number of bits to use, 2048 is a good number for this.
630         
631     *******************************************************************************/
632 
633 
634     this(int bits)
635     {
636         RSA *rsa = RSA_generate_key(bits, RSA_F4, null, null);
637         if (rsa)
638         {
639             if ((_evpKey = EVP_PKEY_new()) !is null)
640                 EVP_PKEY_assign_RSA(_evpKey, rsa);
641             if (_evpKey is null)
642                 RSA_free(rsa);
643         }
644 
645         if (_evpKey is null)
646             throwOpenSSLError();
647     }
648     
649     ~this()
650     {
651         if (_evpKey)
652         {
653             EVP_PKEY_free(_evpKey);
654             _evpKey = null;
655         }
656     }
657 
658     /*******************************************************************************
659 
660         Compares two PrivateKey classes to see if the internal structures are 
661         the same.
662         
663     *******************************************************************************/
664     override bool opEquals(Object obj)
665     {
666         auto pk = cast(PrivateKey)obj;
667         if (pk !is null)
668             return cast(bool)EVP_PKEY_cmp_parameters(pk._evpKey, this._evpKey);
669         return false;
670     }
671 
672     /*******************************************************************************
673 
674         Returns the underlying public/private key pair in PEM format.
675 
676         Params:
677             pass = If this is provided, the private key will be encrypted using
678             AES 256bit encryption, with this as the key.
679         
680     *******************************************************************************/
681     string pemFormat(const(char)[] pass = null)
682     {
683         string rtn = null;
684         BIO *bp = BIO_new(BIO_s_mem());
685         if (bp)
686         {
687             if (PEM_write_bio_PKCS8PrivateKey(bp, _evpKey, pass ? EVP_aes_256_cbc() : null, null, 0, null, pass ? toStringz(pass) : null))
688             {
689                 char *pemData = null;
690                 int pemSize = BIO_get_mem_data(bp, &pemData);
691                 rtn = pemData[0..pemSize].idup;
692             }
693             BIO_free_all(bp);
694         }
695         if (rtn is null)
696             throwOpenSSLError();
697         return rtn;
698     }
699 
700     /*******************************************************************************
701 
702         Returns the underlying PublicKey
703 
704     *******************************************************************************/
705 
706     PublicKey publicKey()
707     {
708         auto rtn = new PublicKey(this);
709         return rtn;
710     }
711 
712     /*******************************************************************************
713         Sign the given data with the private key
714 
715         Params:
716         data = the data to sign
717         sigbuf = the buffer to store the signature in
718         
719         Returns a slice of the signature or null
720 
721     *******************************************************************************/
722 
723     ubyte[] sign(const(ubyte)[] data, ubyte[] sigbuf)
724     {
725         ubyte[MD5_DIGEST_LENGTH] digest;
726 
727         MD5_CTX c;
728         MD5_Init(&c);
729         MD5_Update(&c, data.ptr, data.length);
730         MD5_Final(digest.ptr, &c);
731 
732         EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(_evpKey, null);
733         if (!ctx)
734             throwOpenSSLError();
735         scope(exit) EVP_PKEY_CTX_free(ctx);
736 
737         if (EVP_PKEY_sign_init(ctx) <= 0)
738             throwOpenSSLError();
739             
740         size_t out_len = 0;
741         if (EVP_PKEY_sign(ctx, null, &out_len, digest.ptr, digest.length) <= 0)
742             throwOpenSSLError();
743         if (sigbuf.length < out_len)
744             throw new Exception("The signature buffer is too small to fit the signature for this key.");
745         if (EVP_PKEY_sign(ctx, sigbuf.ptr, &out_len, digest.ptr, digest.length) <= 0)
746             throwOpenSSLError();
747 
748         return sigbuf[0..out_len];
749     }
750 
751     /*******************************************************************************
752 
753         Encrypt the passed data using the PrivateKey
754         
755         Notes:
756         This is size limited based off the key
757         Not recommended for general encryption, use RSA for encrypting a 
758         random key instead and switch to a block cipher.
759 
760         Params:
761         data = the data to encrypt
762             
763     *******************************************************************************/
764 
765     ubyte[] encrypt(const(ubyte)[] data)
766     {
767         ubyte[] rtn;
768         EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(_evpKey, null);
769         if (!ctx)
770             throwOpenSSLError();
771         scope(exit) EVP_PKEY_CTX_free(ctx);
772         
773         if (EVP_PKEY_encrypt_init(ctx) <= 0)
774             throwOpenSSLError();
775         
776         size_t out_len = 0;
777         if (EVP_PKEY_encrypt(ctx, null, &out_len, data.ptr, data.length) <= 0) //determine a out data size
778             throwOpenSSLError();  
779         rtn = new ubyte[out_len];
780         if (EVP_PKEY_encrypt(ctx, rtn.ptr, &out_len, data.ptr, data.length) <= 0)
781             throwOpenSSLError();    
782         return rtn;
783     }
784 
785      /*******************************************************************************
786 
787         Decrypts data previously encrypted with the matching PublicKey
788 
789         Please see the encrypt notes.
790 
791         Parmas:
792             data = the data to encrypt
793 
794     *******************************************************************************/
795        
796     ubyte[] decrypt(const(ubyte)[] data)
797     {
798         ubyte[] rtn;
799         EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(_evpKey, null);
800         if (!ctx)
801             throwOpenSSLError();
802         scope(exit) EVP_PKEY_CTX_free(ctx);
803         
804         if (EVP_PKEY_decrypt_init(ctx) <= 0)
805             throwOpenSSLError();
806         
807         size_t out_len = 0;
808         if (EVP_PKEY_decrypt(ctx, null, &out_len, data.ptr, data.length) <= 0) //determine a out data size
809             throwOpenSSLError();  
810         rtn = new ubyte[out_len];
811         if (EVP_PKEY_decrypt(ctx, rtn.ptr, &out_len, data.ptr, data.length) <= 0)
812             throwOpenSSLError();    
813         return rtn;
814     }
815 
816 }
817 
818 /*******************************************************************************
819 
820     Certificate provides necessary functionality to create and read X509 
821     Certificates.
822 
823     Note, once a Certificate has been signed, it is immutable, and cannot
824     be modified.
825 
826     X509 Certificates are sometimes called SSL Certificates.
827 
828     Example
829     ---
830     auto newPkey = new PrivateKey(2048); // create new keypair
831     auto cert = new Certificate();
832     cert.privateKey = newPkey;
833     cert.serialNumber = 1;
834     cert.dateBeforeOffset = TimeSpan.zero;
835     cert.dateAfterOffset = TimeSpan.days(365); // cert is valid for one year
836     cert.setSubject("US", "State", "City", "Organization", "CN", "Organizational Unit", "Email");
837     cert.sign(cert, newPkey); // self signed cert
838     Stdout(newPkey.pemFormat).newline;
839     Stdout(cert.pemFormat).newline;
840     ---
841 
842 *******************************************************************************/
843 
844 class Certificate
845 {
846     package X509 *_cert = null;
847     private bool readOnly = true;
848     private bool freeIt = true;
849 
850     // used with X509_STORE_CTX
851     package this (X509 *cert)
852     {
853         _cert = cert;
854         freeIt = false;
855     }
856 
857     /*******************************************************************************
858 
859         Parses a X509 Certificate from the provided PEM encoded data.
860             
861     *******************************************************************************/
862     this(const(ubyte)[] publicPemData)
863     {
864         BIO *data = BIO_new_mem_buf(cast(void*)publicPemData.ptr, cast(int) publicPemData.length);
865         if (data)
866         {
867             _cert = PEM_read_bio_X509(data, null, null, null);
868             BIO_free_all(data);
869         }
870         if (_cert is null)
871             throwOpenSSLError();
872     }
873 
874     /*******************************************************************************
875 
876         Creates a new and un-signed (empty) X509 certificate. Useful for generating
877         X509 certificates programatically.
878             
879     *******************************************************************************/
880     this()
881     {
882         if ((_cert = X509_new()) !is null)
883         {
884             if (!X509_set_version(_cert, 2)) // 2 == Version 3
885             {
886                 X509_free(_cert);
887                 _cert = null;
888             }
889             else
890                 readOnly = false;
891         }
892         if (_cert is null)
893             throwOpenSSLError();
894     }
895 
896     ~this()
897     {
898         if (_cert && freeIt)
899         {
900             X509_free(_cert);
901             _cert = null;
902         }
903     }
904 
905     /*******************************************************************************
906 
907         Sets the serial number of the new unsigned certificate.
908 
909         Note, this serial number should be unique for all certificates signed
910         by the provided certificate authority. Having two Certificates with the
911         same serial number can cause problems with web browsers and other apps
912         because they will be different certificates.
913             
914     *******************************************************************************/
915 
916     Certificate serialNumber(uint serial)
917     {
918         checkFlag();
919         if (!ASN1_INTEGER_set(X509_get_serialNumber(_cert), serial))
920             throwOpenSSLError();
921         return this;
922     }
923     /*******************************************************************************
924 
925         Returns the serial number of the Certificate
926             
927     *******************************************************************************/
928 
929     uint serialNumber()
930     {
931         if (!X509_get_serialNumber(_cert))
932             throwOpenSSLError();
933         return ASN1_INTEGER_get(X509_get_serialNumber(_cert));
934     }
935 
936     /*******************************************************************************
937 
938         If the current date is "before" the date set here, the certificate will be
939         invalid.
940 
941         Params:
942             t = A TimeSpan representing the earliest time the Certificate will be valid
943 
944         Example:
945             cert.dateBeforeOffset = TimeSpan.seconds(-86400); // Certificate is invalid before yesterday
946             
947     *******************************************************************************/
948 
949     Certificate dateBeforeOffset(TimeSpan t)
950     {
951         checkFlag();
952         if (!X509_gmtime_adj(X509_get_notBefore(_cert), cast(int)t.seconds))
953             throwOpenSSLError();
954         return this;
955     }
956 
957     /*******************************************************************************
958 
959         If the current date is "after" the date set here, the certificate will be
960         invalid.
961 
962         Params:
963             t = A TimeSpan representing the amount of time from now that the
964             Certificate will be valid. This must be larger than dateBefore
965 
966         Example:
967             cert.dateAfterOffset = TimeSpan.seconds(86400 * 365); // Certificate is valid up to one year from now
968             
969     *******************************************************************************/
970 
971     Certificate dateAfterOffset(TimeSpan t)
972     {
973         checkFlag();
974         if (!X509_gmtime_adj(X509_get_notAfter(_cert), cast(int)t.seconds))
975             throwOpenSSLError();
976         return this;
977     }
978 
979     
980     /*******************************************************************************
981 
982         Returns the dateAfter field of the certificate in ASN1_GENERALIZEDTIME.
983 
984         Note, this will eventually befome a DateTime struct.
985             
986     *******************************************************************************/
987 
988     char[] dateAfter()
989     {
990         char[] rtn;
991         ASN1_GENERALIZEDTIME *genTime = ASN1_TIME_to_generalizedtime(X509_get_notAfter(_cert), null);
992         if (genTime)
993         {
994             rtn = genTime.data[0..genTime.length].dup;
995             ASN1_STRING_free(cast(ASN1_STRING*)genTime);
996         }
997 
998         return rtn;
999     }
1000 
1001     /*******************************************************************************
1002 
1003         Returns the dateBefore field of the certificate in ASN1_GENERALIZEDTIME.
1004 
1005         Note, this will eventually befome a DateTime struct.
1006             
1007     *******************************************************************************/
1008 
1009     char[] dateBefore()    
1010     {
1011         char[] rtn;
1012         ASN1_GENERALIZEDTIME *genTime = ASN1_TIME_to_generalizedtime(X509_get_notBefore(_cert), null);
1013         if (genTime)
1014         {
1015             rtn = genTime.data[0..genTime.length].dup;
1016             ASN1_STRING_free(cast(ASN1_STRING*)genTime);
1017         }
1018         return rtn;
1019     }
1020 
1021     /*******************************************************************************
1022 
1023         Sets the public/private keypair of an unsigned certificate.
1024             
1025     *******************************************************************************/
1026 
1027     Certificate privateKey(PrivateKey key)
1028     {
1029         checkFlag();
1030         if (key)
1031         {
1032             if (!X509_set_pubkey(_cert, key._evpKey))
1033                 throwOpenSSLError();
1034         }
1035         return this;
1036     }
1037 
1038     /*******************************************************************************
1039 
1040         Sets the subject (who this certificate is for) of an unsigned certificate.
1041 
1042         The country code must be a valid two-letter country code (ie: CA, US, etc)
1043 
1044         Params:
1045         country = the two letter country code of the subject
1046         stateProvince = the state or province of the subject
1047         city = the city the subject belong to
1048         organization = the organization the subject belongs to
1049         cn = the cn of the subject. For websites, this should be the website url
1050         or a wildcard version of it (ie: *.dsource.org)
1051         organizationUnit = the optional orgnizationalUnit of the subject
1052         email = the optional email address of the subject
1053 
1054     *******************************************************************************/
1055 
1056     // this kinda sucks.. but it has to be done in a certain order..
1057     Certificate setSubject(const(char)[] country, const(char)[] stateProvince, const(char)[] city, const(char)[] organization, const(char)[] cn, const(char)[] organizationalUnit = null, const(char)[] email = null)
1058     in
1059     {
1060         assert(country);
1061         assert(stateProvince);
1062         assert(organization);
1063         assert(cn);
1064     }
1065     body
1066     {
1067         checkFlag();
1068         X509_NAME *name = X509_get_subject_name(_cert);
1069         if (name)
1070         {
1071             addNameEntry(name, "C", country);
1072             addNameEntry(name, "ST", stateProvince);
1073             addNameEntry(name, "L", city);
1074             addNameEntry(name, "O", organization);
1075             if (organizationalUnit !is null)
1076                 addNameEntry(name, "OU", organizationalUnit);
1077             if (email) // this might have to go after the CN
1078                 addNameEntry(name, "emailAddress", email);
1079             addNameEntry(name, "CN", cn);
1080         }
1081         else
1082             throwOpenSSLError();
1083         return this;
1084     }
1085 
1086     /*******************************************************************************
1087 
1088         Returns the Certificate subject in a multi-line string.
1089             
1090     *******************************************************************************/
1091 
1092     char[] subject() // currently multi-line, could be single-line..
1093     {
1094         char[] rtn = null;
1095         X509_NAME *subjectName = X509_get_subject_name(_cert);
1096         if (subjectName)
1097         {
1098             BIO *subjectBIO = BIO_new(BIO_s_mem());
1099             if (subjectBIO)
1100             {
1101                 if (X509_NAME_print_ex(subjectBIO, subjectName, 0, XN_FLAG_MULTILINE))
1102                 {
1103                     char *subjectPtr = null;
1104                     int length = BIO_get_mem_data(subjectBIO, &subjectPtr);
1105                     rtn = subjectPtr ? subjectPtr[0..length].dup : null;
1106                 }
1107                 BIO_free_all(subjectBIO);
1108             }
1109         }
1110         if (rtn is null)
1111             throwOpenSSLError();
1112         return rtn;
1113     }
1114 
1115     /*******************************************************************************
1116 
1117         Signs the unsigned Certificate with the specified CA X509 Certificate and
1118         it's corresponding public/private keypair.
1119 
1120         Once the Certificate is signed, it can no longer be modified.
1121             
1122     *******************************************************************************/
1123 
1124     Certificate sign(Certificate caCert, PrivateKey caKey)
1125     in
1126     {
1127         assert(caCert);
1128         assert(caKey);
1129     }
1130     body
1131     {
1132         checkFlag();
1133         X509_NAME *issuer = X509_get_subject_name(caCert._cert);
1134         if (issuer)
1135         {
1136             if (X509_set_issuer_name(_cert, issuer))
1137             {
1138                 if (X509_sign(_cert, caKey._evpKey, EVP_sha1()))
1139                     readOnly = true;
1140             }
1141         }
1142 
1143         if (!readOnly)
1144             throwOpenSSLError();
1145         return this;
1146     }
1147 
1148     /*******************************************************************************
1149 
1150         Checks if the underlying data structur of the Certificate is equal
1151             
1152     *******************************************************************************/
1153 
1154     override bool opEquals(Object obj)
1155     {
1156         auto c = cast(Certificate)obj;
1157         if (c !is null)
1158             return !X509_cmp(c._cert, this._cert);
1159         return false;
1160     }
1161 
1162     /*******************************************************************************
1163 
1164         Verifies that the Certificate was signed and issues by a CACert in the 
1165         passed CertificateStore.
1166 
1167         This will also verify the dateBefore and dateAfter fields to see if the
1168         current date falls between them.
1169             
1170     *******************************************************************************/
1171 
1172     bool verify(CertificateStore store)
1173     {
1174         bool rtn = false;
1175         X509_STORE_CTX *verifyCtx = X509_STORE_CTX_new();
1176         if (verifyCtx)
1177         {
1178             if (X509_STORE_CTX_init(verifyCtx, store._store, _cert, null))
1179             {
1180                 if (X509_verify_cert(verifyCtx))
1181                     rtn = true;
1182             }
1183             X509_STORE_CTX_free(verifyCtx);
1184         }
1185 
1186         return rtn;
1187     }
1188 
1189     /*******************************************************************************
1190 
1191         Returns the Certificate in a PEM encoded string.
1192             
1193     *******************************************************************************/
1194 
1195     string pemFormat()
1196     {
1197         string rtn = null;
1198         BIO *bp = BIO_new(BIO_s_mem());
1199         if (bp)
1200         {
1201             if (PEM_write_bio_X509(bp, _cert))
1202             {
1203                 char *pemData = null;
1204                 int pemSize = BIO_get_mem_data(bp, &pemData);
1205                 rtn = pemData[0..pemSize].idup;
1206             }
1207             BIO_free_all(bp);
1208         }
1209         if (rtn is null)
1210             throwOpenSSLError();
1211         return rtn;    
1212     }
1213 
1214     private void addNameEntry(X509_NAME *name, const(char) *type, const(char)[] value)
1215     {
1216         if (!X509_NAME_add_entry_by_txt(name, type, MBSTRING_ASC, toStringz(value), cast(int) value.length, -1, 0))
1217             throwOpenSSLError();
1218     }
1219 
1220     private void checkFlag()
1221     {
1222         if (readOnly)
1223             throw new Exception("The cert is signed already, and cannot be modified.");
1224     }
1225 }
1226 
1227 
1228 version (Test)
1229 {
1230     import util.Test;
1231     import tango.io.Stdout;
1232 
1233     auto t1 = TimeSpan.zero;
1234     auto t2 = TimeSpan.fromDays(365); // can't set this up in delegate ..??
1235     unittest
1236     {
1237         Test.Status _pkeyGenTest(ref char[][] messages)
1238         {
1239             auto pkey = new PrivateKey(2048);
1240             string pem = pkey.pemFormat;
1241             auto pkey2 = new PrivateKey(pem);
1242             if (pkey == pkey2)
1243             {
1244                 auto pkey3 = new PrivateKey(2048);
1245                 string pem2 = pkey3.pemFormat("hello");
1246                 try
1247                     auto pkey4 = new PrivateKey(pem2, "badpass");
1248                 catch (Exception ex)
1249                 {
1250                     auto pkey4 = new PrivateKey(pem2, "hello");
1251                     return Test.Status.Success;
1252                 }
1253             }
1254                 
1255             return Test.Status.Failure;
1256         }
1257 
1258         Test.Status _certGenTest(ref char[][] messages)
1259         {
1260             auto cert = new Certificate();
1261             auto pkey = new PrivateKey(2048);
1262             cert.privateKey(pkey).serialNumber(123).dateBeforeOffset(t1).dateAfterOffset(t2);
1263             cert.setSubject("CA", "Alberta", "Place", "None", "First Last", "no unit", "email@example.com").sign(cert, pkey);
1264             string pemData = cert.pemFormat;
1265             auto cert2 = new Certificate(pemData);
1266 //            Stdout.formatln("{}\n{}\n{}\n{}", cert2.serialNumber, cert2.subject, cert2.dateBefore, cert2.dateAfter);
1267             if (cert2 == cert)
1268                 return Test.Status.Success;
1269             return Test.Status.Failure;
1270         }
1271 
1272         Test.Status _chainValidation(ref char[][] messages)
1273         {
1274             auto caCert = new Certificate();
1275             auto caPkey = new PrivateKey(2048);
1276             caCert.serialNumber = 1;
1277             caCert.privateKey = caPkey;
1278             caCert.dateBeforeOffset = t1;
1279             caCert.dateAfterOffset = t2;
1280             caCert.setSubject("CA", "Alberta", "CA Place", "Super CACerts Anon", "CA Manager");
1281             caCert.sign(caCert, caPkey);
1282             auto store = new CertificateStore();
1283             store.add(caCert);
1284 
1285             auto subCert = new Certificate();
1286             auto subPkey = new PrivateKey(2048);
1287             subCert.serialNumber = 2;
1288             subCert.privateKey = subPkey;
1289             subCert.dateBeforeOffset = t1;
1290             subCert.dateAfterOffset = t2;
1291             subCert.setSubject("US", "California", "Customer Place", "Penny-Pincher", "IT Director");
1292             subCert.sign(caCert, caPkey);
1293 
1294             if (subCert.verify(store))
1295             {
1296                 auto fakeCert = new Certificate();
1297                 auto fakePkey = new PrivateKey(2048);
1298                 fakeCert.serialNumber = 1;
1299                 fakeCert.privateKey = fakePkey;
1300                 fakeCert.dateBeforeOffset = t1;
1301                 fakeCert.dateAfterOffset = t2;
1302                 fakeCert.setSubject("CA", "Alberta", "CA Place", "Super CACerts Anon", "CA Manager");
1303                 fakeCert.sign(caCert, caPkey);
1304                 auto store2 = new CertificateStore();
1305                 if (!subCert.verify(store2))
1306                     return Test.Status.Success;
1307             }
1308 
1309             return Test.Status.Failure;
1310         }   
1311 
1312         Test.Status _rsaCrypto(ref char[][] messages)
1313         {
1314             auto key = new PrivateKey(2048);
1315             string pemData = key.publicKey.pemFormat;
1316             auto pub = new PublicKey(pemData);
1317             auto encrypted = pub.encrypt(cast(ubyte[])"Hello, how are you today?");
1318             auto decrypted = key.decrypt(encrypted);
1319             if (cast(char[])decrypted == "Hello, how are you today?")
1320             {
1321                 encrypted = key.encrypt(cast(ubyte[])"Hello, how are you today, mister?");
1322                 decrypted = pub.decrypt(encrypted);
1323                 if (cast(char[])decrypted == "Hello, how are you today, mister?")
1324                     return Test.Status.Success;
1325             }
1326             return Test.Status.Failure;
1327         }
1328 
1329         Test.Status _rsaSignVerify(ref char[][] messages)
1330         {
1331             auto key = new PrivateKey(1024);
1332             auto key2 = new PrivateKey(1024);
1333             ubyte[] data = cast(ubyte[])"I am some special data, yes I am.";
1334             ubyte[512] sigBuf;
1335             ubyte[512] sigBuf2;
1336             auto sig1 = key.sign(data, sigBuf);
1337             auto sig2 = key2.sign(data, sigBuf2);
1338             if (key.publicKey.verify(data, sig1))
1339             {
1340                 if (!key.publicKey.verify(data, sig2))
1341                 {
1342                     if (key2.publicKey.verify(data, sig2))
1343                     {
1344                         if (!key2.publicKey.verify(data, sig1))
1345                             return Test.Status.Success;
1346                     }
1347                 }
1348             }
1349 
1350             return Test.Status.Failure;
1351         }
1352 
1353 
1354         auto t = new Test("tetra.net.PKI");
1355         t["Public/Private Keypair"] = &_pkeyGenTest;
1356         t["Self-Signed Certificate"] = &_certGenTest;
1357         t["Chain Validation"] = &_chainValidation;
1358         t["RSA Crypto"] = &_rsaCrypto;
1359         t["RSA sign/verify"] = &_rsaSignVerify;
1360         t.run();
1361     }
1362 }