@@ -1193,6 +1193,114 @@ ossl_sslctx_set_security_level(VALUE self, VALUE value)
11931193 return value ;
11941194}
11951195
1196+ /*
1197+ * call-seq:
1198+ * ctx.add_certificate(certiticate, pkey [, extra_certs]) -> self
1199+ *
1200+ * Adds a certificate to the context. _pkey_ must be a corresponding private
1201+ * key with _certificate_.
1202+ *
1203+ * Multiple certificates with different public key type can be added by
1204+ * repeated calls of this method, and OpenSSL will choose the most appropriate
1205+ * certificate during the handshake.
1206+ *
1207+ * #cert=, #key=, and #extra_chain_cert= are old accessor methods for setting
1208+ * certificate and internally call this method.
1209+ *
1210+ * === Parameters
1211+ * _certificate_::
1212+ * A certificate. An instance of OpenSSL::X509::Certificate.
1213+ * _pkey_::
1214+ * The private key for _certificate_. An instance of OpenSSL::PKey::PKey.
1215+ * _extra_certs_::
1216+ * Optional. An array of OpenSSL::X509::Certificate. When sending a
1217+ * certificate chain, the certificates specified by this are sent following
1218+ * _certificate_, in the order in the array.
1219+ *
1220+ * === Example
1221+ * rsa_cert = OpenSSL::X509::Certificate.new(...)
1222+ * rsa_pkey = OpenSSL::PKey.read(...)
1223+ * ca_intermediate_cert = OpenSSL::X509::Certificate.new(...)
1224+ * ctx.add_certificate(rsa_cert, rsa_pkey, [ca_intermediate_cert])
1225+ *
1226+ * ecdsa_cert = ...
1227+ * ecdsa_pkey = ...
1228+ * another_ca_cert = ...
1229+ * ctx.add_certificate(ecdsa_cert, ecdsa_pkey, [another_ca_cert])
1230+ *
1231+ * === Note
1232+ * OpenSSL before the version 1.0.2 could handle only one extra chain across
1233+ * all key types. Calling this method discards the chain set previously.
1234+ */
1235+ static VALUE
1236+ ossl_sslctx_add_certificate (int argc , VALUE * argv , VALUE self )
1237+ {
1238+ VALUE cert , key , extra_chain_ary ;
1239+ SSL_CTX * ctx ;
1240+ X509 * x509 ;
1241+ STACK_OF (X509 ) * extra_chain = NULL ;
1242+ EVP_PKEY * pkey , * pub_pkey ;
1243+
1244+ GetSSLCTX (self , ctx );
1245+ rb_scan_args (argc , argv , "21" , & cert , & key , & extra_chain_ary );
1246+ rb_check_frozen (self );
1247+ x509 = GetX509CertPtr (cert );
1248+ pkey = GetPrivPKeyPtr (key );
1249+
1250+ /*
1251+ * The reference counter is bumped, and decremented immediately.
1252+ * X509_get0_pubkey() is only available in OpenSSL >= 1.1.0.
1253+ */
1254+ pub_pkey = X509_get_pubkey (x509 );
1255+ EVP_PKEY_free (pub_pkey );
1256+ if (!pub_pkey )
1257+ rb_raise (rb_eArgError , "certificate does not contain public key" );
1258+ if (EVP_PKEY_cmp (pub_pkey , pkey ) != 1 )
1259+ rb_raise (rb_eArgError , "public key mismatch" );
1260+
1261+ if (argc >= 3 )
1262+ extra_chain = ossl_x509_ary2sk (extra_chain_ary );
1263+
1264+ if (!SSL_CTX_use_certificate (ctx , x509 )) {
1265+ sk_X509_pop_free (extra_chain , X509_free );
1266+ ossl_raise (eSSLError , "SSL_CTX_use_certificate" );
1267+ }
1268+ if (!SSL_CTX_use_PrivateKey (ctx , pkey )) {
1269+ sk_X509_pop_free (extra_chain , X509_free );
1270+ ossl_raise (eSSLError , "SSL_CTX_use_PrivateKey" );
1271+ }
1272+
1273+ if (extra_chain ) {
1274+ #if OPENSSL_VERSION_NUMBER >= 0x10002000 && !defined(LIBRESSL_VERSION_NUMBER )
1275+ if (!SSL_CTX_set0_chain (ctx , extra_chain )) {
1276+ sk_X509_pop_free (extra_chain , X509_free );
1277+ ossl_raise (eSSLError , "SSL_CTX_set0_chain" );
1278+ }
1279+ #else
1280+ STACK_OF (X509 ) * orig_extra_chain ;
1281+ X509 * x509_tmp ;
1282+
1283+ /* First, clear the existing chain */
1284+ SSL_CTX_get_extra_chain_certs (ctx , & orig_extra_chain );
1285+ if (orig_extra_chain && sk_X509_num (orig_extra_chain )) {
1286+ rb_warning ("SSL_CTX_set0_chain() is not available; " \
1287+ "clearing previously set certificate chain" );
1288+ SSL_CTX_clear_extra_chain_certs (ctx );
1289+ }
1290+ while ((x509_tmp = sk_X509_shift (extra_chain ))) {
1291+ /* Transfers ownership */
1292+ if (!SSL_CTX_add_extra_chain_cert (ctx , x509_tmp )) {
1293+ X509_free (x509_tmp );
1294+ sk_X509_pop_free (extra_chain , X509_free );
1295+ ossl_raise (eSSLError , "SSL_CTX_add_extra_chain_cert" );
1296+ }
1297+ }
1298+ sk_X509_free (extra_chain );
1299+ #endif
1300+ }
1301+ return self ;
1302+ }
1303+
11961304/*
11971305 * call-seq:
11981306 * ctx.session_add(session) -> true | false
@@ -2324,11 +2432,17 @@ Init_ossl_ssl(void)
23242432
23252433 /*
23262434 * Context certificate
2435+ *
2436+ * The _cert_, _key_, and _extra_chain_cert_ attributes are deprecated.
2437+ * It is recommended to use #add_certificate instead.
23272438 */
23282439 rb_attr (cSSLContext , rb_intern ("cert" ), 1 , 1 , Qfalse );
23292440
23302441 /*
23312442 * Context private key
2443+ *
2444+ * The _cert_, _key_, and _extra_chain_cert_ attributes are deprecated.
2445+ * It is recommended to use #add_certificate instead.
23322446 */
23332447 rb_attr (cSSLContext , rb_intern ("key" ), 1 , 1 , Qfalse );
23342448
@@ -2402,6 +2516,9 @@ Init_ossl_ssl(void)
24022516 /*
24032517 * An Array of extra X509 certificates to be added to the certificate
24042518 * chain.
2519+ *
2520+ * The _cert_, _key_, and _extra_chain_cert_ attributes are deprecated.
2521+ * It is recommended to use #add_certificate instead.
24052522 */
24062523 rb_attr (cSSLContext , rb_intern ("extra_chain_cert" ), 1 , 1 , Qfalse );
24072524
@@ -2557,6 +2674,7 @@ Init_ossl_ssl(void)
25572674 rb_define_method (cSSLContext , "ecdh_curves=" , ossl_sslctx_set_ecdh_curves , 1 );
25582675 rb_define_method (cSSLContext , "security_level" , ossl_sslctx_get_security_level , 0 );
25592676 rb_define_method (cSSLContext , "security_level=" , ossl_sslctx_set_security_level , 1 );
2677+ rb_define_method (cSSLContext , "add_certificate" , ossl_sslctx_add_certificate , -1 );
25602678
25612679 rb_define_method (cSSLContext , "setup" , ossl_sslctx_setup , 0 );
25622680 rb_define_alias (cSSLContext , "freeze" , "setup" );
0 commit comments