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 }