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.