srvcacert.pp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. {
  2. Simple low-level example using the GnuTLS binding for how generate an own CA
  3. and self-signed certificate for HTTP server and client.
  4. Author: Silvio Clecio (silvioprog)
  5. Date: Wed Jan 9 03:10:58 BRT 2019
  6. GnuTLS version: 3.4+
  7. Testing the generated files.
  8. Server side:
  9. gnutls-serv --port 8080 --http \
  10. --x509cafile ca.pem \
  11. --x509keyfile server.key \
  12. --x509certfile server.pem
  13. Client side:
  14. curl -k --key client.key --cert client.pem https://localhost:8080
  15. or:
  16. gnutls-cli localhost --port 8080 --insecure \
  17. --x509keyfile client.key \
  18. --x509certfile client.pem
  19. }
  20. program srvcacert;
  21. {$MODE OBJFPC}{$H+}
  22. {$ASSERTIONS ON}
  23. uses
  24. sysutils,
  25. classes,
  26. dateutils,
  27. ctypes,
  28. gnutls;
  29. const
  30. CA_KEY = 'ca.key';
  31. CA_PEM = 'ca.pem';
  32. SERVER_KEY = 'server.key';
  33. SERVER_PEM = 'server.pem';
  34. CLIENT_KEY = 'client.key';
  35. CLIENT_PEM = 'client.pem';
  36. CERT_SIZE = 4096;
  37. type
  38. EGnuTLS = Exception;
  39. procedure Save(const S: AnsiString; const AFileName: TFileName);
  40. begin
  41. with TStringStream.Create(S) do
  42. try
  43. SaveToFile(AFileName);
  44. finally
  45. Free;
  46. end;
  47. end;
  48. procedure TLSCheckRet(Aret: cint); inline;
  49. begin
  50. if Aret <> GNUTLS_E_SUCCESS then
  51. raise EGnuTLS.Create(gnutls_strerror(Aret));
  52. end;
  53. procedure TLSCheck(Aexp: Boolean; Aret: cint); inline;
  54. begin
  55. if Aexp then
  56. raise EGnuTLS.Create(gnutls_strerror(Aret));
  57. end;
  58. procedure TLSGenPrivKey(out Apriv_key: AnsiString);
  59. var
  60. Vkey: Tgnutls_x509_privkey_t;
  61. Vpriv_key_size: cuint;
  62. begin
  63. try
  64. TLSCheckRet(gnutls_x509_privkey_init(@Vkey));
  65. Vpriv_key_size := gnutls_sec_param_to_pk_bits(GNUTLS_PK_RSA,
  66. GNUTLS_SEC_PARAM_HIGH);
  67. Apriv_key := '';
  68. SetLength(Apriv_key, Pred(Vpriv_key_size));
  69. TLSCheckRet(gnutls_x509_privkey_generate(Vkey, GNUTLS_PK_RSA,
  70. Vpriv_key_size, 0));
  71. TLSCheckRet(gnutls_x509_privkey_export(Vkey, GNUTLS_X509_FMT_PEM,
  72. @Apriv_key[1], @Vpriv_key_size));
  73. SetLength(Apriv_key, Pred(Vpriv_key_size));
  74. except
  75. gnutls_x509_privkey_deinit(Vkey);
  76. raise;
  77. end;
  78. end;
  79. procedure TLSGenCACert(const Aca_priv_key: AnsiString; out Aca_pem: AnsiString;
  80. const Acommon_name, Aserial: AnsiString; Adays: Word);
  81. var
  82. Vkey: Tgnutls_x509_privkey_t;
  83. Vcrt: Tgnutls_x509_crt_t = nil;
  84. Vdata: Tgnutls_datum_t;
  85. Vkeyid: AnsiString = '';
  86. Vkeyidsize: csize_t;
  87. Vactivation: ttime_t;
  88. Vca_pem_size: csize_t;
  89. Vret: cint;
  90. begin
  91. try
  92. TLSCheckRet(gnutls_x509_privkey_init(@Vkey));
  93. Vdata.data := @Aca_priv_key[1];
  94. Vdata.size := Length(Aca_priv_key);
  95. TLSCheckRet(gnutls_x509_privkey_import(Vkey, @Vdata, GNUTLS_X509_FMT_PEM));
  96. TLSCheckRet(gnutls_x509_crt_init(@Vcrt));
  97. TLSCheckRet(gnutls_x509_crt_set_key(Vcrt, Vkey));
  98. TLSCheckRet(gnutls_x509_crt_set_dn_by_oid(Vcrt, GNUTLS_OID_X520_COMMON_NAME,
  99. 0, @Acommon_name[1], Length(Acommon_name)));
  100. TLSCheckRet(gnutls_x509_crt_set_version(Vcrt, 3));
  101. TLSCheckRet(gnutls_x509_crt_set_serial(Vcrt, @Aserial[1], Length(Aserial)));
  102. Vactivation := DateTimeToUnix(Now,False);
  103. TLSCheckRet(gnutls_x509_crt_set_activation_time(Vcrt, Vactivation));
  104. TLSCheckRet(gnutls_x509_crt_set_expiration_time(Vcrt,
  105. Vactivation + (Adays * 86400)));
  106. TLSCheckRet(gnutls_x509_crt_set_ca_status(Vcrt, Ord(True)));
  107. TLSCheckRet(gnutls_x509_crt_set_key_usage(Vcrt, GNUTLS_KEY_KEY_CERT_SIGN));
  108. Vkeyidsize := 0;
  109. Vret := gnutls_x509_crt_get_key_id(Vcrt, GNUTLS_KEYID_USE_SHA1, nil,
  110. @Vkeyidsize);
  111. TLSCheck((Vret <> GNUTLS_E_SHORT_MEMORY_BUFFER) or (Vkeyidsize < 1), Vret);
  112. SetLength(Vkeyid, Pred(Vkeyidsize));
  113. TLSCheckRet(gnutls_x509_crt_get_key_id(Vcrt, GNUTLS_KEYID_USE_SHA1,
  114. @Vkeyid[1], @Vkeyidsize));
  115. TLSCheckRet(gnutls_x509_crt_set_subject_key_id(Vcrt, @Vkeyid[1], Vkeyidsize));
  116. TLSCheckRet(gnutls_x509_crt_sign2(Vcrt, Vcrt, Vkey, GNUTLS_DIG_SHA256, 0));
  117. Aca_pem := '';
  118. Vca_pem_size := CERT_SIZE;
  119. SetLength(Aca_pem, Pred(Vca_pem_size));
  120. TLSCheckRet(gnutls_x509_crt_export(Vcrt, GNUTLS_X509_FMT_PEM, @Aca_pem[1],
  121. @Vca_pem_size));
  122. SetLength(Aca_pem, Pred(Vca_pem_size));
  123. except
  124. gnutls_x509_privkey_deinit(Vkey);
  125. gnutls_x509_crt_deinit(Vcrt);
  126. raise;
  127. end;
  128. end;
  129. procedure TLSGenSrvCert(const Aca_priv_key, Aca_pem, Asrv_priv_key: AnsiString;
  130. out Asrv_pem: AnsiString; const Acommon_name, Aorganization,
  131. Aserial: AnsiString; Adays: Word);
  132. var
  133. Vsrv_key: Tgnutls_x509_privkey_t = nil;
  134. Vca_key: Tgnutls_x509_privkey_t = nil;
  135. Vca_crt: Tgnutls_x509_crt_t = nil;
  136. Vsrv_crt: Tgnutls_x509_crt_t = nil;
  137. Vdata: Tgnutls_datum_t;
  138. Vkeyid: AnsiString = '';
  139. Vkeyidsize: csize_t;
  140. Vactivation: ttime_t;
  141. Vsrv_pem_size: csize_t;
  142. Vret: cint;
  143. begin
  144. try
  145. TLSCheckRet(gnutls_x509_privkey_init(@Vca_key));
  146. Vdata.data := @Aca_priv_key[1];
  147. Vdata.size := Length(Aca_priv_key);
  148. TLSCheckRet(gnutls_x509_privkey_import(Vca_key, @Vdata, GNUTLS_X509_FMT_PEM));
  149. TLSCheckRet(gnutls_x509_privkey_init(@Vsrv_key));
  150. Vdata.data := @Asrv_priv_key[1];
  151. Vdata.size := Length(Asrv_priv_key);
  152. TLSCheckRet(gnutls_x509_privkey_import(Vsrv_key, @Vdata, GNUTLS_X509_FMT_PEM));
  153. TLSCheckRet(gnutls_x509_crt_init(@Vca_crt));
  154. Vdata.data := @Aca_pem[1];
  155. Vdata.size := Length(Aca_pem);
  156. TLSCheckRet(gnutls_x509_crt_import(Vca_crt, @Vdata, GNUTLS_X509_FMT_PEM));
  157. TLSCheckRet(gnutls_x509_crt_init(@Vsrv_crt));
  158. TLSCheckRet(gnutls_x509_crt_set_key(Vsrv_crt, Vsrv_key));
  159. TLSCheckRet(gnutls_x509_crt_set_dn_by_oid(Vsrv_crt,
  160. GNUTLS_OID_X520_COMMON_NAME, 0, @Acommon_name[1], Length(Acommon_name)));
  161. TLSCheckRet(gnutls_x509_crt_set_dn_by_oid(Vsrv_crt,
  162. GNUTLS_OID_X520_ORGANIZATION_NAME, 0, @Aorganization[1],
  163. Length(Aorganization)));
  164. TLSCheckRet(gnutls_x509_crt_set_version(Vsrv_crt, 3));
  165. TLSCheckRet(gnutls_x509_crt_set_serial(Vsrv_crt, @Aserial[1],
  166. Length(Aserial)));
  167. Vactivation := DateTimeToUnix(Now,False);
  168. TLSCheckRet(gnutls_x509_crt_set_activation_time(Vsrv_crt, Vactivation));
  169. TLSCheckRet(gnutls_x509_crt_set_expiration_time(Vsrv_crt,
  170. Vactivation + (Adays * 86400)));
  171. TLSCheckRet(gnutls_x509_crt_set_ca_status(Vsrv_crt, Ord(False)));
  172. TLSCheckRet(gnutls_x509_crt_set_key_purpose_oid(Vsrv_crt,
  173. @GNUTLS_KP_TLS_WWW_SERVER[1], Ord(False)));
  174. Vkeyidsize := 0;
  175. Vret := gnutls_x509_crt_get_subject_key_id(Vca_crt, nil, @Vkeyidsize, nil);
  176. TLSCheck((Vret <> GNUTLS_E_SHORT_MEMORY_BUFFER) or (Vkeyidsize < 1), Vret);
  177. SetLength(Vkeyid, Pred(Vkeyidsize));
  178. TLSCheckRet(gnutls_x509_crt_get_subject_key_id(Vca_crt, @Vkeyid[1],
  179. @Vkeyidsize, nil));
  180. TLSCheckRet(gnutls_x509_crt_set_subject_key_id(Vsrv_crt, @Vkeyid[1],
  181. Vkeyidsize));
  182. Vkeyidsize := 0;
  183. gnutls_x509_crt_get_key_id(Vsrv_crt, GNUTLS_KEYID_USE_SHA1, nil, @Vkeyidsize);
  184. TLSCheck((Vret <> GNUTLS_E_SHORT_MEMORY_BUFFER) or (Vkeyidsize < 1), Vret);
  185. SetLength(Vkeyid, Pred(Vkeyidsize));
  186. TLSCheckRet(gnutls_x509_crt_get_key_id(Vsrv_crt, GNUTLS_KEYID_USE_SHA1,
  187. @Vkeyid[1], @Vkeyidsize));
  188. TLSCheckRet(gnutls_x509_crt_set_authority_key_id(Vsrv_crt,
  189. @Vkeyid[1], Vkeyidsize));
  190. TLSCheckRet(gnutls_x509_crt_sign2(Vsrv_crt, Vca_crt, Vca_key,
  191. GNUTLS_DIG_SHA256, 0));
  192. Vsrv_pem_size := CERT_SIZE;
  193. Asrv_pem := '';
  194. SetLength(Asrv_pem, Pred(Vsrv_pem_size));
  195. TLSCheckRet(gnutls_x509_crt_export(Vsrv_crt, GNUTLS_X509_FMT_PEM,
  196. @Asrv_pem[1], @Vsrv_pem_size));
  197. SetLength(Asrv_pem, Vsrv_pem_size);
  198. except
  199. gnutls_x509_privkey_deinit(Vsrv_key);
  200. gnutls_x509_privkey_deinit(Vca_key);
  201. gnutls_x509_crt_deinit(Vca_crt);
  202. gnutls_x509_crt_deinit(Vsrv_crt);
  203. raise;
  204. end;
  205. end;
  206. procedure TLSGenCliCert(const Aca_priv_key, Aca_pem, Acli_priv_key: AnsiString;
  207. out Acli_pem: AnsiString; const Acommon_name, Aserial: AnsiString;
  208. Adays: Word);
  209. var
  210. Vcli_key: Tgnutls_x509_privkey_t = nil;
  211. Vca_key: Tgnutls_x509_privkey_t = nil;
  212. Vca_crt: Tgnutls_x509_crt_t = nil;
  213. Vcli_crt: Tgnutls_x509_crt_t = nil;
  214. Vdata: Tgnutls_datum_t;
  215. Vkeyid: AnsiString = '';
  216. Vkeyidsize: csize_t;
  217. Vactivation: ttime_t;
  218. Vcli_pem_size: csize_t;
  219. Vret: cint;
  220. begin
  221. try
  222. TLSCheckRet(gnutls_x509_privkey_init(@Vca_key));
  223. Vdata.data := @Aca_priv_key[1];
  224. Vdata.size := Length(Aca_priv_key);
  225. TLSCheckRet(gnutls_x509_privkey_import(Vca_key, @Vdata,
  226. GNUTLS_X509_FMT_PEM));
  227. TLSCheckRet(gnutls_x509_privkey_init(@Vcli_key));
  228. Vdata.data := @Acli_priv_key[1];
  229. Vdata.size := Length(Acli_priv_key);
  230. TLSCheckRet(gnutls_x509_privkey_import(Vcli_key, @Vdata,
  231. GNUTLS_X509_FMT_PEM));
  232. TLSCheckRet(gnutls_x509_crt_init(@Vca_crt));
  233. Vdata.data := @Aca_pem[1];
  234. Vdata.size := Length(Aca_pem);
  235. TLSCheckRet(gnutls_x509_crt_import(Vca_crt, @Vdata, GNUTLS_X509_FMT_PEM));
  236. TLSCheckRet(gnutls_x509_crt_init(@Vcli_crt));
  237. TLSCheckRet(gnutls_x509_crt_set_key(Vcli_crt, Vcli_key));
  238. TLSCheckRet(gnutls_x509_crt_set_dn_by_oid(Vcli_crt,
  239. GNUTLS_OID_X520_COMMON_NAME, 0, @Acommon_name[1], Length(Acommon_name)));
  240. TLSCheckRet(gnutls_x509_crt_set_version(Vcli_crt, 3));
  241. TLSCheckRet(gnutls_x509_crt_set_serial(Vcli_crt, @Aserial[1],
  242. Length(Aserial)));
  243. Vactivation := DateTimeToUnix(Now,False);
  244. TLSCheckRet(gnutls_x509_crt_set_activation_time(Vcli_crt, Vactivation));
  245. TLSCheckRet(gnutls_x509_crt_set_expiration_time(Vcli_crt,
  246. Vactivation + (Adays * 86400)));
  247. TLSCheckRet(gnutls_x509_crt_set_ca_status(Vcli_crt, Ord(False)));
  248. TLSCheckRet(gnutls_x509_crt_set_key_purpose_oid(Vcli_crt,
  249. @GNUTLS_KP_TLS_WWW_CLIENT[1], Ord(False)));
  250. Vkeyidsize := 0;
  251. Vret := gnutls_x509_crt_get_subject_key_id(Vca_crt, nil, @Vkeyidsize, nil);
  252. TLSCheck((Vret <> GNUTLS_E_SHORT_MEMORY_BUFFER) or (Vkeyidsize < 1), Vret);
  253. SetLength(Vkeyid, Pred(Vkeyidsize));
  254. TLSCheckRet(gnutls_x509_crt_get_subject_key_id(Vca_crt, @Vkeyid[1],
  255. @Vkeyidsize, nil));
  256. TLSCheckRet(gnutls_x509_crt_set_subject_key_id(Vcli_crt, @Vkeyid[1],
  257. Vkeyidsize));
  258. Vkeyidsize := 0;
  259. Vret := gnutls_x509_crt_get_key_id(Vca_crt, GNUTLS_KEYID_USE_SHA1,
  260. nil, @Vkeyidsize);
  261. TLSCheck((Vret <> GNUTLS_E_SHORT_MEMORY_BUFFER) or (Vkeyidsize < 1), Vret);
  262. SetLength(Vkeyid, Vkeyidsize);
  263. TLSCheckRet(gnutls_x509_crt_get_key_id(Vca_crt, GNUTLS_KEYID_USE_SHA1,
  264. @Vkeyid[1], @Vkeyidsize));
  265. TLSCheckRet(gnutls_x509_crt_set_authority_key_id(Vcli_crt, @Vkeyid[1],
  266. Vkeyidsize));
  267. TLSCheckRet(gnutls_x509_crt_sign2(Vcli_crt, Vca_crt, Vca_key,
  268. GNUTLS_DIG_SHA256, 0));
  269. Vcli_pem_size := CERT_SIZE;
  270. Acli_pem := '';
  271. SetLength(Acli_pem, Pred(Vcli_pem_size));
  272. TLSCheckRet(gnutls_x509_crt_export(Vcli_crt, GNUTLS_X509_FMT_PEM,
  273. @Acli_pem[1], @Vcli_pem_size));
  274. SetLength(Acli_pem, Vcli_pem_size);
  275. except
  276. gnutls_x509_privkey_deinit(Vcli_key);
  277. gnutls_x509_privkey_deinit(Vca_key);
  278. gnutls_x509_crt_deinit(Vca_crt);
  279. gnutls_x509_crt_deinit(Vcli_crt);
  280. raise;
  281. end;
  282. end;
  283. var
  284. ca_pkey, ca_crt, pkey, crt: AnsiString;
  285. begin
  286. LoadGnuTLS;
  287. Assert(GnuTLSLoaded);
  288. try
  289. WriteLn('Generating ', CA_KEY);
  290. TLSGenPrivKey(ca_pkey);
  291. Save(ca_pkey, CA_KEY);
  292. WriteLn('Done!');
  293. WriteLn('Generating ', CA_PEM);
  294. TLSGenCACert(ca_pkey, ca_crt, 'GnuTLS test CA', '01', 365);
  295. Save(ca_crt, CA_PEM);
  296. WriteLn('Done!');
  297. WriteLn('Generating ', SERVER_KEY);
  298. TLSGenPrivKey(pkey);
  299. Save(pkey, SERVER_KEY);
  300. WriteLn('Done!');
  301. WriteLn('Generating ', SERVER_PEM);
  302. TLSGenSrvCert(ca_pkey, ca_crt, pkey, crt, 'test.gnutls.org',
  303. 'GnuTLS test server', '01', 365);
  304. Save(crt, SERVER_PEM);
  305. WriteLn('Done!');
  306. WriteLn('Generating ', CLIENT_KEY);
  307. TLSGenPrivKey(pkey);
  308. Save(pkey, CLIENT_KEY);
  309. WriteLn('Done!');
  310. WriteLn('Generating ', CLIENT_PEM);
  311. TLSGenCliCert(ca_pkey, ca_crt, pkey, crt, 'GnuTLS test client', '01', 365);
  312. Save(crt, CLIENT_PEM);
  313. WriteLn('Done!');
  314. finally
  315. FreeGnuTLS;
  316. end;
  317. end.