123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 |
- {
- Simple low-level example using the GnuTLS binding for how generate an own CA
- and self-signed certificate for HTTP server and client.
- Author: Silvio Clecio (silvioprog)
- Date: Wed Jan 9 03:10:58 BRT 2019
- GnuTLS version: 3.4+
- Testing the generated files.
- Server side:
- gnutls-serv --port 8080 --http \
- --x509cafile ca.pem \
- --x509keyfile server.key \
- --x509certfile server.pem
- Client side:
- curl -k --key client.key --cert client.pem https://localhost:8080
- or:
- gnutls-cli localhost --port 8080 --insecure \
- --x509keyfile client.key \
- --x509certfile client.pem
- }
- program srvcacert;
- {$MODE OBJFPC}{$H+}
- {$ASSERTIONS ON}
- uses
- sysutils,
- classes,
- dateutils,
- ctypes,
- gnutls;
- const
- CA_KEY = 'ca.key';
- CA_PEM = 'ca.pem';
- SERVER_KEY = 'server.key';
- SERVER_PEM = 'server.pem';
- CLIENT_KEY = 'client.key';
- CLIENT_PEM = 'client.pem';
- CERT_SIZE = 4096;
- type
- EGnuTLS = Exception;
- procedure Save(const S: AnsiString; const AFileName: TFileName);
- begin
- with TStringStream.Create(S) do
- try
- SaveToFile(AFileName);
- finally
- Free;
- end;
- end;
- procedure TLSCheckRet(Aret: cint); inline;
- begin
- if Aret <> GNUTLS_E_SUCCESS then
- raise EGnuTLS.Create(gnutls_strerror(Aret));
- end;
- procedure TLSCheck(Aexp: Boolean; Aret: cint); inline;
- begin
- if Aexp then
- raise EGnuTLS.Create(gnutls_strerror(Aret));
- end;
- procedure TLSGenPrivKey(out Apriv_key: AnsiString);
- var
- Vkey: Tgnutls_x509_privkey_t;
- Vpriv_key_size: cuint;
- begin
- try
- TLSCheckRet(gnutls_x509_privkey_init(@Vkey));
- Vpriv_key_size := gnutls_sec_param_to_pk_bits(GNUTLS_PK_RSA,
- GNUTLS_SEC_PARAM_HIGH);
- Apriv_key := '';
- SetLength(Apriv_key, Pred(Vpriv_key_size));
- TLSCheckRet(gnutls_x509_privkey_generate(Vkey, GNUTLS_PK_RSA,
- Vpriv_key_size, 0));
- TLSCheckRet(gnutls_x509_privkey_export(Vkey, GNUTLS_X509_FMT_PEM,
- @Apriv_key[1], @Vpriv_key_size));
- SetLength(Apriv_key, Pred(Vpriv_key_size));
- except
- gnutls_x509_privkey_deinit(Vkey);
- raise;
- end;
- end;
- procedure TLSGenCACert(const Aca_priv_key: AnsiString; out Aca_pem: AnsiString;
- const Acommon_name, Aserial: AnsiString; Adays: Word);
- var
- Vkey: Tgnutls_x509_privkey_t;
- Vcrt: Tgnutls_x509_crt_t = nil;
- Vdata: Tgnutls_datum_t;
- Vkeyid: AnsiString = '';
- Vkeyidsize: csize_t;
- Vactivation: ttime_t;
- Vca_pem_size: csize_t;
- Vret: cint;
- begin
- try
- TLSCheckRet(gnutls_x509_privkey_init(@Vkey));
- Vdata.data := @Aca_priv_key[1];
- Vdata.size := Length(Aca_priv_key);
- TLSCheckRet(gnutls_x509_privkey_import(Vkey, @Vdata, GNUTLS_X509_FMT_PEM));
- TLSCheckRet(gnutls_x509_crt_init(@Vcrt));
- TLSCheckRet(gnutls_x509_crt_set_key(Vcrt, Vkey));
- TLSCheckRet(gnutls_x509_crt_set_dn_by_oid(Vcrt, GNUTLS_OID_X520_COMMON_NAME,
- 0, @Acommon_name[1], Length(Acommon_name)));
- TLSCheckRet(gnutls_x509_crt_set_version(Vcrt, 3));
- TLSCheckRet(gnutls_x509_crt_set_serial(Vcrt, @Aserial[1], Length(Aserial)));
- Vactivation := DateTimeToUnix(Now,False);
- TLSCheckRet(gnutls_x509_crt_set_activation_time(Vcrt, Vactivation));
- TLSCheckRet(gnutls_x509_crt_set_expiration_time(Vcrt,
- Vactivation + (Adays * 86400)));
- TLSCheckRet(gnutls_x509_crt_set_ca_status(Vcrt, Ord(True)));
- TLSCheckRet(gnutls_x509_crt_set_key_usage(Vcrt, GNUTLS_KEY_KEY_CERT_SIGN));
- Vkeyidsize := 0;
- Vret := gnutls_x509_crt_get_key_id(Vcrt, GNUTLS_KEYID_USE_SHA1, nil,
- @Vkeyidsize);
- TLSCheck((Vret <> GNUTLS_E_SHORT_MEMORY_BUFFER) or (Vkeyidsize < 1), Vret);
- SetLength(Vkeyid, Pred(Vkeyidsize));
- TLSCheckRet(gnutls_x509_crt_get_key_id(Vcrt, GNUTLS_KEYID_USE_SHA1,
- @Vkeyid[1], @Vkeyidsize));
- TLSCheckRet(gnutls_x509_crt_set_subject_key_id(Vcrt, @Vkeyid[1], Vkeyidsize));
- TLSCheckRet(gnutls_x509_crt_sign2(Vcrt, Vcrt, Vkey, GNUTLS_DIG_SHA256, 0));
- Aca_pem := '';
- Vca_pem_size := CERT_SIZE;
- SetLength(Aca_pem, Pred(Vca_pem_size));
- TLSCheckRet(gnutls_x509_crt_export(Vcrt, GNUTLS_X509_FMT_PEM, @Aca_pem[1],
- @Vca_pem_size));
- SetLength(Aca_pem, Pred(Vca_pem_size));
- except
- gnutls_x509_privkey_deinit(Vkey);
- gnutls_x509_crt_deinit(Vcrt);
- raise;
- end;
- end;
- procedure TLSGenSrvCert(const Aca_priv_key, Aca_pem, Asrv_priv_key: AnsiString;
- out Asrv_pem: AnsiString; const Acommon_name, Aorganization,
- Aserial: AnsiString; Adays: Word);
- var
- Vsrv_key: Tgnutls_x509_privkey_t = nil;
- Vca_key: Tgnutls_x509_privkey_t = nil;
- Vca_crt: Tgnutls_x509_crt_t = nil;
- Vsrv_crt: Tgnutls_x509_crt_t = nil;
- Vdata: Tgnutls_datum_t;
- Vkeyid: AnsiString = '';
- Vkeyidsize: csize_t;
- Vactivation: ttime_t;
- Vsrv_pem_size: csize_t;
- Vret: cint;
- begin
- try
- TLSCheckRet(gnutls_x509_privkey_init(@Vca_key));
- Vdata.data := @Aca_priv_key[1];
- Vdata.size := Length(Aca_priv_key);
- TLSCheckRet(gnutls_x509_privkey_import(Vca_key, @Vdata, GNUTLS_X509_FMT_PEM));
- TLSCheckRet(gnutls_x509_privkey_init(@Vsrv_key));
- Vdata.data := @Asrv_priv_key[1];
- Vdata.size := Length(Asrv_priv_key);
- TLSCheckRet(gnutls_x509_privkey_import(Vsrv_key, @Vdata, GNUTLS_X509_FMT_PEM));
- TLSCheckRet(gnutls_x509_crt_init(@Vca_crt));
- Vdata.data := @Aca_pem[1];
- Vdata.size := Length(Aca_pem);
- TLSCheckRet(gnutls_x509_crt_import(Vca_crt, @Vdata, GNUTLS_X509_FMT_PEM));
- TLSCheckRet(gnutls_x509_crt_init(@Vsrv_crt));
- TLSCheckRet(gnutls_x509_crt_set_key(Vsrv_crt, Vsrv_key));
- TLSCheckRet(gnutls_x509_crt_set_dn_by_oid(Vsrv_crt,
- GNUTLS_OID_X520_COMMON_NAME, 0, @Acommon_name[1], Length(Acommon_name)));
- TLSCheckRet(gnutls_x509_crt_set_dn_by_oid(Vsrv_crt,
- GNUTLS_OID_X520_ORGANIZATION_NAME, 0, @Aorganization[1],
- Length(Aorganization)));
- TLSCheckRet(gnutls_x509_crt_set_version(Vsrv_crt, 3));
- TLSCheckRet(gnutls_x509_crt_set_serial(Vsrv_crt, @Aserial[1],
- Length(Aserial)));
- Vactivation := DateTimeToUnix(Now,False);
- TLSCheckRet(gnutls_x509_crt_set_activation_time(Vsrv_crt, Vactivation));
- TLSCheckRet(gnutls_x509_crt_set_expiration_time(Vsrv_crt,
- Vactivation + (Adays * 86400)));
- TLSCheckRet(gnutls_x509_crt_set_ca_status(Vsrv_crt, Ord(False)));
- TLSCheckRet(gnutls_x509_crt_set_key_purpose_oid(Vsrv_crt,
- @GNUTLS_KP_TLS_WWW_SERVER[1], Ord(False)));
- Vkeyidsize := 0;
- Vret := gnutls_x509_crt_get_subject_key_id(Vca_crt, nil, @Vkeyidsize, nil);
- TLSCheck((Vret <> GNUTLS_E_SHORT_MEMORY_BUFFER) or (Vkeyidsize < 1), Vret);
- SetLength(Vkeyid, Pred(Vkeyidsize));
- TLSCheckRet(gnutls_x509_crt_get_subject_key_id(Vca_crt, @Vkeyid[1],
- @Vkeyidsize, nil));
- TLSCheckRet(gnutls_x509_crt_set_subject_key_id(Vsrv_crt, @Vkeyid[1],
- Vkeyidsize));
- Vkeyidsize := 0;
- gnutls_x509_crt_get_key_id(Vsrv_crt, GNUTLS_KEYID_USE_SHA1, nil, @Vkeyidsize);
- TLSCheck((Vret <> GNUTLS_E_SHORT_MEMORY_BUFFER) or (Vkeyidsize < 1), Vret);
- SetLength(Vkeyid, Pred(Vkeyidsize));
- TLSCheckRet(gnutls_x509_crt_get_key_id(Vsrv_crt, GNUTLS_KEYID_USE_SHA1,
- @Vkeyid[1], @Vkeyidsize));
- TLSCheckRet(gnutls_x509_crt_set_authority_key_id(Vsrv_crt,
- @Vkeyid[1], Vkeyidsize));
- TLSCheckRet(gnutls_x509_crt_sign2(Vsrv_crt, Vca_crt, Vca_key,
- GNUTLS_DIG_SHA256, 0));
- Vsrv_pem_size := CERT_SIZE;
- Asrv_pem := '';
- SetLength(Asrv_pem, Pred(Vsrv_pem_size));
- TLSCheckRet(gnutls_x509_crt_export(Vsrv_crt, GNUTLS_X509_FMT_PEM,
- @Asrv_pem[1], @Vsrv_pem_size));
- SetLength(Asrv_pem, Vsrv_pem_size);
- except
- gnutls_x509_privkey_deinit(Vsrv_key);
- gnutls_x509_privkey_deinit(Vca_key);
- gnutls_x509_crt_deinit(Vca_crt);
- gnutls_x509_crt_deinit(Vsrv_crt);
- raise;
- end;
- end;
- procedure TLSGenCliCert(const Aca_priv_key, Aca_pem, Acli_priv_key: AnsiString;
- out Acli_pem: AnsiString; const Acommon_name, Aserial: AnsiString;
- Adays: Word);
- var
- Vcli_key: Tgnutls_x509_privkey_t = nil;
- Vca_key: Tgnutls_x509_privkey_t = nil;
- Vca_crt: Tgnutls_x509_crt_t = nil;
- Vcli_crt: Tgnutls_x509_crt_t = nil;
- Vdata: Tgnutls_datum_t;
- Vkeyid: AnsiString = '';
- Vkeyidsize: csize_t;
- Vactivation: ttime_t;
- Vcli_pem_size: csize_t;
- Vret: cint;
- begin
- try
- TLSCheckRet(gnutls_x509_privkey_init(@Vca_key));
- Vdata.data := @Aca_priv_key[1];
- Vdata.size := Length(Aca_priv_key);
- TLSCheckRet(gnutls_x509_privkey_import(Vca_key, @Vdata,
- GNUTLS_X509_FMT_PEM));
- TLSCheckRet(gnutls_x509_privkey_init(@Vcli_key));
- Vdata.data := @Acli_priv_key[1];
- Vdata.size := Length(Acli_priv_key);
- TLSCheckRet(gnutls_x509_privkey_import(Vcli_key, @Vdata,
- GNUTLS_X509_FMT_PEM));
- TLSCheckRet(gnutls_x509_crt_init(@Vca_crt));
- Vdata.data := @Aca_pem[1];
- Vdata.size := Length(Aca_pem);
- TLSCheckRet(gnutls_x509_crt_import(Vca_crt, @Vdata, GNUTLS_X509_FMT_PEM));
- TLSCheckRet(gnutls_x509_crt_init(@Vcli_crt));
- TLSCheckRet(gnutls_x509_crt_set_key(Vcli_crt, Vcli_key));
- TLSCheckRet(gnutls_x509_crt_set_dn_by_oid(Vcli_crt,
- GNUTLS_OID_X520_COMMON_NAME, 0, @Acommon_name[1], Length(Acommon_name)));
- TLSCheckRet(gnutls_x509_crt_set_version(Vcli_crt, 3));
- TLSCheckRet(gnutls_x509_crt_set_serial(Vcli_crt, @Aserial[1],
- Length(Aserial)));
- Vactivation := DateTimeToUnix(Now,False);
- TLSCheckRet(gnutls_x509_crt_set_activation_time(Vcli_crt, Vactivation));
- TLSCheckRet(gnutls_x509_crt_set_expiration_time(Vcli_crt,
- Vactivation + (Adays * 86400)));
- TLSCheckRet(gnutls_x509_crt_set_ca_status(Vcli_crt, Ord(False)));
- TLSCheckRet(gnutls_x509_crt_set_key_purpose_oid(Vcli_crt,
- @GNUTLS_KP_TLS_WWW_CLIENT[1], Ord(False)));
- Vkeyidsize := 0;
- Vret := gnutls_x509_crt_get_subject_key_id(Vca_crt, nil, @Vkeyidsize, nil);
- TLSCheck((Vret <> GNUTLS_E_SHORT_MEMORY_BUFFER) or (Vkeyidsize < 1), Vret);
- SetLength(Vkeyid, Pred(Vkeyidsize));
- TLSCheckRet(gnutls_x509_crt_get_subject_key_id(Vca_crt, @Vkeyid[1],
- @Vkeyidsize, nil));
- TLSCheckRet(gnutls_x509_crt_set_subject_key_id(Vcli_crt, @Vkeyid[1],
- Vkeyidsize));
- Vkeyidsize := 0;
- Vret := gnutls_x509_crt_get_key_id(Vca_crt, GNUTLS_KEYID_USE_SHA1,
- nil, @Vkeyidsize);
- TLSCheck((Vret <> GNUTLS_E_SHORT_MEMORY_BUFFER) or (Vkeyidsize < 1), Vret);
- SetLength(Vkeyid, Vkeyidsize);
- TLSCheckRet(gnutls_x509_crt_get_key_id(Vca_crt, GNUTLS_KEYID_USE_SHA1,
- @Vkeyid[1], @Vkeyidsize));
- TLSCheckRet(gnutls_x509_crt_set_authority_key_id(Vcli_crt, @Vkeyid[1],
- Vkeyidsize));
- TLSCheckRet(gnutls_x509_crt_sign2(Vcli_crt, Vca_crt, Vca_key,
- GNUTLS_DIG_SHA256, 0));
- Vcli_pem_size := CERT_SIZE;
- Acli_pem := '';
- SetLength(Acli_pem, Pred(Vcli_pem_size));
- TLSCheckRet(gnutls_x509_crt_export(Vcli_crt, GNUTLS_X509_FMT_PEM,
- @Acli_pem[1], @Vcli_pem_size));
- SetLength(Acli_pem, Vcli_pem_size);
- except
- gnutls_x509_privkey_deinit(Vcli_key);
- gnutls_x509_privkey_deinit(Vca_key);
- gnutls_x509_crt_deinit(Vca_crt);
- gnutls_x509_crt_deinit(Vcli_crt);
- raise;
- end;
- end;
- var
- ca_pkey, ca_crt, pkey, crt: AnsiString;
- begin
- LoadGnuTLS;
- Assert(GnuTLSLoaded);
- try
- WriteLn('Generating ', CA_KEY);
- TLSGenPrivKey(ca_pkey);
- Save(ca_pkey, CA_KEY);
- WriteLn('Done!');
- WriteLn('Generating ', CA_PEM);
- TLSGenCACert(ca_pkey, ca_crt, 'GnuTLS test CA', '01', 365);
- Save(ca_crt, CA_PEM);
- WriteLn('Done!');
- WriteLn('Generating ', SERVER_KEY);
- TLSGenPrivKey(pkey);
- Save(pkey, SERVER_KEY);
- WriteLn('Done!');
- WriteLn('Generating ', SERVER_PEM);
- TLSGenSrvCert(ca_pkey, ca_crt, pkey, crt, 'test.gnutls.org',
- 'GnuTLS test server', '01', 365);
- Save(crt, SERVER_PEM);
- WriteLn('Done!');
- WriteLn('Generating ', CLIENT_KEY);
- TLSGenPrivKey(pkey);
- Save(pkey, CLIENT_KEY);
- WriteLn('Done!');
- WriteLn('Generating ', CLIENT_PEM);
- TLSGenCliCert(ca_pkey, ca_crt, pkey, crt, 'GnuTLS test client', '01', 365);
- Save(crt, CLIENT_PEM);
- WriteLn('Done!');
- finally
- FreeGnuTLS;
- end;
- end.
|