@@ -1213,6 +1213,114 @@ ossl_sslctx_enable_fallback_scsv(VALUE self)
12131213}
12141214#endif
12151215
1216+ /*
1217+ * call-seq:
1218+ * ctx.add_certificate(certiticate, pkey [, extra_certs]) -> self
1219+ *
1220+ * Adds a certificate to the context. _pkey_ must be a corresponding private
1221+ * key with _certificate_.
1222+ *
1223+ * Multiple certificates with different public key type can be added by
1224+ * repeated calls of this method, and OpenSSL will choose the most appropriate
1225+ * certificate during the handshake.
1226+ *
1227+ * #cert=, #key=, and #extra_chain_cert= are old accessor methods for setting
1228+ * certificate and internally call this method.
1229+ *
1230+ * === Parameters
1231+ * _certificate_::
1232+ * A certificate. An instance of OpenSSL::X509::Certificate.
1233+ * _pkey_::
1234+ * The private key for _certificate_. An instance of OpenSSL::PKey::PKey.
1235+ * _extra_certs_::
1236+ * Optional. An array of OpenSSL::X509::Certificate. When sending a
1237+ * certificate chain, the certificates specified by this are sent following
1238+ * _certificate_, in the order in the array.
1239+ *
1240+ * === Example
1241+ * rsa_cert = OpenSSL::X509::Certificate.new(...)
1242+ * rsa_pkey = OpenSSL::PKey.read(...)
1243+ * ca_intermediate_cert = OpenSSL::X509::Certificate.new(...)
1244+ * ctx.add_certificate(rsa_cert, rsa_pkey, [ca_intermediate_cert])
1245+ *
1246+ * ecdsa_cert = ...
1247+ * ecdsa_pkey = ...
1248+ * another_ca_cert = ...
1249+ * ctx.add_certificate(ecdsa_cert, ecdsa_pkey, [another_ca_cert])
1250+ *
1251+ * === Note
1252+ * OpenSSL before the version 1.0.2 could handle only one extra chain across
1253+ * all key types. Calling this method discards the chain set previously.
1254+ */
1255+ static VALUE
1256+ ossl_sslctx_add_certificate (int argc , VALUE * argv , VALUE self )
1257+ {
1258+ VALUE cert , key , extra_chain_ary ;
1259+ SSL_CTX * ctx ;
1260+ X509 * x509 ;
1261+ STACK_OF (X509 ) * extra_chain = NULL ;
1262+ EVP_PKEY * pkey , * pub_pkey ;
1263+
1264+ GetSSLCTX (self , ctx );
1265+ rb_scan_args (argc , argv , "21" , & cert , & key , & extra_chain_ary );
1266+ rb_check_frozen (self );
1267+ x509 = GetX509CertPtr (cert );
1268+ pkey = GetPrivPKeyPtr (key );
1269+
1270+ /*
1271+ * The reference counter is bumped, and decremented immediately.
1272+ * X509_get0_pubkey() is only available in OpenSSL >= 1.1.0.
1273+ */
1274+ pub_pkey = X509_get_pubkey (x509 );
1275+ EVP_PKEY_free (pub_pkey );
1276+ if (!pub_pkey )
1277+ rb_raise (rb_eArgError , "certificate does not contain public key" );
1278+ if (EVP_PKEY_cmp (pub_pkey , pkey ) != 1 )
1279+ rb_raise (rb_eArgError , "public key mismatch" );
1280+
1281+ if (argc >= 3 )
1282+ extra_chain = ossl_x509_ary2sk (extra_chain_ary );
1283+
1284+ if (!SSL_CTX_use_certificate (ctx , x509 )) {
1285+ sk_X509_pop_free (extra_chain , X509_free );
1286+ ossl_raise (eSSLError , "SSL_CTX_use_certificate" );
1287+ }
1288+ if (!SSL_CTX_use_PrivateKey (ctx , pkey )) {
1289+ sk_X509_pop_free (extra_chain , X509_free );
1290+ ossl_raise (eSSLError , "SSL_CTX_use_PrivateKey" );
1291+ }
1292+
1293+ if (extra_chain ) {
1294+ #if OPENSSL_VERSION_NUMBER >= 0x10002000 && !defined(LIBRESSL_VERSION_NUMBER )
1295+ if (!SSL_CTX_set0_chain (ctx , extra_chain )) {
1296+ sk_X509_pop_free (extra_chain , X509_free );
1297+ ossl_raise (eSSLError , "SSL_CTX_set0_chain" );
1298+ }
1299+ #else
1300+ STACK_OF (X509 ) * orig_extra_chain ;
1301+ X509 * x509_tmp ;
1302+
1303+ /* First, clear the existing chain */
1304+ SSL_CTX_get_extra_chain_certs (ctx , & orig_extra_chain );
1305+ if (orig_extra_chain && sk_X509_num (orig_extra_chain )) {
1306+ rb_warning ("SSL_CTX_set0_chain() is not available; " \
1307+ "clearing previously set certificate chain" );
1308+ SSL_CTX_clear_extra_chain_certs (ctx );
1309+ }
1310+ while ((x509_tmp = sk_X509_shift (extra_chain ))) {
1311+ /* Transfers ownership */
1312+ if (!SSL_CTX_add_extra_chain_cert (ctx , x509_tmp )) {
1313+ X509_free (x509_tmp );
1314+ sk_X509_pop_free (extra_chain , X509_free );
1315+ ossl_raise (eSSLError , "SSL_CTX_add_extra_chain_cert" );
1316+ }
1317+ }
1318+ sk_X509_free (extra_chain );
1319+ #endif
1320+ }
1321+ return self ;
1322+ }
1323+
12161324/*
12171325 * call-seq:
12181326 * ctx.session_add(session) -> true | false
@@ -2345,11 +2453,17 @@ Init_ossl_ssl(void)
23452453
23462454 /*
23472455 * Context certificate
2456+ *
2457+ * The _cert_, _key_, and _extra_chain_cert_ attributes are deprecated.
2458+ * It is recommended to use #add_certificate instead.
23482459 */
23492460 rb_attr (cSSLContext , rb_intern ("cert" ), 1 , 1 , Qfalse );
23502461
23512462 /*
23522463 * Context private key
2464+ *
2465+ * The _cert_, _key_, and _extra_chain_cert_ attributes are deprecated.
2466+ * It is recommended to use #add_certificate instead.
23532467 */
23542468 rb_attr (cSSLContext , rb_intern ("key" ), 1 , 1 , Qfalse );
23552469
@@ -2423,6 +2537,9 @@ Init_ossl_ssl(void)
24232537 /*
24242538 * An Array of extra X509 certificates to be added to the certificate
24252539 * chain.
2540+ *
2541+ * The _cert_, _key_, and _extra_chain_cert_ attributes are deprecated.
2542+ * It is recommended to use #add_certificate instead.
24262543 */
24272544 rb_attr (cSSLContext , rb_intern ("extra_chain_cert" ), 1 , 1 , Qfalse );
24282545
@@ -2581,6 +2698,7 @@ Init_ossl_ssl(void)
25812698#ifdef SSL_MODE_SEND_FALLBACK_SCSV
25822699 rb_define_method (cSSLContext , "enable_fallback_scsv" , ossl_sslctx_enable_fallback_scsv , 0 );
25832700#endif
2701+ rb_define_method (cSSLContext , "add_certificate" , ossl_sslctx_add_certificate , -1 );
25842702
25852703 rb_define_method (cSSLContext , "setup" , ossl_sslctx_setup , 0 );
25862704 rb_define_alias (cSSLContext , "freeze" , "setup" );
0 commit comments