#include "wvfile.h"
#include "wvfileutils.h"
#include "wvrsa.h"
#include "wvtest.h"
#include "wvx509.h"
#include "wvx509mgr.h"
#include "wvautoconf.h"

// default keylen for where we're not using pre-existing certs
const static int DEFAULT_KEYLEN = 512; 

// carillon cert: has a number of interesting characteristics (policy,
// aia information, ...) which we can test against
const static char carillon_cert[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIFSzCCBDOgAwIBAgIBAzANBgkqhkiG9w0BAQUFADCBgDELMAkGA1UEBhMCQ0Ex\n"
"KzApBgNVBAoTIkNhcmlsbG9uIEluZm9ybWF0aW9uIFNlY3VyaXR5IEluYy4xHzAd\n"
"BgNVBAsTFkNlcnRpZmljYXRpb24gU2VydmljZXMxIzAhBgNVBAMTGlRlc3QgQ2Vy\n"
"dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTA2MTIyNDIyMzgzNVoXDTA5MTIyMzIyMzgz\n"
"NVowgYIxCzAJBgNVBAYTAkNBMSswKQYDVQQKDCJDYXJpbGxvbiBJbmZvcm1hdGlv\n"
"biBTZWN1cml0eSBJbmMuMQ4wDAYDVQQLDAVVc2VyczEVMBMGA1UEAwwMVGVzdGlu\n"
"ZyBVc2VyMR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGNhcmlsbG9uLmNhMIGfMA0GCSqG\n"
"SIb3DQEBAQUAA4GNADCBiQKBgQCwYnEgxl9uly9EeXg6N/SLzx6ZB5iU/REgB1bi\n"
"9u/4O7S7ebAh5VKO8JIo+0JaHW7pBFM0ywe3KvHwpvdsHvmRCQ4+Qze8itFuhDSS\n"
"TFJ7eP9bxiyroYIaRuS9G3xrM6jGdkw1IhmN2FDpWXBTWtfF/8Lor4p9TemGUARl\n"
"prfDjQIDAQABo4ICTjCCAkowCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCB4AwUAYJ\n"
"YIZIAYb4QgENBEMWQURvIE5vdCB0cnVzdCAtIENlcnRpUGF0aCBjb21wbGlhbnQg\n"
"SUQgQ2VydCBmb3IgVEVTVCBwdXJwb3NlcyBvbmx5MB0GA1UdDgQWBBRI8EWCS88U\n"
"Hug1TLLei5iiJGJSXTAfBgNVHSMEGDAWgBQZ8G5V0iRoGnd6l8LUaI01I0M3+jAb\n"
"BgNVHREEFDASgRB0ZXN0QGNhcmlsbG9uLmNhMEsGCCsGAQUFBwEBBD8wPTA7Bggr\n"
"BgEFBQcwAoYvaHR0cDovL3d3dy5jYXJpbGxvbi5jYS9jYW9wcy9tZWRpdW0tdGVz\n"
"dC1jYS5jcnQwPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDovL3d3dy5jYXJpbGxvbi5j\n"
"YS9jYW9wcy9tZWRpdW0tY3JsMS5jcmwwgfEGA1UdIASB6TCB5jBuBgsrBgEEAYHD\n"
"XgEBZTBfMF0GCCsGAQUFBwICMFEwKRoiQ2FyaWxsb24gSW5mb3JtYXRpb24gU2Vj\n"
"dXJpdHkgSW5jLjADAgEBGiRURVNUIG1lZGl1bSBzdyBwb2xpY3kgLSBkbyBub3Qg\n"
"dHJ1c3QwdAYLKwYBBAGBw14BAWYwZTBjBggrBgEFBQcCAjBXMCkaIkNhcmlsbG9u\n"
"IEluZm9ybWF0aW9uIFNlY3VyaXR5IEluYy4wAwIBARoqVEVTVCBtZWRpdW0gaGFy\n"
"ZHdhcmUgcG9saWN5IC0gZG8gbm90IHRydXN0MA0GCSqGSIb3DQEBBQUAA4IBAQAH\n"
"NnhF+cbDe2jCPZ8J36Vb9p7QBFhGHiPQKpWVgEISjfYDqyilDUMvqo1RshBnxdTt\n"
"cPMu1MxcQOOvU1f3jRCJPngzzZXW91zOZKQREPanekUcil7ZrY9MPgpIPqMgGw5h\n"
"rX5R/RyAG/vkfJXe5SMd4GeUOuPuDVCxM3y2cylT0TS5zaWSYNdmERDm5tp2fHlj\n"
"rqE1w9PZDeLz9ZhKfo/pcxCvE7RdS1Zhpi3KMUEtDXq9RN2D81mvo1TyzHvm84QB\n"
"R7Z/1y/HH8vZj7q//xJrJt/IqPuXcYUacaS520ouzPhXNRkMxl4VZ8fCGnbrqPig\n"
"0KUUwRu2l4LDN9drx+L5\n"
"-----END CERTIFICATE-----\n";

const static char rsakeytext[] = 
"-----BEGIN RSA PRIVATE KEY-----\n"
"MIICXgIBAAKBgQDVQrw1PHhLm2qSGl24SAb4+mJb2jPW0UwsbodDG5Da0h405zUp\n"
"0U5yL7zKNQG6BYvuV5bCUeo6Q0uvd7D6eKE7VqzcVHVJfM2KvfKOI5eMiu76pz6V\n"
"XxnW53kEdw1ZiCnCQTK1JntJn41ZsWxY7mQ3PLBpcobNIdoyUDwXIOFwOQIDAQAB\n"
"AoGAVcyuqgB1KX4Sx0tCT4TzATLDZc8JMjEso2eoldA+XDtTGde3pOZn2DrqirP+\n"
"yNe4b6Dfr7iDMwOmLKdMFcl4nAmE8UnOQWFtW52SCWFGLdiSqD8hn0gbKoYvaGDu\n"
"vlqN/KvKen0AxiveYS6T24GFzHejr2RVcmT4m5UogNGFR90CQQD7VcPzfcPJZ3c1\n"
"jWiJvoUrzdLqYactRoKidm+O7AVjMU7xkXCPLGgXbzVoyuzx11z1D/nFUE615t/v\n"
"vkKVsnTDAkEA2TgOnSX6gSClZLUy7bWoSHQwcuj3Ogt2C0zA9p/sRgRaAuNCDWNC\n"
"OmkRAHjbOtJK0tChxoG0BYdyv9WIZ5bHUwJBANf+0xH06UezNY2+YzLNmyEUF8j5\n"
"93Q/fpEke6c2S0L94zxTo4pHvYU2O449pvgH/4lUG3FpHNvS+GzO8+Y2oYUCQQC8\n"
"Nf8rmOmqEvBcB0jegQUT6mDEYCk+yQl6FwInb0AZFtIrKGBmGzgaRkkuAInsOKQO\n"
"cCmMR3wFQmxh3ZI4N4PzAkEA4YbOing3yMDXxUFByZMKSX6oqwNePuVfK1IHmoUP\n"
"oZxEIiwTN7rE9kRV1OFDUwvGiqcegSxjf6SUgdpBC7Du9A==\n"
"-----END RSA PRIVATE KEY-----\n";
    
const static char x509certtext[] = 
"-----BEGIN CERTIFICATE-----\n"
"MIICXDCCAcWgAwIBAgIEM7WjbjANBgkqhkiG9w0BAQUFADBBMRUwEwYDVQQDEwx0\n"
"ZXN0LmZvby5jb20xEzARBgoJkiaJk/IsZAEZEwNmb28xEzARBgoJkiaJk/IsZAEZ\n"
"EwNjb20wHhcNMDQwMzI2MjEwNTI5WhcNMTQwMzI0MjEwNTI5WjBBMRUwEwYDVQQD\n"
"Ewx0ZXN0LmZvby5jb20xEzARBgoJkiaJk/IsZAEZEwNmb28xEzARBgoJkiaJk/Is\n"
"ZAEZEwNjb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANVCvDU8eEubapIa\n"
"XbhIBvj6YlvaM9bRTCxuh0MbkNrSHjTnNSnRTnIvvMo1AboFi+5XlsJR6jpDS693\n"
"sPp4oTtWrNxUdUl8zYq98o4jl4yK7vqnPpVfGdbneQR3DVmIKcJBMrUme0mfjVmx\n"
"bFjuZDc8sGlyhs0h2jJQPBcg4XA5AgMBAAGjYTBfMBEGCWCGSAGG+EIBAQQEAwIG\n"
"QDAbBglghkgBhvhCAQwEDhYMdGVzdC5mb28uY29tMA4GA1UdDwEB/wQEAwICpDAd\n"
"BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADgYEA\n"
"D8WtaiTyx3DkHiJwC7EHVCOTqRctuS+3x1y0b2CO/3ijWASg+UDvGFKpAT4i6EF4\n"
"/e1aTgQEJ3a29XQEKamk5Py2AAb7acBbuKIUzEzcFx4QzzY+fdz1PgKz4DiUypZj\n"
"UFCAL0f74uuFTQ7ylt6F0m554LSniOHDOEzyBZZq/bk=\n"
"-----END CERTIFICATE-----\n";
    
const static char certreqtext[] =
"-----BEGIN CERTIFICATE REQUEST-----\n"
"MIIBazCB1QIBADAsMSowKAYKCZImiZPyLGQBGRMadGVzdC5mb28uY29tL0RDPWZv\n"
"by9EQz1jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANVCvDU8eEubapIa\n"
"XbhIBvj6YlvaM9bRTCxuh0MbkNrSHjTnNSnRTnIvvMo1AboFi+5XlsJR6jpDS693\n"
"sPp4oTtWrNxUdUl8zYq98o4jl4yK7vqnPpVfGdbneQR3DVmIKcJBMrUme0mfjVmx\n"
"bFjuZDc8sGlyhs0h2jJQPBcg4XA5AgMBAAGgADANBgkqhkiG9w0BAQUFAAOBgQA3\n"
"+zj1ZCRGOqCyWJFT0J6P+LJxn57My7GKkUihXQFo6xym98kKfYwc7HTIVq+clnYA\n"
"rqmoQu1THFu6mVRdM9zp2zmRBtsmOxKkhx89RtNDhKu75HQE8S5xGmCPyZhFA0eX\n"
"elfqYQM30sg+MeZVTccV0wCd9augzYa2qA8MExu7SQ==\n"
"-----END CERTIFICATE REQUEST-----\n";
	
const static char signedcerttext[] =
"-----BEGIN CERTIFICATE-----\n"
"MIICiDCCAfGgAwIBAgIBATANBgkqhkiG9w0BAQQFADAUMRIwEAYDVQQKEwlOSVRJ\n"
"LVRFU1QwHhcNMDQwMzMwMTgzOTM4WhcNMDUwMzMwMTgzOTM4WjBkMQswCQYDVQQG\n"
"EwJDQTEPMA0GA1UECBMGUXVlYmVjMREwDwYDVQQHEwhNb250cmVhbDESMBAGA1UE\n"
"ChMJVGVzdHkgT25lMR0wGwYDVQQDExRhcmlhMi53ZWF2ZXJuZXQubnVsbDCBnzAN\n"
"BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtQcmm+hJqHI+xy1vdpyaTiKPOk58TJSH\n"
"dtj1JTZLmM7ZIDFZKgVAQvX+Y3V8wnCsKnF4U0fW14T0uHEWHhPjrCq+XBgw2NJA\n"
"GnKXZDX9QXDHKqBj7ttF+gTXztkpdBYjY9eHFfsETWbm7jgqLx6nDo2MULmipXWr\n"
"FM19VkZgRjUCAwEAAaOBmTCBljAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1P\n"
"cGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUzpHzxgB4JiHt\n"
"/EbtqgfEbSpsThUwPAYDVR0jBDUwM4AUx1ECvcgeUvpwQJNQs2g3qq0CPzWhGKQW\n"
"MBQxEjAQBgNVBAoTCU5JVEktVEVTVIIBADANBgkqhkiG9w0BAQQFAAOBgQCLUpzC\n"
"nPlIYTDK7rq1Mh/Bqcw0RYamxUOCMsNmkMBovb37yfmo4k+HfhiHhiM6Ao6H5GIF\n"
"+p09tcn1Iotrek1IGrXsH7Hjkpzf8lkqwEjygIJ0iAWMqAI4AVoRx/5JrG+8ePPU\n"
"rKZf8WzBwn45ibb+xwtjdQXVro1l+K4UATTHLQ==\n"
"-----END CERTIFICATE-----\n";


static void basic_test(WvX509 &t509, WvStringParm dname)
{
    WVPASS(t509.isok());
    WVPASSEQ(t509.get_subject(), dname);
    WVPASSEQ(t509.get_issuer(), dname);
}


WVTEST_MAIN("X509")
{
    // Setup a new DN entry, like a server would set.
    const WvString dName("cn=test.foo.com,dc=foo,dc=com");
    const WvString dName1("/CN=test.foo.com/DC=foo/DC=com");
    const WvString dName2("/C=CA/ST=Quebec/L=Montreal/O=Testy One"
			  "/CN=aria2.weavernet.null"
			  "/DC=webmaster@weavernet.null");
    
    {
	WvX509Mgr t509;
	WVFAIL(t509.isok());
	WVFAIL(t509.test());
    }
    
    // WIN32 people!  If your unit tests are crashing here, you probably
    // linked openssl with a different version of msvcr*.dll than the
    // unit test.  We pass a FILE* from one to the other here, and it
    // won't work if there's a library mismatch.
    {
        WvX509Mgr t509;
	t509.decode(WvRSAKey::RsaPEM, rsakeytext);
	t509.decode(WvX509::CertPEM, x509certtext);
	basic_test(t509, dName1);
	WvString certencode = t509.encode(WvX509::CertPEM);
	WVPASSEQ(certencode, x509certtext);
	WvString rsaencode = t509.encode(WvRSAKey::RsaPEM);
	WVPASSEQ(rsaencode, rsakeytext);
        WvRSAKey rsa;
        rsa.decode(WvRSAKey::RsaPEM, rsakeytext);
	WVPASSEQ(WvX509::certreq(t509.get_subject(), rsa),
                 certreqtext);
    }
    {
        // test the copy constructor for x509
        WvX509 t1;
	t1.decode(WvX509::CertPEM, x509certtext);
        basic_test(t1, dName1);

        WvX509 t2(t1);
        basic_test(t2, dName1);
    }

    {
        // test the copy construct for x509mgr
        WvX509Mgr t1;
	t1.decode(WvX509::CertPEM, x509certtext);
	t1.decode(WvRSAKey::RsaPEM, rsakeytext);
        basic_test(t1, dName1);

        WvX509Mgr t2(t1);
        basic_test(t2, dName1);
        WVPASSEQ(t1.encode(WvRSAKey::RsaPEM), t2.encode(WvRSAKey::RsaPEM));
    }

    {
	WvX509Mgr t509(dName, 1024);
	basic_test(t509, dName1);
    }
    {        
        WvX509 t509;
	t509.decode(WvX509::CertPEM, signedcerttext);
	WVPASS(t509.isok()); 
        WVFAIL(t509.validate()); // this certificate has expired
	WVPASSEQ(t509.get_subject(),
		 "/C=CA/ST=Quebec/L=Montreal/O=Testy One"
		 "/CN=aria2.weavernet.null");
	WVPASSEQ(t509.get_issuer(), "/O=NITI-TEST");
        WVPASSEQ(t509.get_ski(), 
             "CE:91:F3:C6:00:78:26:21:ED:FC:46:ED:AA:07:C4:6D:2A:6C:4E:15");
        WVPASSEQ(t509.get_aki(), 
             "C7:51:02:BD:C8:1E:52:FA:70:40:93:50:B3:68:37:AA:AD:02:3F:35");
    }    
    {
	// Now we stress test it to make sure that it fails predictably...
	WvX509Mgr t509;
	t509.decode(WvRSAKey::RsaPEM, WvString::null);
	t509.decode(WvX509::CertPEM, "");
	WVFAIL(t509.test());
	WVFAIL(t509.isok());
	WVFAIL(t509.get_subject() == "/CN=test.foo.com/DC=foo/DC=com");
	WVFAIL(t509.get_issuer() == "/CN=test.foo.com/DC=foo/DC=com");
    }
}


WVTEST_MAIN("mismatched key / certificate")
{
    WvX509Mgr t509;
    t509.decode(WvX509::CertPEM, signedcerttext);
    t509.decode(WvRSAKey::RsaPEM, rsakeytext);
    WVFAIL(t509.isok());
    WVFAIL(t509.test());
}


WVTEST_MAIN("Hexified encoding/decoding")
{
    const char cert_hex[] ="3082030030820269a003020102020460ad1ad"
    "5300d06092a864886f70d010105050030818e310b3009060355040613024341310"
    "f300d060355040813065175656265633111300f060355040713084d6f6e7472656"
    "16c31123010060355040a13095465737479204f6e65311d301b060355040313146"
    "1726961322e7765617665726e65742e6e756c6c31283026060a0992268993f22c6"
    "4011916187765626d6173746572407765617665726e65742e6e756c6c301e170d3"
    "034303333313230343235315a170d3134303332393230343235315a30818e310b3"
    "009060355040613024341310f300d060355040813065175656265633111300f060"
    "355040713084d6f6e747265616c31123010060355040a13095465737479204f6e6"
    "5311d301b0603550403131461726961322e7765617665726e65742e6e756c6c312"
    "83026060a0992268993f22c64011916187765626d6173746572407765617665726"
    "e65742e6e756c6c30819f300d06092a864886f70d010101050003818d003081890"
    "2818100b507269be849a8723ec72d6f769c9a4e228f3a4e7c4c948776d8f525364"
    "b98ced92031592a054042f5fe63757cc270ac2a71785347d6d784f4b871161e13e"
    "3ac2abe5c1830d8d2401a72976435fd4170c72aa063eedb45fa04d7ced92974162"
    "363d78715fb044d66e6ee382a2f1ea70e8d8c50b9a2a575ab14cd7d56466046350"
    "203010001a3693067301106096086480186f842010104040302064030230609608"
    "6480186f842010c0416161461726961322e7765617665726e65742e6e756c6c300"
    "e0603551d0f0101ff0404030202a4301d0603551d250416301406082b060105050"
    "7030106082b06010505070302300d06092a864886f70d010105050003818100b2b"
    "fe7b80feec0bfe23250f5d9eb3d409364628c001fee50185eb755ffb863093fe23"
    "973c939fdd03bc90b32eb697f1eb9b4a7a0134ee78a509992da0d803af22b5148a"
    "76b197a54126be4a3d03897ed6cc98e77e65b797aee3f3b66c17afb4a2fd6dc498"
    "cd86f4d7952cfde7d1e044a38373f80b9d1da51461267a83d967f24"; 

    const char rsa_hex[] = "3082025e02010002818100b507269be849a872"
    "3ec72d6f769c9a4e228f3a4e7c4c948776d8f525364b98ced92031592a054042f5"
    "fe63757cc270ac2a71785347d6d784f4b871161e13e3ac2abe5c1830d8d2401a72"
    "976435fd4170c72aa063eedb45fa04d7ced92974162363d78715fb044d66e6ee38"
    "2a2f1ea70e8d8c50b9a2a575ab14cd7d5646604635020301000102818100b277a4"
    "469c10d1f21f95f963240a6bcd9020a818ec4e0b3829a0e6bd92f3a0687c825264"
    "571aea29999efbaabe1e6b3a3075c16c492cb338ae928f5a80b897005fdeca7966"
    "14f58329552bfd117755fae42ba39aa3266bb2560377d5d747e31ddfd2ae1bbd9b"
    "8979e8748d4d47573588a4140715fbc14ea373f2461517749641024100f0b0ddee"
    "376d6e08c9dc0bce540e9b464bf23121241cf6dd69fd67c9e195c6dce0ddb47012"
    "56761567a8c70fff3e12cc412e8cb74f120eb620d7d129624c359d024100c08acd"
    "153fe5f801c81a85a62b63f50b9346ebe350a18c3aecd11379a17093f52fdea874"
    "df97b22189dd4ca479723422c0a5b5124873e087f316cbf681d2fb79024100810c"
    "33517fc25a56b7f4151861151bc78afca5bec1200e741459db85f03f5fca197e85"
    "39f97b0600dffd2c0db5aa5065d724e02980698c1db66a4028d21d4e3902405e57"
    "a48574f9c9bb95c0e91bb2c7179ac45f4bd5e5fc4229dd3fd4bb144f852fee74bb"
    "360918db3f73bdeb7febc1f9a9cd9b644dc112864216ea64a634969c81024100e5"
    "453ec9a3cdbe2fe17e86b32e998fe8713066ed254a60390f7898e4e536dea92af7"
    "743d55a35fad75c14fe1e239251c471e133ca8e85ef3a1d3c5b6b288bfda";

    const WvString dName2("/C=CA/ST=Quebec/L=Montreal"
			  "/O=Testy One/CN=aria2.weavernet.null"
			  "/DC=webmaster@weavernet.null");

    WvX509Mgr t509;
    t509.decode(WvX509Mgr::CertHex, cert_hex);
    t509.decode(WvRSAKey::RsaHex, rsa_hex);
    basic_test(t509, dName2);
    
    WVPASSEQ(t509.get_serial(), "1621957333");
    WVPASSEQ(t509.encode(WvX509Mgr::CertHex), cert_hex);
    WVPASSEQ(t509.encode(WvRSAKey::RsaHex), rsa_hex);
}


WVTEST_MAIN("Replacing key failure")
{
    WvX509Mgr t509("cn=test.foo.com,dc=foo,dc=com", DEFAULT_KEYLEN);
    WVPASS(t509.test());
    WvRSAKey rsakey(DEFAULT_KEYLEN);
    t509.decode(WvRSAKey::RsaHex, rsakey.encode(WvRSAKey::RsaHex));
    WVFAIL(t509.test());
}


WVTEST_MAIN("Get extensions memory corruption")
{
    // this test validates a workaround for a bug
    // in openssl 0.9.8 -- where getting certain attributes using
    // ASN1_item_d2i could cause openssl to shuffle around
    // const pointers, leading to weird memory behaviour on
    // destroys

    WvX509 t509;
    t509.decode(WvX509Mgr::CertPEM, carillon_cert);

    WVPASS(t509.isok());
    WVPASSEQ("CA Issuers - "
	     "URI:http://www.carillon.ca/caops/medium-test-ca.crt", 
             t509.get_aia());
}


WVTEST_MAIN("set_aia")
{
    // yeah, the certificate generated doesn't make much sense here,
    // but we're only testing that the extension works properly

    WvX509Mgr x("cn=test.foo.com,dc=foo,dc=com", DEFAULT_KEYLEN, false);
    WvStringList ca_in;
    WvStringList ocsp_in;
    ca_in.append("http://localhost/~wlach/testca.pem");
    ca_in.append("http://localhost/~wlach/testca-alt.pem");
    ocsp_in.append("http://localhost/~wlach/blah.ocsp");
    ocsp_in.append("http://localhost/~wlach/blarg.ocsp");

    x.set_aia(ca_in, ocsp_in);
    WvStringList ca_out;
    x.get_ca_urls(ca_out);
    WvStringList ocsp_out;
    x.get_ocsp(ocsp_out);

    WVPASSEQ(ca_in.popstr(), ca_out.popstr());
    WVPASSEQ(ca_in.popstr(), ca_out.popstr());
    WVPASSEQ(ocsp_in.popstr(), ocsp_out.popstr());
    WVPASSEQ(ocsp_in.popstr(), ocsp_out.popstr());
    WVPASSEQ(ca_in.count(), 0);
    WVPASSEQ(ca_out.count(), 0);
    WVPASSEQ(ocsp_in.count(), 0);
    WVPASSEQ(ocsp_out.count(), 0);
}


WVTEST_MAIN("set_crl_dp")
{
    WvX509Mgr x("cn=test.foo.com,dc=foo,dc=com", DEFAULT_KEYLEN, false);
    WvStringList dp_in;
    dp_in.append("http://localhost/~wlach/testca.crl");
    dp_in.append("http://localhost/~wlach/testca-alt.crl");
    x.set_crl_urls(dp_in);

    WvStringList dp_out;
    x.get_crl_urls(dp_out);

    WVPASSEQ(dp_in.popstr(), dp_out.popstr());
    WVPASSEQ(dp_in.popstr(), dp_out.popstr());
    WVPASSEQ(dp_in.count(), 0);
    WVPASSEQ(dp_out.count(), 0);
}


WVTEST_MAIN("certreq / signreq / signcert")
{
    // certificate request
    WvRSAKey rsakey(DEFAULT_KEYLEN);
    WvString certreq 
	= WvX509Mgr::certreq("cn=test.signed.com,dc=signed,dc=com", rsakey);

    WvX509Mgr cacert("CN=test.foo.com,DC=foo,DC=com", DEFAULT_KEYLEN, true);
    WVPASS(cacert.isok());
    WVPASS(cacert.validate());
    
    WvX509 cert;
    WvString certpem = cacert.signreq(certreq);
    cert.decode(WvX509Mgr::CertPEM, certpem);

    // test that it initially checks out
    WVPASS(cert.isok());
    WVPASS(cert.validate());
    WVPASS(cert.issuedbyca(cacert));
    WVPASS(cert.signedbyca(cacert));
    WVPASSEQ(cert.get_aki(), cacert.get_ski());
    
    // change some stuff, verify that tests fail
    WvStringList ca_in, ocsp_in;
    ca_in.append("http://localhost/~wlach/testca.pem");
    ca_in.append("http://localhost/~wlach/testca-alt.pem");
    cert.set_aia(ca_in, ocsp_in);
    WVPASS(cert.issuedbyca(cacert));
    WVFAIL(cert.signedbyca(cacert));

    // should pass again after re-signing
    cacert.signcert(cert);    
    WVPASSEQ(cert.get_issuer(), cacert.get_subject());
    WVPASS(cert.issuedbyca(cacert));
    WVPASS(cert.signedbyca(cacert));
}


WVTEST_MAIN("certificate policies")
{
    WvX509 t509;
    t509.decode(WvX509Mgr::CertPEM, carillon_cert);

    WvStringList policies;
    WVPASS(t509.get_policies(policies));
    WVPASSEQ(policies.popstr(), "1.3.6.1.4.1.25054.1.1.101");
    WVPASSEQ(policies.popstr(), "1.3.6.1.4.1.25054.1.1.102");
    WVPASSEQ(policies.count(), 0);

    policies.zap();
    WvX509Mgr cacert("CN=test.foo.com, DC=foo, DC=com", DEFAULT_KEYLEN, true);
    WVFAIL(cacert.get_policies(policies)); 
    WVPASSEQ(policies.count(), 0);
}


WVTEST_MAIN("certificate policies set and get")
{
    WvX509Mgr x("cn=test.foo.com,dc=foo,dc=com", DEFAULT_KEYLEN, false);
    WvStringList policies_in;
    policies_in.append("1.2.3.4");
    policies_in.append("1.2.3.5");
    x.set_policies(policies_in);

    WvStringList policies_out;
    x.get_policies(policies_out);

    WVPASSEQ(policies_in.popstr(), policies_out.popstr());
    WVPASSEQ(policies_in.popstr(), policies_out.popstr());
    WVPASSEQ(policies_in.count(), 0);
    WVPASSEQ(policies_out.count(), 0);
}


WVTEST_MAIN("basic constraints")
{
    bool is_ca;
    int pathlen;

    // the carillon cert is a user cert, so it should have no path length,
    // and the ca bit of basicConstraints set to false
    WvX509 t509;
    t509.decode(WvX509Mgr::CertPEM, carillon_cert);

    WVPASS(t509.get_basic_constraints(is_ca, pathlen));
    WVFAIL(is_ca);
    WVPASSEQ(pathlen, (-1)); // no path length restriction

    // by default, a CA certificate we create should have the ca bit set to 
    // true, and no path length restriction.
    WvX509Mgr cacert("CN=test.foo.com, DC=foo, DC=com", DEFAULT_KEYLEN, true);
    WVPASS(cacert.get_basic_constraints(is_ca, pathlen));
    WVPASS(is_ca);
    WVPASSEQ(pathlen, (-1)); // no path length restriction

    // now, let's try setting the path length, and see what the result is
    cacert.set_basic_constraints(true, 5);
    WVPASS(cacert.get_basic_constraints(is_ca, pathlen));
    WVPASS(is_ca);
    WVPASSEQ(pathlen, 5); 
    

}


#ifdef HAVE_OPENSSL_POLICY_MAPPING
WVTEST_MAIN("get/set certificate policy extensions")
{
    WvRSAKey rsakey(DEFAULT_KEYLEN);

    WvString certreq 
	= WvX509Mgr::certreq("cn=test.signed.com,dc=signed,dc=com", rsakey);
    WvX509Mgr cacert("CN=test.foo.com, DC=foo, DC=com", DEFAULT_KEYLEN, true);
    WvString certpem = cacert.signreq(certreq);
    
    WvX509Mgr cert;
    cert.decode(WvX509Mgr::CertPEM, certpem);

    int require_explicit_policy_in = 10;
    int inhibit_policy_mapping_in = 5;
    cert.set_policy_constraints(require_explicit_policy_in, 
                         inhibit_policy_mapping_in);

    WvString issuer_domain_in = "2.16.840.1.101.3.2.1.48.1";
    WvString subject_domain_in = "2.16.840.1.101.3.2.1.48.2";
    WvX509Mgr::PolicyMapList list;
    list.append(new WvX509Mgr::PolicyMap(issuer_domain_in, subject_domain_in), 
                true);
    cert.set_policy_mapping(list);
    list.zap();
    cacert.signcert(cert);    

    int require_explicit_policy_out = 0;
    int inhibit_policy_mapping_out = 0;
    cert.get_policy_constraints(require_explicit_policy_out, 
                                inhibit_policy_mapping_out);
    WVPASSEQ(require_explicit_policy_in, require_explicit_policy_out);
    WVPASSEQ(inhibit_policy_mapping_in, inhibit_policy_mapping_out);

    WVPASS(cert.get_policy_mapping(list));
    WVPASSEQ(list.count(), 1);
    if (list.count() > 0)
    {
        WvX509Mgr::PolicyMap *map = list.first();
        WVPASSEQ(map->issuer_domain, issuer_domain_in);
        WVPASSEQ(map->subject_domain, subject_domain_in);
    }
}


WVTEST_MAIN("ski / aki")
{
    WvRSAKey rsakey(DEFAULT_KEYLEN);
    WvString certreq 
	= WvX509Mgr::certreq("cn=test.signed.com,dc=signed,dc=com", rsakey);
    WvX509Mgr cacert("CN=test.foo.com, DC=foo, DC=com", DEFAULT_KEYLEN, true);

    WvString certpem = cacert.signreq(certreq);
    wvcon->print("\n%s\n", certpem);
    WvX509Mgr cert;
    cert.decode(WvX509Mgr::CertPEM, certpem);
    WVPASS(!!cert.get_ski());
    WVPASS(!!cert.get_aki());
    WVPASS(!!cacert.get_ski());
    WVFAIL(!!cacert.get_aki());
    WVPASSEQ(cert.get_aki(), cacert.get_ski());
}
#endif // HAVE_OPENSSL_POLICY_MAPPING

bool test_encode_decode_str(WvX509::DumpMode mode)
{
    WvX509Mgr cert1("cn=test.foo.com,dc=foo,dc=com", DEFAULT_KEYLEN, false);
    WvString s = cert1.encode(mode);
    WvX509Mgr cert2;
    cert2.decode(mode, s);

    WVPASSEQ(cert1.get_subject(), cert2.get_subject());
    return cert1.get_subject() == cert2.get_subject();
}


bool test_encode_decode_buf(WvX509::DumpMode mode)
{
    WvX509Mgr cert1("cn=test.foo.com,dc=foo,dc=com", DEFAULT_KEYLEN, false);
    WvDynBuf buf;
    cert1.encode(mode, buf);
    WvX509Mgr cert2;
    cert2.decode(mode, buf);

    WVPASSEQ(cert1.get_subject(), cert2.get_subject());
    return cert1.get_subject() == cert2.get_subject();
}


bool test_encode_load_file(WvX509::DumpMode mode)
{
    WvString tmpfile = wvtmpfilename("cert");

    WvX509Mgr cert1("cn=test.foo.com,dc=foo,dc=com", DEFAULT_KEYLEN, false);
    {
        WvFile f(tmpfile, O_WRONLY);
        WvDynBuf buf;
        cert1.encode(mode, buf);
	fprintf(stderr, "Encode to file '%s' (%ld bytes)\n",
		tmpfile.cstr(), (long)buf.used());
	size_t used = buf.used(), len;
        len = f.write(buf, buf.used());
	WVPASSEQ(len, used);
	fprintf(stderr, "Write result: %s\n", f.errstr().cstr());
    }

    WvX509Mgr cert2;
    if (mode == WvX509::CertPEM)
	cert2.decode(WvX509::CertFilePEM, tmpfile);
    else
        cert2.decode(WvX509::CertFileDER, tmpfile);
    
    unlink(tmpfile);

    WVPASSEQ(cert1.get_subject(), cert2.get_subject());
    return cert1.get_subject() == cert2.get_subject();
}


WVTEST_MAIN("encode / decode / load")
{
    WVPASS(test_encode_decode_str(WvX509::CertPEM));
    WVPASS(test_encode_decode_str(WvX509::CertHex));    

    WVPASS(test_encode_decode_buf(WvX509::CertPEM));
    WVPASS(test_encode_decode_buf(WvX509::CertHex));
    WVPASS(test_encode_decode_buf(WvX509::CertDER));
    
    WVPASS(test_encode_load_file(WvX509::CertPEM));
    WVPASS(test_encode_load_file(WvX509::CertDER));
}


WVTEST_MAIN("pkcs12")
{
    WvX509Mgr cert1("CN=test.foo.com, DC=foo, DC=com", DEFAULT_KEYLEN, true);
    WvString p12fname = wvtmpfilename("p12");
    cert1.write_p12(p12fname, "123");

    WvX509Mgr cert2;
    cert2.read_p12(p12fname, "123");
    WVPASS(cert2.isok());
    WVPASSEQ(cert1.get_ski(), cert2.get_ski());

    WvX509Mgr cert3;
    cert3.read_p12(p12fname, "321");
    WVFAIL(cert3.isok());

    ::unlink(p12fname);
}


const char random_cert[] = "-----BEGIN CERTIFICATE-----\n"
"MIIDvzCCAqegAwIBAgIEShc7yDANBgkqhkiG9w0BAQUFADBWMR0wGwYDVQQDExRj\n"
"b250aW5nZW5jeXdvcmtzLmNvbTEgMB4GCgmSJomT8ixkARkTEGNvbnRpbmdlbmN5\n"
"d29ya3MxEzARBgoJkiaJk/IsZAEZEwNjb20wHhcNMDgwNzMxMjAyMzE4WhcNMTgw\n"
"NzI5MjAyMzE4WjBWMR0wGwYDVQQDExRjb250aW5nZW5jeXdvcmtzLmNvbTEgMB4G\n"
"CgmSJomT8ixkARkTEGNvbnRpbmdlbmN5d29ya3MxEzARBgoJkiaJk/IsZAEZEwNj\n"
"b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrFneQgdQSoVfzCDNu\n"
"IAXhFpaCJvxDzWPCCZXWPf1/OlncskrRs4FirMnrPUyvw74krPh4lVILKZxIp+Io\n"
"s7DX8oPHct1ZjQoi7eK7r6iva38ghI3YFPtZSXC3z1DllKzUTezBzNJDkhg/SqUX\n"
"ne4NNw1mZ90Q07Y8KgREyjJ8/A0+jUNoQ3TydXPWsdNevP6jcczyULbzhlZrCj5J\n"
"7c21sSWkrm7HUegvJNPPv7vX67onYCMhj0f8Fsiw7TqacmmrJksrW3ifpCf5smIZ\n"
"/W8F9RwSefnVLn1TA2UoNAFTPDxwPVWFuOEkP8ObDnZXzfWc5WHRbXeVLnTAKbab\n"
"T6slAgMBAAGjgZQwgZEwHQYDVR0OBBYEFGryLpBlzbGgYCcpX/MbiAQj8nb8MBEG\n"
"CWCGSAGG+EIBAQQEAwIGQDAjBglghkgBhvhCAQwEFhYUY29udGluZ2VuY3l3b3Jr\n"
"cy5jb20wDgYDVR0PAQH/BAQDAgOoMAkGA1UdEwQCMAAwHQYDVR0lBBYwFAYIKwYB\n"
"BQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3DQEBBQUAA4IBAQBkWsSLHPmDajwg12AT\n"
"2QW3pxJKVu46nfdgLLTo6bQekAF+kYRERjbC9E/T5Eia5+6yTUbWR8xxDZoftkSK\n"
"/m3h7/Y+mqb13XiOowTkiJWzL5kwdv3Pb4O5X7YYMcNC4Rnax806b5pplXmzDY9Z\n"
"rqq/uP6wsRFiu5S7UcfA+KjoTrjMYsSLERAEus5RaP9J6uDvdBn0PwdfTT5rr2yb\n"
"et8AuJcBLFfFuLXPKfUlbnMe29BUr9s1A01OfHDCmzVN8hAhndBs2xDI1FOy9xDt\n"
"QXj9z/yvsK0AYwxknkHKqRboW//DVk6tcsL9dCiGSlZta5ob5yt6KK+R5X+rtQ0W\n"
"InR4\n"
"-----END CERTIFICATE-----";

WVTEST_MAIN("get_fingerprint")
{
    WvX509 foo;
    foo.decode(WvX509::CertPEM, random_cert);

    WVPASSEQ(foo.get_fingerprint(WvX509::FingerSHA1),
		"CA:49:50:D4:44:51:0B:AD:46:4F:E8:6C:3B:B2:3E:3F:61:31:27:ED");
    WVPASSEQ(foo.get_fingerprint(WvX509::FingerMD5),
		"53:FD:C7:D4:8F:45:AE:1D:90:14:45:B4:0C:1B:02:BD");
}


WVTEST_MAIN("extended key usage")
{
    WvX509Mgr ca("CN=test.foo.com,DC=foo,DC=com", DEFAULT_KEYLEN, true);

    WVPASSEQ(ca.get_ext_key_usage(), "");
    ca.set_ext_key_usage("TLS Web Server Authentication, "
                         "TLS Web Client Authentication, OCSP Signing");
    WVPASSEQ(ca.get_ext_key_usage(), "TLS Web Server Authentication;\n"
             "TLS Web Client Authentication;\nOCSP Signing");

    ca.set_ext_key_usage("OCSP Signing");
    WVPASSEQ(ca.get_ext_key_usage(), "OCSP Signing");
}
