www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - openssl example for D

reply "lzzll" <ownrepos gmail.com> writes:
Prior work:
1. Download OpenSSL D interface from 
https://github.com/D-Programming-Deimos/openssl and extract
2. install openssl-static (or other package inclued 
/usr/lib/libssl.a and /usr/lib/libcrypto.a)
3. before compile, put your source file into 
D-Programming-Deimos-openssl-xxx
or use -I/xxx/D-Programming-Deimos-openssl-xxx with compiler.

Example code:

//Server
import std.stdio;
import std.socket;
import std.algorithm;
import core.thread;
import deimos.openssl.ssl;
import deimos.openssl.err;
static import std.c.stdio;

const ListenAddress = "127.0.0.1";
const ListenPort = 10443;
const RecvSize = 1024;
const CertFile = "server_cert.pem";
const KeyFile = "server_key.pem";

class SSLConnection : Thread{
	Socket r;
	char[RecvSize] buf;
	uint len;
	SSL_CTX *ctx;
	SSL *ssl;
	
	this(Socket r, SSL_CTX *ctx){
		this.r = r;
		this.ctx = ctx;
		this.isDaemon(true);
		super(&run);
	}
	
	void run(){
		writeln("new connection from ", r.remoteAddress().toString());
		
		ssl = SSL_new(ctx);
		SSL_set_fd(ssl, r.handle());
		sslAssert(SSL_accept(ssl) != -1);
		
		while (r.isAlive()){
			len = SSL_read(ssl, cast(void*) buf, RecvSize);
			if (len <= 0){
				break;
			}
			writeln("ssl read ", len, " bytes: ", buf[0 .. len]);
			SSL_write(ssl, cast(const void*) buf[0 .. len], len);
		}
		
		if (r.isAlive()){
			writeln("close connection from ", 
r.remoteAddress().toString());
		}
		SSL_free(ssl);
		r.close();
	}
}

void sslAssert(bool ret){
	if (!ret){
		ERR_print_errors_fp(std.c.stdio.stderr);
		throw new Exception("SSL_ERROR");
	}
}

void initSSL(){
	SSL_library_init();
	OpenSSL_add_all_algorithms();
	SSL_load_error_strings();
}

SSL_CTX *getCTX(string certfile, string keyfile){
	SSL_CTX *ctx = SSL_CTX_new(SSLv3_server_method());
	sslAssert(!(ctx is null));
	sslAssert(SSL_CTX_use_certificate_file(ctx, cast(const char*) 
certfile, SSL_FILETYPE_PEM) > 0);
	sslAssert(SSL_CTX_use_PrivateKey_file(ctx, cast(const char*) 
keyfile, SSL_FILETYPE_PEM) > 0);
	sslAssert(SSL_CTX_check_private_key(ctx) > 0);
	return ctx;
}

int main(string[] args){
	initSSL();
	SSL_CTX *ctx = getCTX(CertFile, KeyFile);
	
	Socket s = new TcpSocket();
	s.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, 
true);
	s.bind(new InternetAddress(ListenAddress, ListenPort));
	s.listen(10);
	
	writef("listen %s:%d\n", ListenAddress, ListenPort);
	while (s.isAlive()){
		Thread ss = new SSLConnection(s.accept(), ctx);
		ss.start();
	}
	
	s.close();
	SSL_CTX_free(ctx);
	return 0;
}





//Client
import std.stdio;
import std.socket;
import std.algorithm;
import std.string;
import core.thread;
import core.memory;
import deimos.openssl.ssl;
import deimos.openssl.err;
import deimos.openssl.sha;
static import std.c.stdio;

const ConnectAddress = "127.0.0.1";
const ConnectPort = 10443;
const BufSize = 1024;
const msg = "test message";

void sslAssert(bool ret){
	if (!ret){
		ERR_print_errors_fp(std.c.stdio.stderr);
		throw new Exception("SSL_ERROR");
	}
}

string getSha256(string input){
	char[SHA256_DIGEST_LENGTH] hash;
	string hash_hex;
	SHA256_CTX *sha256 = new SHA256_CTX;
	SHA256_Init(sha256);
	SHA256_Update(sha256, cast(const void*) input, input.length);
	SHA256_Final(cast(ubyte*) hash, sha256);
	for(int i=0; i<SHA256_DIGEST_LENGTH; i++){
		hash_hex ~= format("%02x", hash[i]);
	}
	return hash_hex;
}

string getCertInfo(X509 *cert){
	char buf[BufSize];
	uint len;
	string data;
	BIO* strio = BIO_new(BIO_s_mem());
	X509_print(strio, cert);
	//BIO_ctrl(strio, BIO_C_FILE_SEEK, 0, null);
	while (true){
		len = BIO_gets(strio, cast(char*) buf, BufSize);
		if (len <= 0){
			break;
		}
		data ~= buf[0 .. len];
	}
	BIO_free(strio);
	return data;
}

bool verifyCert(X509 *cert){
	X509_print_fp(std.c.stdio.stdout, cert);
	
	/* PEM_read... are broken
	//std.c.stdio.FILE *fp = std.c.stdio.fopen(cast(char*) CertFile, 
"rb");
	BIO* lcertio = BIO_new_file(cast(char*) "server_cert.pem", "rb");
	X509* lcert = PEM_read_bio_X509(lcertio, null, null, null);
	writeln(X509_verify(lcert, X509_get_pubkey(cert)));
	X509_free(lcert);
	BIO_free(lcertio);*/
	
	string cert_hash = getSha256(getCertInfo(cert));
	writeln("sha256: ", cert_hash);
	
	return true; //or return (cert_hash == some_const_value) to 
verify cert
}

int main(string[] args){
	char buf[BufSize];
	uint len;
	
	SSL_library_init();
	OpenSSL_add_all_algorithms();
	SSL_load_error_strings();
	
	SSL_CTX *ctx = SSL_CTX_new(SSLv3_client_method());
	sslAssert(!(ctx is null));
	
	Socket r = new TcpSocket();
	r.connect(new InternetAddress(ConnectAddress, ConnectPort));
	
	SSL *ssl = SSL_new(ctx);
	SSL_set_fd(ssl, r.handle());
	sslAssert(SSL_connect(ssl) != -1);
	
	if (!verifyCert(SSL_get_peer_certificate(ssl))){
		throw new Exception("verify cert failed");
	}
	
	SSL_write(ssl, cast(const char*) msg, msg.length);
	len = SSL_read(ssl, cast(void*) buf, BufSize);
	writef("get reply %d bytes: %s\n", len, buf[0 .. len]);
	
	SSL_free(ssl);
	r.close();
	
	SSL_CTX_free(ctx);
	return 0;
}



Build:
You need link ssl and crypto, in dmd, there are -L-lssl -L-lcrypto
ex:
dmd server.d -L-lssl -L-lcrypto 
-I/xxx/D-Programming-Deimos-openssl-xxx

Problem:
1. These example could not compile with rdmd on rhel6.
will error at ld like undefined reference to xxxx , xxyy, xxzz ...
2. PEM_read... are broken (ld error), the example use sha256 to 
verify certificate.
Apr 07 2012
parent reply "David Nadlinger" <see klickverbot.at> writes:
On Saturday, 7 April 2012 at 17:55:29 UTC, lzzll wrote:
 2. PEM_read... are broken (ld error), the example use sha256 to 
 verify certificate.
What exactly are the error messages you get? David
Apr 07 2012
parent "lzzll" <ownrepos gmail.com> writes:
 What exactly are the error messages you get?
dmd test_client.d -w -L-lssl -L-lcrypto -L-ldl && ./test_client test_client.o: In function `_D11test_client10verifyCertFPS6deimos7openssl4x5097x509_stZb': test_client.d:(.text._D11test_client10verifyCertFPS6deimos7openssl4x 097x509_stZb+0x2e): undefined reference to `_D6deimos7openssl3pem65__T14DECLARE_PEM_rwVAyaa4_58353039TS6deimos7openssl4x5097x509_stZ67__T16DECLARE_PEM_readVAyaa4_58353039TS6deimos7openssl4x5097x509_stZ71__T20DECLARE_PEM_read_bioVAyaa4_58353039TS6deimos7openssl4x5097x509_stZ17PEM_read_bio_X509UNbPS6deimos7openssl3bio6bio_stPPS6deimos7openssl4x5097x509_stPUPaiiPvZiPvZPS6deimos7openssl4x5097x509_st' collect2: ld returned 1 exit status --- errorlevel 1 And sorry for a memory leak problem, add X509_free(cert); before return true; //or return (cert_hash == some_const_value) to verify cert can fix.
Apr 07 2012