tango.net.util.PKI

License:

BSD style: see license.txt

Author:

Jeff Davey
int SSL_VERIFY_NONE [const]
PKI provides Public Key Infrastructure.
Specifically, it provides the ability to:

- Make a X509 Certificate (SSL Certificate)

- Make a Public and Private key pair

- Validate a X509 Certificate against a Certificate Authority

- Generate a SSLCtx for SSLSocket and SSLServerSocket

- Wrap a SSLVerifyCallback so that retrieving the peer cert is easier

PKI requires the OpenSSL library, and uses a dynamic binding to the library. You can find the library at http://www.openssl.org and a Win32 specific port at http://www.slproweb.com/products/Win32OpenSSL.html.

Do not verify the peer certificate. Nor fail if it's not provided (server only).
int SSL_VERIFY_PEER [const]
Ask for a peer certificate, but do not fail if it is not provided.
int SSL_VERIFY_FAIL_IF_NO_PEER_CERT [const]
Ask for a peer certificate, however, fail if it is not provided
int SSL_VERIFY_CLIENT_ONCE [const]
Only validate once, do not re-validate during handshake renegotiation.
alias int function(int, X509_STORE_CTX* ctx) SSLVerifyCallback [extern(C)]
SSLVerifyCallback is passed into SSLCtx and is called during handshake when OpenSSL is doing certificate validation.
Wrapping the X509_STORE_CTX in the CertificateStoreCtx utility class gives the ability to access the peer certificate, and reason for error.
class SSLCtx
SSLCtx is provided to SSLSocket and SSLServerSocket.
It contains the public/private keypair, and some additional options that control how the SSL streams work.

Example
1
2
3
4
5
6
auto cert = new Certificate(cast(char[])File("public.pem").read);
auto pkey = new PrivateKey(cast(char[])File("private.pem").read);;
auto ctx = new SSLCtx();
ctx.certificate = cert;
ctx.pkey = pkey;
ctx.checkKey();
this()
Creates a new SSLCtx supporting SSLv3 and TLSv1 methods.
SSL_CTX* native() [@property]
Return the native context from OpenSSL
SSLCtx certificate(Certificate cert)
Assigns a X509 Certificate to the SSLCtx.
This is required for SSL
SSLCtx privateKey(PrivateKey key)
Assigns a PrivateKey (public/private keypair to the SSLCtx.
This is required for SSL.
SSLCtx checkKey() [@property]
Validates that the X509 certificate was signed with the provided public/private keypair. Throws an exception if this is not the case.
SSLCtx setVerification(int flags, SSLVerifyCallback cb)
Sets a SSLVerifyCallback function using the SSL_VERIFY_(NONE|PEER|etc) flags to control how verification is handled.
SSLCtx store(CertificateStore store)
Sets a CertificateStore of certs that are valid and trust Certificate Authorities during verification.
SSLCtx caCertsPath(const(char)[] path)
Loads valid Certificate Authorities from the specified path.
From the SSL_CTX_load_verify_locations manpage:

Each file must contain only one CA certificate. Also, the files are looked up by the CA subject name hash value, which must be available. If more than one CA certificate with the same name hash value exists, the extension must be different. (ie: 9d66eef0.0, 9d66eef0.1, etc). The search is performed in the ordering of the extension, regardless of other properties of the certificates. Use the c_rehash utility to create the necessary symlinks
class CertificateStoreCtx
The CertificateStoreCtx is a wrapper to the SSLVerifyCallback X509_STORE_CTX parameter.
It allows retrieving the peer certificate, and examining any errors during validation.

The following example will probably change sometime soon.

Example
1
2
3
4
5
6
7
8
9
10
extern (C)
{
    int myCallback(int code, X509_STORE_CTX *ctx)
    {
        auto myCtx = new CertificateStoreCtx(ctx);
        Certificate cert = myCtx.cert;
        Stdout(cert.subject).newline;
        return 0; // BAD CERT! (1 is good)
    }
}
this(X509_STORE_CTX* ctx)
This constructor takes a X509_STORE_CTX as provided by the SSLVerifyCallback function.
Certificate cert()
Returns the peer certificate.
class CertificateStore
CertificateStore stores numerous X509 Certificates for use in CRL lists, CA lists, etc.
Example
1
2
3
4
5
6
7
8
auto store = new CertificateStore();
auto caCert = new Certificate(cast(char[])File("cacert.pem").read);
store.add(caCert);
auto untrustedCert = new Certificate(cast(char[])File("cert.pem").read);
if (untrustedCert.verify(store))
    Stdout("The untrusted cert was signed by our caCert and is valid.").newline;
else
    Stdout("The untrusted cert was expired, or not signed by the caCert").newline;
CertificateStore add(Certificate cert)
Add a Certificate to the store.
class PublicKey
PublicKey contains the RSA public key from a private/public keypair.
It also allows extraction of the public key from a keypair.

This is useful for encryption, you can encrypt data with someone's public key and they can decrypt it with their private key.

Example
1
2
3
auto public = new PublicKey(cast(char[])File("public.pem").read);
auto encrypted = public.encrypt(cast(ubyte[])"Hello, how are you today?");
auto pemData = public.pemFormat;
this(char[] publicPemData)
Generate a PublicKey object from the passed PEM formatted data

Parameters:

publicPemDatapem encoded data containing the public key
char[] pemFormat()
Return a PublicKey in the PEM format.
bool verify(ubyte[] data, ubyte[] signature)
Verify the data passed was signed with the public key.

Parameters:

datathe data to verify
signaturethe digital signature
ubyte[] encrypt(ubyte[] data)
Encrypt the passed data using the PublicKey

Notes:

This is size limited based off the key Not recommended for general encryption, use RSA for encrypting a random key instead and switch to a block cipher.

Parameters:

datathe data to encrypt
ubyte[] decrypt(ubyte[] data)
Decrypts data previously encrypted with the matching PrivateKey
Please see the encrypt notes.

Parameters:

datathe data to encrypt
class PrivateKey
Generates a RSA public/private key pair for use with X509 Certificates and other applications search as S/MIME, DomainKeys, etc.
Example
1
2
3
4
5
auto newPkey = new PrivateKey(2048); // create new keypair
Stdout(newPkey.pemFormat("password")); // dumps in pemFormat with encryption
Stdout(newPkey.pemFormat()); // dumps in pemFormat without encryption
Stdout(newPkey.publicKey.pemFormat); // dump out just the public key portion
auto data = newPkey.decrypt(someData); // decrypt data encrypted with public Key
this(char[] privatePemData, char[] certPass = null)
Reads in the provided PEM data, with an optional password to decrypt the private key.

Parameters:

privatePemDatathe PEM encoded data of the private key
certPassan optional password to decrypt the key.
this(int bits)
Generates a new private/public key at the specified bit leve.

Parameters:

bitsNumber of bits to use, 2048 is a good number for this.
bool opEquals(Object obj) [override]
Compares two PrivateKey classes to see if the internal structures are the same.
char[] pemFormat(char[] pass = null)
Returns the underlying public/private key pair in PEM format.

Parameters:

passIf this is provided, the private key will be encrypted using AES 256bit encryption, with this as the key.
PublicKey publicKey()
Returns the underlying PublicKey
ubyte[] sign(const(ubyte)[] data, ubyte[] sigbuf)
Sign the given data with the private key

Parameters:

datathe data to sign
sigbufthe buffer to store the signature in Returns a slice of the signature or null
ubyte[] encrypt(ubyte[] data)
Encrypt the passed data using the PrivateKey

Notes:

This is size limited based off the key Not recommended for general encryption, use RSA for encrypting a random key instead and switch to a block cipher.

Parameters:

datathe data to encrypt
ubyte[] decrypt(ubyte[] data)
Decrypts data previously encrypted with the matching PublicKey
Please see the encrypt notes.

Parmas:

data = the data to encrypt
class Certificate
Certificate provides necessary functionality to create and read X509 Certificates.
Note, once a Certificate has been signed, it is immutable, and cannot be modified.

X509 Certificates are sometimes called SSL Certificates.

Example
1
2
3
4
5
6
7
8
9
10
auto newPkey = new PrivateKey(2048); // create new keypair
auto cert = new Certificate();
cert.privateKey = newPkey;
cert.serialNumber = 1;
cert.dateBeforeOffset = TimeSpan.zero;
cert.dateAfterOffset = TimeSpan.days(365); // cert is valid for one year
cert.setSubject("US", "State", "City", "Organization", "CN", "Organizational Unit", "Email");
cert.sign(cert, newPkey); // self signed cert
Stdout(newPkey.pemFormat).newline;
Stdout(cert.pemFormat).newline;
this(char[] publicPemData)
Parses a X509 Certificate from the provided PEM encoded data.
this()
Creates a new and un-signed (empty) X509 certificate. Useful for generating X509 certificates programatically.
Certificate serialNumber(uint serial)
Sets the serial number of the new unsigned certificate.
Note, this serial number should be unique for all certificates signed by the provided certificate authority. Having two Certificates with the same serial number can cause problems with web browsers and other apps because they will be different certificates.
uint serialNumber()
Returns the serial number of the Certificate
Certificate dateBeforeOffset(TimeSpan t)
If the current date is "before" the date set here, the certificate will be invalid.

Parameters:

tA TimeSpan representing the earliest time the Certificate will be valid

Example:

cert.dateBeforeOffset = TimeSpan.seconds(-86400); // Certificate is invalid before yesterday
Certificate dateAfterOffset(TimeSpan t)
If the current date is "after" the date set here, the certificate will be invalid.

Parameters:

tA TimeSpan representing the amount of time from now that the Certificate will be valid. This must be larger than dateBefore

Example:

cert.dateAfterOffset = TimeSpan.seconds(86400 * 365); // Certificate is valid up to one year from now
char[] dateAfter()
Returns the dateAfter field of the certificate in ASN1_GENERALIZEDTIME.
Note, this will eventually befome a DateTime struct.
char[] dateBefore()
Returns the dateBefore field of the certificate in ASN1_GENERALIZEDTIME.
Note, this will eventually befome a DateTime struct.
Certificate privateKey(PrivateKey key)
Sets the public/private keypair of an unsigned certificate.
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)
Sets the subject (who this certificate is for) of an unsigned certificate.
The country code must be a valid two-letter country code (ie: CA, US, etc)

Parameters:

countrythe two letter country code of the subject
stateProvincethe state or province of the subject
citythe city the subject belong to
organizationthe organization the subject belongs to
cnthe cn of the subject. For websites, this should be the website url or a wildcard version of it (ie: *.dsource.org)
organizationUnitthe optional orgnizationalUnit of the subject
emailthe optional email address of the subject
char[] subject()
Returns the Certificate subject in a multi-line string.
Certificate sign(Certificate caCert, PrivateKey caKey)
Signs the unsigned Certificate with the specified CA X509 Certificate and it's corresponding public/private keypair.
Once the Certificate is signed, it can no longer be modified.
bool opEquals(Object obj) [override]
Checks if the underlying data structur of the Certificate is equal
bool verify(CertificateStore store)
Verifies that the Certificate was signed and issues by a CACert in the passed CertificateStore.
This will also verify the dateBefore and dateAfter fields to see if the current date falls between them.
char[] pemFormat()
Returns the Certificate in a PEM encoded string.