赞
踩
以下根据strongswan代码中的testing/tests/ikev2/net2net-dnssec/中的测试环境,来看一下IKEv2协议在协商时不交换证书,而是通过DNSSEC获取对端公钥的认证流程。拓扑结构如下:
拓扑图中使用到的设备包括:虚拟网关moon和sun网关。
moon的配置文件:/etc/ipsec.conf,内容如下。注意此处的leftsigkey字段,在strongswan中此字段等同于leftrsasigkey字段,用于配置本地的证书公钥。
conn %default rekeymargin=3m keyexchange=ikev2 mobike=no conn net-net left=PH_IP_MOON leftid=moon.strongswan.org leftsubnet=10.1.0.0/16 leftsigkey=moonPub.pem leftauth=pubkey leftfirewall=yes right=sun.strongswan.org rightid=sun.strongswan.org rightsubnet=10.2.0.0/16 rightauth=pubkey auto=add
配置文件/etc/strongswan.conf中加载相关插件dnskey、ipseckey和unbound。此外charon.plugins.ipseckey.enable等于yes,表明使能由DNS中获取IPSECKEY RRs资源记录,默认请求下禁止此操作。charon.plugin.unbound.trust_anchors的默认值为:/etc/ipsec.d/dnssec.keys,其指定了DNS可信锚所在文件,以下将看到其内容。
charon {
load = random nonce aes sha1 sha2 hmac curve25519 gmp dnskey pem pkcs1 pubkey unbound ipseckey curl kernel-netlink socket-default stroke updown
plugins {
ipseckey {
enable = yes
}
unbound {
# trust_anchors = /etc/ipsec.d/dnssec.keys
# resolv_conf = /etc/resolv.conf
}
}
}
此外,moon网关的公钥位于:/etc/ipsec.d/certs/moonPub.pem文件中。moon网关的文件/etc/ipsec.d/dnssec.keys如下,按照RFC4034中对DNSSEC RR的格式定义解析以下数据:
; This is a key-signing key, keyid 32329, for .
. IN DNSKEY 257 3 8 (
AwEAAbcskaratFgvgvXl0bNq4I43ZBzd9jYnoPqsIcA0ahqXlUTUa+c2
XzN2mS7DGcI4Z5Gn+8v/Ih4lQJQrlf9I/c2HjooCAsK1bA5cRS2DiU+b
L6Ge0nLtvNOf4C0MHGLrWcDONg5QoL0OcFvMXuUtOvDkoIMdtfDYDScx
E9vSokc98Sx553/MTxpssXeM9i+OauGqohIZU+MVRdWwvJPieCL7Ma4b
AttgG+KSbQy7x/qXPISoqzwGQvCxsL93fvD/cpp+KziqA0oH+Dfryvc5
nWdCdra4gYz7WCFFwcY1PW6PbL5ie4jnjl3WWxopuzT46HKROxDhE+FO
O9fOgGnjzAk=
)
sun网关的配置与moon网关基本相同,并且具有和sun网关相同dnssec.key文件。
文件strongswan-5.8.1/src/charon/charon.c中主函数main,调用daemon_t结构类型的charon的成员函数initialize,根据配置文件strongswan.conf中的"charon.load"部分的值,初始化要加载的插件。
/* Main function, starts the daemon.
*/
int main(int argc, char *argv[])
{
...
/* initialize daemon */
if (!charon->initialize(charon, lib->settings->get_str(lib->settings, "charon.load", PLUGINS))) {
DBG1(DBG_DMN, "initialization failed - aborting charon");
goto deinit;
}
lib->plugins->status(lib->plugins, LEVEL_CTRL);
如下为文件strongswan-5.8.1/src/libcharon/daemon.c中的initialize函数实现,其主体调用了plugin_loader_t结构的load函数指针,其指向src/libstrongswan/plugins/plugin_loader.c文件中的函数load_plugins。默认情况下所有的plugin都按照在目录/usr/local/lib/ipsec/plugins/下。
METHOD(daemon_t, initialize, bool, private_daemon_t *this, char *plugins)
{
...
/* load plugins, further infrastructure may need it */
if (!lib->plugins->load(lib->plugins, plugins))
{
return FALSE;
}
在加载插件对应的so库文件之后,将调用其中的函数%name_plugin_create,对于DNSSEC相关的插件:dnskey/unbound/ipseckey,将分别调用函数dnskey_plugin_create/unbound_plugin_create/ipseckey_plugin_create,具体调用过程可参见文件libstrongswan/plugins/plugin_loader.c中的函数create_plugin。
接下来,函数register_features将通过调用插件中提供的get_features函数,获取插件可提供的特性。最后,通过函数load_features,加载插件特性,在此之前,将首先加载此插件所依赖的其它插件,参见函数load_feature。以下为最终的插件特性加载函数,对于类型FEATURE_CALLBACK,此处将调用其回调函数。
bool plugin_feature_load(plugin_t *plugin, plugin_feature_t *feature, plugin_feature_t *reg) { char *name; if (reg->kind == FEATURE_CALLBACK) { if (!reg->arg.cb.f || reg->arg.cb.f(plugin, feature, TRUE, reg->arg.cb.data)) { return TRUE; } return FALSE; } name = plugin->get_name(plugin); switch (feature->type) { ... case FEATURE_PUBKEY: lib->creds->add_builder(lib->creds, CRED_PUBLIC_KEY, feature->arg.pubkey, reg->arg.reg.final, reg->arg.reg.f); break; case FEATURE_CERT_DECODE: case FEATURE_CERT_ENCODE: lib->creds->add_builder(lib->creds, CRED_CERTIFICATE, feature->arg.cert, reg->arg.reg.final, reg->arg.reg.f); case FEATURE_RESOLVER: lib->resolver->add_resolver(lib->resolver, reg->arg.reg.f); break;
ipseckey插件注册了FEATURE_CALLBACK类型特性,在以上加载函数plugin_feature_load中,将调用其注册的回调函数plugin_cb。
METHOD(plugin_t, get_features, int, private_ipseckey_plugin_t *this, plugin_feature_t *features[])
{
static plugin_feature_t f[] = {
PLUGIN_CALLBACK((plugin_feature_callback_t)plugin_cb, NULL),
PLUGIN_PROVIDE(CUSTOM, "ipseckey"),
PLUGIN_DEPENDS(RESOLVER),
PLUGIN_DEPENDS(PUBKEY, KEY_RSA),
PLUGIN_DEPENDS(CERT_ENCODE, CERT_TRUSTED_PUBKEY),
};
*features = f;
return countof(f);
与以上的ipseckey插件不同,dnskey插件不是FEATURE_CALLBACK类型。其提供了FEATURE_PUBKEY,在加载此插件时,函数plugin_feature_load将调用lib->creds->add_builder函数指针,向系统的信任管理模块(credential_factory_t结构)添加一个builder。
METHOD(plugin_t, get_features, int, private_dnskey_plugin_t *this, plugin_feature_t *features[])
{
static plugin_feature_t f[] = {
PLUGIN_REGISTER(PUBKEY, dnskey_public_key_load, FALSE),
PLUGIN_PROVIDE(PUBKEY, KEY_ANY),
PLUGIN_REGISTER(PUBKEY, dnskey_public_key_load, FALSE),
PLUGIN_PROVIDE(PUBKEY, KEY_RSA),
};
*features = f;
return countof(f);
在此插件加载时,如下插件创建函数dnskey_plugin_create,将调用lib->encoding->add_encoder函数指针,向系统的信任管理模块添加一个编码器。
plugin_t *dnskey_plugin_create() { private_dnskey_plugin_t *this; INIT(this, .public = { .plugin = { .get_name = _get_name, .get_features = _get_features, .destroy = _destroy, }, }, ); lib->encoding->add_encoder(lib->encoding, dnskey_encoder_encode); return &this->public.plugin;
与以上的dnskey插件类似,unbound插件也不是FEATURE_CALLBACK类型,get_feature没有指定回调函数。unbound插件的大部分函数使用系统的unbound和ldns库实现,在编译Makefile中,需要链接这两个库(-lunbound -lldns)。
METHOD(plugin_t, get_features, int, private_unbound_plugin_t *this, plugin_feature_t *features[])
{
static plugin_feature_t f[] = {
PLUGIN_REGISTER(RESOLVER, unbound_resolver_create),
PLUGIN_PROVIDE(RESOLVER),
};
*features = f;
return countof(f);
由以上函数plugin_feature_load可知,对于FEATURE_RESOLVER类型,将调用函数指针lib->resolver->add_resolver,向系统的resolver管理器(resolver_manager_t结构)添加一个resolver。
以下为unbound插件的加载函数,在本例的配置中,没有未其指定加载参数,所以unbound.resolv_conf字段使用默认值:’/etc/resolv.conf’。字段unbound.trust_anchors也使用默认值:’/etc/ipsec.d/dnssec.keys’。字段dlv_anchors的默认值为NULL。此插件仅实现了一个公共函数query。
resolver_t *unbound_resolver_create(void)
{
private_resolver_t *this;
char *resolv_conf, *trust_anchors, *dlv_anchors;
resolv_conf = lib->settings->get_str(lib->settings, "%s.plugins.unbound.resolv_conf", RESOLV_CONF_FILE, lib->ns);
trust_anchors = lib->settings->get_str(lib->settings, "%s.plugins.unbound.trust_anchors", TRUST_ANCHOR_FILE, lib->ns);
dlv_anchors = lib->settings->get_str(lib->settings, "%s.plugins.unbound.dlv_anchors", NULL, lib->ns);
INIT(this,
.public = {
.query = _query,
.destroy = _destroy,
},
);
以下为unbound库的初始化函数,分别将DNS服务器配置文件resolv_conf,和DNS可信任锚文件trust_anchors添加到unbound库中。
this->ctx = ub_ctx_create();
if (!this->ctx) { ... return NULL; }
DBG2(DBG_CFG, "loading unbound resolver config from '%s'", resolv_conf);
ub_retval = ub_ctx_resolvconf(this->ctx, resolv_conf);
if (ub_retval) { ... return NULL; }
DBG2(DBG_CFG, "loading unbound trust anchors from '%s'", trust_anchors);
ub_retval = ub_ctx_add_ta_file(this->ctx, trust_anchors);
if (ub_retval) {
DBG1(DBG_CFG, "failed to load trust anchors: %s (%s)", ub_strerror(ub_retval), strerror(errno));
}
...
return &this->public;
文件src/libcharon/plugins/stroke/stroke_config.c中,函数build_auth_cfg解析处理sigkey字段配置。
static auth_cfg_t *build_auth_cfg(private_stroke_config_t *this, stroke_msg_t *msg, bool local, bool primary)
{
/* add raw RSA public key */
pubkey = end->rsakey;
if (pubkey && !streq(pubkey, "") && !streq(pubkey, "%cert")) {
certificate = this->cred->load_pubkey(this->cred, pubkey, identity);
if (certificate) {
cfg->add(cfg, AUTH_RULE_SUBJECT_CERT, certificate);
}
}
文件src/libcharon/plugins/stroke/stroke_cred.c中函数load_pubkey如下,由于在配置文件中leftsigkey字段值等于moonPub.pem,使用默认的路径CERTIFICATE_DIR(目录:/etc/ipsec.d/certs/)获取秘钥文件。之后,据此文件调用函数lib->creds->create生成证书结构(certificate_t),其中的类型参数type为CRED_CERTIFICATE,子类型为CERT_TRUSTED_PUBKEY,依据这两个参数找到合适的处理插件。
METHOD(stroke_cred_t, load_pubkey, certificate_t*, private_stroke_cred_t *this, char *filename, identification_t *identity) { certificate_t *cert; public_key_t *key; key_type_t type = KEY_ANY; if (strncaseeq(filename, "0x", 2) || strncaseeq(filename, "0s", 2)) { ... } else { if (*filename == '/') { snprintf(path, sizeof(path), "%s", filename); } else { snprintf(path, sizeof(path), "%s/%s", CERTIFICATE_DIR, filename); } cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_TRUSTED_PUBKEY, BUILD_FROM_FILE, path, BUILD_SUBJECT, identity, BUILD_END); if (cert) { cert = this->creds->add_cert_ref(this->creds, TRUE, cert); key = cert->get_public_key(cert); type = key->get_type(key); key->destroy(key); DBG1(DBG_CFG, " loaded %N public key for \"%Y\" from '%s'", key_type_names, type, identity, filename); return cert;
位于文件src/libstrongswan/plugins/pem/pem_plugin.c中的PEM插件,注册的函数pem_certificate_load,提供子类型为CERT_TRUSTED_PUBKEY的特性。由以上函数plugin_feature_load可知,所有支持CERT_DECODE/CERT_ENCODE的插件,都将向系统注册类型为CRED_CERTIFICATE的credential builder,与以上的create函数对应,这里调用注册函数pem_certificate_load。
METHOD(plugin_t, get_features, int, private_pem_plugin_t *this, plugin_feature_t *features[])
{
static plugin_feature_t f[] = {
/* certificate PEM decoding */
...
PLUGIN_REGISTER(CERT_DECODE, pem_certificate_load, FALSE),
PLUGIN_PROVIDE(CERT_DECODE, CERT_TRUSTED_PUBKEY),
函数pem_certificate_load,封装了文件src/libstrongswan/plugins/pem/pem_builder.c文件中的函数pem_load,如下所示。解析出参数,文件名file和subject,调用函数load_from_file继续处理,此函数读取文件内容到缓存块chunk_t结构中,调用函数load_from_blob处理。
static void *pem_load(credential_type_t type, int subtype, va_list args) { char *file = NULL; identification_t *subject = NULL; while (TRUE) { switch (va_arg(args, builder_part_t)) { case BUILD_FROM_FILE: file = va_arg(args, char*); continue; case BUILD_SUBJECT: subject = va_arg(args, identification_t*); continue; case BUILD_END: break; default: return NULL; } break; } if (file) { return load_from_file(file, type, subtype, subject, flags); }
如下所示,第一个参数blob保存了秘钥文件的内容,这里再次调用credential_factory_t结构的create函数,与上次不同,此次使用的两个参数为BUILD_BLOB_ASN1_DER和BUILD_SUBJECT。
static void *load_from_blob(chunk_t blob, credential_type_t type, int subtype, identification_t *subject, x509_flag_t flags)
{
void *cred = NULL;
if (type == CRED_CERTIFICATE && subtype == CERT_TRUSTED_PUBKEY && subject) {
cred = lib->creds->create(lib->creds, type, subtype,
BUILD_BLOB_ASN1_DER, blob, BUILD_SUBJECT, subject, BUILD_END);
}
位于文件src/libstrongswan/plugins/pubkey/pubkey_plugin.c的插件pubkey,注册了类型为RED_CERTIFICATE,子类型为CERT_TRUSTED_PUBKEY的特性,这里调用其函数pubkey_cert_wrap进行处理。
METHOD(plugin_t, get_features, int, private_pubkey_plugin_t *this, plugin_feature_t *features[])
{
static plugin_feature_t f[] = {
PLUGIN_REGISTER(CERT_ENCODE, pubkey_cert_wrap, FALSE),
PLUGIN_PROVIDE(CERT_ENCODE, CERT_TRUSTED_PUBKEY),
PLUGIN_REGISTER(CERT_DECODE, pubkey_cert_wrap, TRUE),
PLUGIN_PROVIDE(CERT_DECODE, CERT_TRUSTED_PUBKEY),
如下src/libstrongswan/plugins/pubkey/pubkey_cert.c文件中函数pubkey_cert_wrap,根据传入的参数bloc和subject,再次调用credential_factory_t结构的create指针创建public_key_t结构,这次使用的类型值为CRED_PUBLIC_KEY,子类型为KEY_ANY。完成之后,由函数pubkey_cert_create据此publick_key_t结构创建一个公钥证书结构pubkey_cert_t。
pubkey_cert_t *pubkey_cert_wrap(certificate_type_t type, va_list args) { public_key_t *key = NULL; chunk_t blob = chunk_empty; identification_t *subject = NULL; time_t notBefore = UNDEFINED_TIME, notAfter = UNDEFINED_TIME; while (TRUE) { switch (va_arg(args, builder_part_t)) { case BUILD_BLOB_ASN1_DER: blob = va_arg(args, chunk_t); continue; case BUILD_SUBJECT: subject = va_arg(args, identification_t*); continue; case BUILD_END: break; default: return NULL; } break; } if (key) { ... } else if (blob.ptr) { key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY, BUILD_BLOB_ASN1_DER, blob, BUILD_END); } if (key) { return pubkey_cert_create(key, notBefore, notAfter, subject); }
文件src/libstrongswan/plugins/pkcs1/pkcs1_plugin.c中实现的pkcs1插件,提供了对类型为CRED_PUBLIC_KEY,子类型为KEY_ANY的支持,由函数pkcs1_public_key_load完成。
METHOD(plugin_t, get_features, int, private_pkcs1_plugin_t *this, plugin_feature_t *features[])
{
static plugin_feature_t f[] = {
PLUGIN_REGISTER(PUBKEY, pkcs1_public_key_load, FALSE),
PLUGIN_PROVIDE(PUBKEY, KEY_ANY),
如下为pkcs1_public_key_load函数,其根据BUILD_BLOB_ASN1_DER类型,取得公钥文件内容blob,之后根据公钥类型KEY_ANY,调用函数parse_public_key进行处理,而且将会再次调用credential_factory_t结构的create函数指针,但是,将key_type_t参数修改为KEY_ESA。因此,最终将调用到以下的函数parse_rsa_public_key。
public_key_t *pkcs1_public_key_load(key_type_t type, va_list args) { chunk_t blob = chunk_empty; while (TRUE) { switch (va_arg(args, builder_part_t)) { case BUILD_BLOB_ASN1_DER: blob = va_arg(args, chunk_t); continue; case BUILD_END: break; default: return NULL; } break; } switch (type) { case KEY_ANY: return parse_public_key(blob); case KEY_RSA: return parse_rsa_public_key(blob);
如下函数parse_rsa_public_key,其由blob中解析出PUB_KEY_MODULUS和PUB_KEY_EXPONENT两个字段,以这两个值为参数再次调用credential_factory_t结构的create函数指针。
static public_key_t *parse_rsa_public_key(chunk_t blob) { chunk_t n, e; asn1_parser_t *parser; parser = asn1_parser_create(pubkeyObjects, blob); while (parser->iterate(parser, &objectID, &object)) { switch (objectID) { case PUB_KEY_MODULUS: n = object; break; case PUB_KEY_EXPONENT: e = object; break; } } success = parser->success(parser); parser->destroy(parser); if (!success) { return NULL; } return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, BUILD_RSA_MODULUS, n, BUILD_RSA_PUB_EXP, e, BUILD_END);
如下文件src/libstrongswan/plugins/gmp/gmp_plugin.c中定义的gmp插件,其注册了FEATURE_PUBKEY,子类型为KEY_RSA的处理特性。处理函数为gmp_rsa_public_key_load。
METHOD(plugin_t, get_features, int, private_gmp_plugin_t *this, plugin_feature_t *features[])
{
static plugin_feature_t f[] = {
PLUGIN_REGISTER(PUBKEY, gmp_rsa_public_key_load, TRUE),
PLUGIN_PROVIDE(PUBKEY, KEY_RSA),
以下函数gmp_rsa_public_key_load,首先提取参数中传入的RSA的modulus和exponent数据;之后,使用GMP库函数mpz_import导入modules和exponent两个数据。
gmp_rsa_public_key_t *gmp_rsa_public_key_load(key_type_t type, va_list args) { private_gmp_rsa_public_key_t *this; chunk_t n, e; n = e = chunk_empty; while (TRUE) { switch (va_arg(args, builder_part_t)) { case BUILD_RSA_MODULUS: n = va_arg(args, chunk_t); continue; case BUILD_RSA_PUB_EXP: e = va_arg(args, chunk_t); continue; ... } break; } if (!e.len || !n.len || (n.ptr[n.len-1] & 0x01) == 0) { return NULL; } INIT(this, .public = { ... }, .ref = 1, ); mpz_init(this->n); mpz_init(this->e); mpz_import(this->n, n.len, 1, 1, 1, 0, n.ptr); mpz_import(this->e, e.len, 1, 1, 1, 0, e.ptr); this->k = (mpz_sizeinbase(this->n, 2) + 7) / BITS_PER_BYTE; if (!mpz_sgn(this->e)) { destroy(this); return NULL; } return &this->public;
至此,公钥文件解析完成,用到的插件有gmp、pem、pkcs1和pubkey等,这些插件都在strongswan.conf文件中进行了加载。
文件src/libcharon/sa/ikev2/tasks/ike_cert_pre.c中,函数build_r在响应的IKE_SA_INIT报文中,添加类型为PLV2_CERTREQ(38)的证书请求载荷。
METHOD(task_t, build_r, status_t, private_ike_cert_pre_t *this, message_t *message)
{
if (message->get_exchange_type(message) == IKE_SA_INIT) {
build_certreqs(this, message);
}
if (this->final) { return SUCCESS; }
return NEED_MORE;
在上节函数load_pubkey(位于文件src/libcharon/plugins/stroke/stroke_cred.c)的介绍中可知,此处的证书类型为CERT_TRUSTED_PUBKEY。函数add_certreq仅对证书类型为CERT_X509有效,所以,在sun网关回复的IKE_SA_INIT报文中,并不带有PLV1_CERTREQ(7)类型的载荷。
static void add_certreq(certreq_payload_t **req, certificate_t *cert)
{
switch (cert->get_type(cert)) {
case CERT_X509: {
...
break;
}
default:
break;
随后,在IKE_AUTH报文的交互中,sun和moon双方都不会发送PLV1_CERTREQ(7)类型的载荷,并且也不为发送PLV1_CERTIFICATE(6)类型的载荷,双方需要通过DNSSEC获取对端的公钥。
sun网关在接收到moon网关的IKE_AUTH报文之后,调用文件src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c中的函数process,处理报文中的PLV2_AUTH载荷数据。主要的验证工作由public_key_t结构的verify函数实现。
METHOD(authenticator_t, process, status_t, private_pubkey_authenticator_t *this, message_t *message) { auth_payload = (auth_payload_t*)message->get_payload(message, PLV2_AUTH); if (!auth_payload) { return FAILED; } auth_method = auth_payload->get_auth_method(auth_payload); auth_data = auth_payload->get_data(auth_payload); switch (auth_method) { case AUTH_RSA: key_type = KEY_RSA; params->scheme = SIGN_RSA_EMSA_PKCS1_SHA1; break; ... } enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, key_type, id, auth, online); while (enumerator->enumerate(enumerator, &public, ¤t_auth)) { if (public->verify(public, params->scheme, params->params, octets, auth_data) && is_compliant_cert(current_auth))
以上函数中的lib->credmgr->create_public_enumerator将调用到文件src/libcharon/plugins/ipseckey/ipseckey_cred.c中ipseckey插件的函数create_cert_enumerator,如下所示。首先,调用resolver_t结构的query函数执行DNS查询操作,对于moon主机,其ID载荷字段中带有ID类型为ID_FQDN(2),值为:moon.strongswan.org。此处的query函数将尝试解析此域名,请求RR_CLASS_IN类,RR_TYPE_IPSECKEY类型的资源记录。返回结构保存在返回的resolver_response_t结构中。
METHOD(credential_set_t, create_cert_enumerator, enumerator_t*, private_ipseckey_cred_t *this, certificate_type_t cert, key_type_t key, identification_t *id, bool trusted) { resolver_response_t *response; cert_enumerator_t *e; /* query the DNS for the required IPSECKEY RRs */ if (asprintf(&fqdn, "%Y", id) <= 0) { DBG1(DBG_CFG, "failed to determine FQDN to retrieve IPSECKEY RRs"); return enumerator_create_empty(); } DBG1(DBG_CFG, "performing a DNS query for IPSECKEY RRs of '%s'", fqdn); response = this->res->query(this->res, fqdn, RR_CLASS_IN, RR_TYPE_IPSECKEY); if (!response) { DBG1(DBG_CFG, " query for IPSECKEY RRs failed"); free(fqdn); return enumerator_create_empty(); } ...
参考注释说明,以下代码根据DNS回复报文中的第一个RRSIG RR记录中的证书起止时间,来验证IPSECKEY RR记录的有效期。之后的版本,可能考虑报文中的多个RRSIG记录。之后,由DNS报文中RRSIG记录中获取出起止时间,保存在变量:nAfter和nBefore中。函数最后,初始化一个cert_enumerator_t结构,返回其public成员(enumerator_t结构类型)。
/* determine the validity period of the retrieved IPSECKEYs * we use the "Signature Inception" and "Signature Expiration" field of the first RRSIG RR to determine the validity period of the IPSECKEY RRs. TODO: Take multiple RRSIGs into account. */ rrset = response->get_rr_set(response); rrsig_enum = rrset->create_rrsig_enumerator(rrset); if (!rrsig_enum || !rrsig_enum->enumerate(rrsig_enum, &rrsig)) { ... return enumerator_create_empty(); } reader = bio_reader_create(rrsig->get_rdata(rrsig)); /* parse the RRSIG for its validity period (RFC 4034) */ if (!reader->read_data(reader, 8, &ignore) || !reader->read_uint32(reader, &nAfter) || !reader->read_uint32(reader, &nBefore)) { ... return enumerator_create_empty(); } INIT(e, .public = { .enumerate = enumerator_enumerate_default, .venumerate = _cert_enumerator_enumerate, }, .inner = rrset->create_rr_enumerator(rrset), .response = response, .notBefore = nBefore, .notAfter = nAfter, .identity = id, ); return &e->public;
接下来pubkey_authenticator.c文件中的函数process,将调用以上函数中返回的enumerator_t结构的成员函数enumerate获取DNS报文中公钥(public_key_t),以及本地认证配置信息(auth_cfg_t),之后,由公钥结构的成员函数verify(public->verify)来执行对moon网关发送的AUTH载荷中数据的验证。
其中enumerate函数,实际上调用的为ipseckey_cred.c文件中的函数cert_enumerator_enumerate。如下所示,其将遍历所有的IPSECKEY RR记录,据此记录创建IPSEC秘钥结构ipseckey_t,首先具体结构创建公钥结构public_key_t;再将此公钥结构封装在证书(certificate_t)结构之内,
METHOD(enumerator_t, cert_enumerator_enumerate, bool, cert_enumerator_t *this, va_list args) { certificate_t **cert; ipseckey_t *cur_ipseckey; public_key_t *public; rr_t *cur_rr; chunk_t key; /* Get the next supported IPSECKEY using the inner enumerator. */ while (this->inner->enumerate(this->inner, &cur_rr)) { cur_ipseckey = ipseckey_create_frm_rr(cur_rr); if (cur_ipseckey->get_algorithm(cur_ipseckey) != IPSECKEY_ALGORITHM_RSA) { ... continue; } /* wrap the key of the IPSECKEY in a certificate and return this certificate */ key = cur_ipseckey->get_public_key(cur_ipseckey); public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, BUILD_BLOB_DNSKEY, key, BUILD_END); this->cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_TRUSTED_PUBKEY, BUILD_PUBLIC_KEY, public, BUILD_SUBJECT, this->identity, BUILD_NOT_BEFORE_TIME, this->notBefore, BUILD_NOT_AFTER_TIME, this->notAfter, BUILD_END); public->destroy(public); *cert = this->cert; return TRUE;
其中,函数ipseckey_create_frm_rr负责根据RR记录创建ipseckey_t结构的IPSEC秘钥。函数gmp_rsa_public_key_load负责将原始秘钥,封装为公钥结构public_key_t。最后函数pubkey_cert_wrap负责将公钥结构封装在证书(pubkey_cert_t)结构内。
之后,将调用credential_manager.c文件中的trusted_enumerate函数,如下,获取相应的本地认证配置。
METHOD(enumerator_t, trusted_enumerate, bool, trusted_enumerator_t *this, va_list args) { certificate_t *current, **cert; auth_cfg_t **auth; VA_ARGS_VGET(args, cert, auth); this->auth = auth_cfg_create(); if (!this->candidates) { this->candidates = create_cert_enumerator(this->this, CERT_ANY, this->type, this->id, FALSE); this->pretrusted = get_pretrusted_cert(this->this, this->type, this->id); if (this->pretrusted) { /* if we find a trusted self signed certificate, we just accept it... */ if (issued_by(this->this, this->pretrusted, this->pretrusted, NULL) || verify_trust_chain(this->this, this->pretrusted, this->auth, TRUE, this->online)) { DBG1(DBG_CFG, " using trusted certificate \"%Y\"", this->pretrusted->get_subject(this->pretrusted)); *cert = this->pretrusted; if (!this->auth->get(this->auth, AUTH_RULE_SUBJECT_CERT)) { this->auth->add(this->auth, AUTH_RULE_SUBJECT_CERT, this->pretrusted->get_ref(this->pretrusted)); } if (auth) { *auth = this->auth;
如下为文件src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c中的验证函数verify,其封装了函数verify_emsa_pkcs1_signature,本例中使用的scheme为SHA256。
METHOD(public_key_t, verify, bool, private_gmp_rsa_public_key_t *this, signature_scheme_t scheme, void *params,
chunk_t data, chunk_t signature)
{
switch (scheme) {
case SIGN_RSA_EMSA_PKCS1_SHA2_256:
return verify_emsa_pkcs1_signature(this, HASH_SHA256, data, signature);
以下为函数verify_emsa_pkcs1_signature,其负责根据数据生成期望的编码消息,并将其与证书中获取到的编码消息进行对比,以验证签名的有效性。
static bool verify_emsa_pkcs1_signature(private_gmp_rsa_public_key_t *this, hash_algorithm_t algorithm, chunk_t data, chunk_t signature) { chunk_t em_expected, em; bool success = FALSE; /* remove any preceding 0-bytes from signature */ while (signature.len && *(signature.ptr) == 0x00) { signature = chunk_skip(signature, 1); } if (signature.len == 0 || signature.len > this->k) { return FALSE; } /* generate expected signature value */ if (!gmp_emsa_pkcs1_signature_data(algorithm, data, this->k, &em_expected)) { return FALSE; } /* unpack signature */ em = rsavp1(this, signature); success = chunk_equals_const(em_expected, em);
至此,sun网关对接收到的IKE_AUTH报文中的AUTH载荷验证完成,连接建立,并且组织IKE_AUTH回复报文,发送到moon网关。moon网关在接收到之后,对AUTH字段的验证与以上介绍的sun网关的处理流程一致。
文件src/libstrongswan/plugins/unbound/unbound_resolver.c中函数query如下。首先调用unbound库中的函数ub_resolve执行域名解析操作;之后,由函数unbound_response_create_frm_libub_response解析返回的结果信息。
unbound库的代码可在github地址:https://github.com/NLnetLabs/unbound.git中获得。
METHOD(resolver_t, query, resolver_response_t*, private_resolver_t *this, char *domain, rr_class_t rr_class, rr_type_t rr_type) { unbound_response_t *response = NULL; ub_retval = ub_resolve(this->ctx, domain, rr_type, rr_class, &result); if (ub_retval) { DBG1(DBG_LIB, "unbound resolver error: %s", ub_strerror(ub_retval)); ub_resolve_free(result); return NULL; } response = unbound_response_create_frm_libub_response(result); if (!response) { DBG1(DBG_LIB, "unbound resolver failed to create response"); ub_resolve_free(result); return NULL; } ub_resolve_free(result); return (resolver_response_t*)response;
以下为sun网关发送的第一个DNS query报文,这里查询的域名为:moon.strongswan.org,查询的类为INTERNET(0x0001),类型为IPSECKEY(45)。另外,在附加区域中,包含一个EDNS0定义的OPT伪资源记录,其名称为根root,EDNS的版本version为0,标志位DO(DNSSEC OK)为1,表明可接收DNSSEC定义的安全相关RR记录。EDNS的相关初始化可参见文件libunbound/libworker.c中函数setup_qinfo_edns。
另外在DNS报文头部,将标志为CD(Checking disabled)设置为0,表明不接受未认证的数据。
以下为相应的query回复报文。在libunbound库中,文件iterator/iterator.c中的函数process_response负责处理DNS回复消息。之后文件validator/validator.c中的函数val_handle进行验证处理,初始情况下,认证器处于VAL_INIT_STATE状态,子函数processInit负责处理。
以下Answer段中RRSIG资源记录中的签名者字段为strongswan.org,而之前使用函数ub_ctx_add_ta_file添加的trusted anchor文件/etc/ipsec.d/dnssec.keys,包含的是根root的KSK。签名者字段strongswan.org为根域的子域。
Key tag字段值为:9396,用于标识可验证此签名的公钥所对应的DNSKEY资源记录。
由于本端dnssec.keys文件中包含的为根root的KSK,无法对RRSIG中的签名数据进行验证。接下来请求root的DNSKEY资源记录。如下为sun网关发送的第二个DNS query报文,请求类型为DNSKEY,域名为根。与之前的请求不同,这里设置DNS头部的CD位为1,接受非认证的数据。
以下为相应的回复报文,可见其中包含根root的KSK(用于验证ZSK签名)和ZSK(用于验证区数据签名),以及对应的RRSIG签名资源记录。回顾以上在Trust Anchor配置文件/etc/ipsec.d/dnssec.keys中,指定了可信任的key id为32329的KSK,其可用来验证如下具有相同keytag(32329)值的RRSIG资源记录中的签名的正确性。如下所示,签名算法使用RSA,哈希算法使用SHA-256,除此之外,还需验证签名的有效期。此签名覆盖了之前的两个DNSKEY资源记录。
完整的验证过程可参考文件validator/val_sigcrypt.c中的dnskey_verify_rrset_sig函数。首先根据哈希算法SHA-256对DNSKEY资源记录数据进行哈希计算,之后,使用Openssl函数EVP_VerifyFinal根据签名数据和公钥(dnssec.keys),验证DNSKEY资源记录的完整性。
最后,注意在此报文中包含一个keyid等于43749的DNSKEY资源记录,将用于对随后子域中相应签名的验证。
以上我们获取并验证了根域root(.)中DNSKEY资源记录信息,下一步获取根域中org.子域的代理签名(Delegation Signer)信息。以下为sun网关发送的第3个DNS query报文,请求类型为DS(Delegation Signer):
以下为相应的回复报文,可见获取到了两个DS资源记录,keyid(计算而得,非报文内容)都为0xca93(51859),但是前一个RR使用的摘要算法为SHA-256;而后一个使用的为SHA-1。除此之外,还可见一个RRSIG资源记录,其可由keytag为43749的DNSKEY进行验证,此正是在之前根域中获取的一个DNSKEY记录。
文件validator/validator.c中的函数process_ds_response负责处理此报文。函数val_verify_rrset_entry负责根据根域(.)中的keyid为43749的DNSKEY(ZSK),以及RRSIG签名记录,验证DS资源的完整性,算法为RSA/SHA-256,除使用的DNSKEY不同外,其余流程与以上介绍的验证流程相同。
至此,根域的信息都已经获取并处理完成,得到了两个keyid为0xca93(51859)的可信DS资源记录。
关于DNSKEY资源记录的key id值的计算可参考函数sldns_calc_keytag_raw,在rfc4034中有原理说明,即将所属域名和RDATA字段按照16bit进行累加所得。
接下来,获取资源org.相关的DNSSEC信息。以下为sun网关发送的第4个DNS query报文,请求类型为DNSKEY,域名为org:
以下为相应的回复报文。可见两个DNSKEY资源记录,以及相应的RRSIG签名资源记录。其中第二个DNSKEY资源记录的keyid为51859,正对应于在根域中获取的DS资源记录的keyid值。对此DNSKEY资源记录的验证由函数val_verify_DNSKEY_with_DS完成。这里使用的算法为SHA-256,首先对DNSKEY资源记录数据进行摘要计算;之后将结果与DS资源记录中的摘要数据进行对比,相同的话验证通过。注意此DNSKEY为一个KSK。
最后,使用此DNSKEY验证其RRSIG资源记录的有效性,此RRSIG签名覆盖了之前的两个DS资源记录。另外一个keyid为24285的DNSKEY资源记录,其类型为ZSK,用于之后DS字段的签名验证。
由于我们最终要查找的为moon.strongswan.org.域名的DNSKEY资源记录。接下来sun网关发送的第5个DNS query报文,请求org.区中子域strongswan.org的代理签名资源记录DS(Delegation Signer):
以下为相应的回复报文。其中包含两个key id(此处keyid为报文数据,不同于DNSKEY中的计算所得值)为0x01e1(481)的DS资源记录,前者算法为SHA-1,后者使用算法SHA-256。最后的为RRSIG资源记录,可见此签名数据由keytag为24285的名称为org的DNSKEY所签,此签名覆盖了之前的两个DNSKEY资源记录。
至此,org区中的DNSSEC相关数据都已经得到。
以下为sun网关发送的第6个DNS query报文,请求类型为DNSKEY,域名为strongswan.org:
以下为相应的回复报文。可见其中的两个DNSKEY资源记录,前者经计算的keyid为481,后者keyid为9396。另外是两个RRSIG资源记录,分别对应keytag为481和9396。由keytag为481的RRSIG数据可知,其签名数据由strongswan.org生成,可使用keytag为481的DNSKEY进行验证。
如前所述,在验证签名之前,首先使用org中的keyid为481的DS资源记录,验证keyid为481的DNSKEY的有效性。之后,验证RRSIG中签名数据的有效性。此签名覆盖了之前的两个DNSKEY资源记录。其中keyid为9396的DNSKEY用于验证moon.strongswan.org域中RRSIG签名的公钥。
至此,可对moon.strongswan.org中的资源记录IPSECKEY的签名进行验证,验证成功之后,即得到了一个可信的moon.strongswan.org的公钥。
对于moon网关而言,其使用同样的方法可获得sun.strongswan.org的可信公钥。
以下为解析函数unbound_response_create_frm_libub_response,首先,调用ldns库函数ldns_wire2pkt将unbound库结构ub_result中的answer_packet转换为ldns库结构dns_pkt(结构为:ldns_pkt)。接下来,遍历其中的Answers段中的RR记录,这其中有两个记录:IPSECKEY和RRSIG记录,unbound库结构libub_response的成员qtype和qclass保存着发起query请求时,指定的类型和类。
对于IPSECKEY类型记录,使用函数unbound_rr_create_frm_ldns_rr创建一个private_unbound_rr_t结构,将记录中的内容赋值到此结构中,包括:type/class/ttl/rdata字段。并且将此新建的结构插入到rr_list链表。
unbound_response_t *unbound_response_create_frm_libub_response(struct ub_result *libub_response) { private_unbound_response_t *this = NULL; /* Create RRset */ if (this->query_name_exist && this->has_data) { status = ldns_wire2pkt(&dns_pkt, libub_response->answer_packet, libub_response->answer_len); if (status != LDNS_STATUS_OK) { ... return NULL; } rr_list = linked_list_create(); orig_rr_list = ldns_pkt_answer(dns_pkt); orig_rr_count = ldns_rr_list_rr_count(orig_rr_list); for (i = 0; i < orig_rr_count; i++) { orig_rr = ldns_rr_list_rr(orig_rr_list, i); if (ldns_rr_get_type(orig_rr) == libub_response->qtype && ldns_rr_get_class(orig_rr) == libub_response->qclass) { rr = unbound_rr_create_frm_ldns_rr(orig_rr); if (rr) { rr_list->insert_last(rr_list, rr); } else { DBG1(DBG_LIB, "failed to create RR"); } }
对于RRSIG类型记录,检查其type covered字段的值,此处为IPSECKEY(45),表明其中的签名数据属于当前查询的RRset,此种情况下,同样使用unbound_rr_create_frm_ldns_rr函数创建一个private_unbound_rr_t结构,初始化之后,将其链接到另外的一个rrsig_list链表。
if (ldns_rr_get_type(orig_rr) == LDNS_RR_TYPE_RRSIG) { orig_rdf = ldns_rr_rrsig_typecovered(orig_rr); if (!orig_rdf) { ... } else if (ldns_rdf2native_int16(orig_rdf) == libub_response->qtype) { /* The current RR represent a signature (RRSIG) which belongs to the queried RRset. * => add it to the list of signatures. */ rr = unbound_rr_create_frm_ldns_rr(orig_rr); if (rr) { if (!rrsig_list) { rrsig_list = linked_list_create(); } rrsig_list->insert_last(rrsig_list, rr); } } } }
函数最后,将IPSECKEY类型记录的链表rr_list和RRSIG类型记录的链接rrsig_list,共同初始化一个private_rr_set_t结构,并且赋值给private_unbound_response_t结构成员rr_set,以供之后使用。
/* Create the RRset for which the query was performed.
*/
this->rr_set = rr_set_create(rr_list, rrsig_list);
ldns_pkt_free(dns_pkt);
}
return &this->public;
上节的处理流程返回到ipseckey_cred.c文件中的create_cert_enumerator函数之后,将由返回结构response中取出rrsig结构,进而读取其中的证书有效期,最后初始化一个cert_enumerator_t结构,其中包含RRSIG记录数据,证书有效期等数据。并且,根据返回结构中的rrset(包含IPSECKEY和RRSIG链表)创建一个enumerator_t结构赋值于成员inner。
METHOD(credential_set_t, create_cert_enumerator, enumerator_t*, private_ipseckey_cred_t *this, certificate_type_t cert, key_type_t key, identification_t *id, bool trusted) { rrset = response->get_rr_set(response); rrsig_enum = rrset->create_rrsig_enumerator(rrset); reader = bio_reader_create(rrsig->get_rdata(rrsig)); if (!reader->read_data(reader, 8, &ignore) || !reader->read_uint32(reader, &nAfter) || !reader->read_uint32(reader, &nBefore)) { ... return enumerator_create_empty(); } INIT(e, .public = { .enumerate = enumerator_enumerate_default, .venumerate = _cert_enumerator_enumerate, .destroy = _cert_enumerator_destroy, }, .inner = rrset->create_rr_enumerator(rrset), .response = response, .notBefore = nBefore, .notAfter = nAfter, .identity = id,
以下的函数cert_enumerator_enumerate开始调用以上创建的inner的成员函数enumerate,虽然inner中包含有两个RR,IPSECKEY和RRSIG,但是此处仅处理IPSECKEY类型的RR。
METHOD(enumerator_t, cert_enumerator_enumerate, bool, cert_enumerator_t *this, va_list args) { certificate_t **cert; ipseckey_t *cur_ipseckey; public_key_t *public; rr_t *cur_rr; chunk_t key; VA_ARGS_VGET(args, cert); /* Get the next supported IPSECKEY using the inner enumerator. */ while (this->inner->enumerate(this->inner, &cur_rr)) { cur_ipseckey = ipseckey_create_frm_rr(cur_rr); if (!cur_ipseckey) { ... continue; } if (cur_ipseckey->get_algorithm(cur_ipseckey) != IPSECKEY_ALGORITHM_RSA) { ... continue; } key = cur_ipseckey->get_public_key(cur_ipseckey); public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, BUILD_BLOB_DNSKEY, key, BUILD_END); cur_ipseckey->destroy(cur_ipseckey); if (!public) { ... continue; } DESTROY_IF(this->cert); this->cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_TRUSTED_PUBKEY, BUILD_PUBLIC_KEY, public, BUILD_SUBJECT, this->identity, BUILD_NOT_BEFORE_TIME, this->notBefore, BUILD_NOT_AFTER_TIME, this->notAfter, BUILD_END); public->destroy(public); if (!this->cert) { ... continue; } *cert = this->cert; return TRUE;
如下函数ipseckey_create_frm_rr,解析IPSECKEY类型RR记录,获取RDATA字段中的precedence优先级(10)、gateway类型(3),以及公钥算法(RSA)。最后,将IPSECKEY RR记录中公钥数据读取到private_ipseckey_t结构变量的成员public_key中。
ipseckey_t *ipseckey_create_frm_rr(rr_t *rr) { private_ipseckey_t *this; bio_reader_t *reader = NULL; if (rr->get_type(rr) != RR_TYPE_IPSECKEY) { ... return NULL; } /** Parse the content (RDATA field) of the RR */ reader = bio_reader_create(rr->get_rdata(rr)); if (!reader->read_uint8(reader, &this->precedence) || !reader->read_uint8(reader, &this->gateway_type) || !reader->read_uint8(reader, &this->algorithm)) { ... return NULL; } switch (this->gateway_type) { case IPSECKEY_GW_TP_WR_ENC_DNAME: /* Uncompressed domain name as defined in RFC 1035 chapter 3. TODO: Currently we ignore wire encoded domain names. */ while (reader->read_uint8(reader, &label) && label != 0 && label < 192) { if (!reader->read_data(reader, label, &tmp)) { ... return NULL; } } break; } if (!reader->read_data(reader, reader->remaining(reader), &this->public_key)) { return NULL; } this->public_key = chunk_clone(this->public_key);
之后,函数指针lib->creds->create将依据以上由IPSECKEY记录中读取的公钥数据,创建公钥结构public_key_t。此函数指针的实现为文件src/libstrongswan/plugins/dnskey/dnskey_builder.c中的函数dnskey_public_key_load。其主要功能由函数parse_rsa_public_key完成。
dnskey_public_key_t *dnskey_public_key_load(key_type_t type, va_list args) { chunk_t blob = chunk_empty; while (TRUE) { switch (va_arg(args, builder_part_t)) { case BUILD_BLOB_DNSKEY: blob = va_arg(args, chunk_t); continue; case BUILD_END: break; default: return NULL; } break; } if (!blob.ptr) { return NULL; } switch (type) { case KEY_ANY: return parse_public_key(blob); case KEY_RSA: return parse_rsa_public_key(blob);
如下函数parse_rsa_public_key,其将IPSECKEY记录中的公钥数据拆分成两个部分:Modules和Exponent,拆分算法如下,如果数据中第一个字节不为空,其表示exponent数据的长度;否则,exponent数据的长度为一个16bit的值,高8位为数据中的第二个字节,低8位为数据中的第三个字节。确定exponent数据后,剩余的就为modules数据。最后,调用函数指针lib->creds->create完成创建工作,与上次调用此函数指针不同,此处传入不同的参数。
实际调用的为文件src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c中的函数gmp_rsa_public_key_load。
static dnskey_public_key_t *parse_rsa_public_key(chunk_t blob) { chunk_t n, e; if (blob.ptr[0]) { e.len = blob.ptr[0]; blob = chunk_skip(blob, 1); } else { e.len = blob.ptr[1] * 256 + blob.ptr[2]; blob = chunk_skip(blob, 3); } e.ptr = blob.ptr; if (e.len >= blob.len) { DBG1(DBG_LIB, "RFC 3110 public key blob too short for exponent"); return NULL; } n = chunk_skip(blob, e.len); return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, BUILD_RSA_MODULUS, n, BUILD_RSA_PUB_EXP, e, BUILD_END);
如下为函数gmp_rsa_public_key_load,首先读取modulus和exponent参数数据到n,e缓存中。随后,使用GNU MP库函数mpz_import加载这两个数据。新创建结构private_gmp_rsa_public_key_t的成员k保存了modulus数据的字节表示的长度值。最后,确认exponent的值不为零。
gmp_rsa_public_key_t *gmp_rsa_public_key_load(key_type_t type, va_list args) { private_gmp_rsa_public_key_t *this; chunk_t n, e; n = e = chunk_empty; while (TRUE) { switch (va_arg(args, builder_part_t)) { case BUILD_RSA_MODULUS: n = va_arg(args, chunk_t); continue; case BUILD_RSA_PUB_EXP: e = va_arg(args, chunk_t); continue; ... } mpz_init(this->n); mpz_init(this->e); mpz_import(this->n, n.len, 1, 1, 1, 0, n.ptr); mpz_import(this->e, e.len, 1, 1, 1, 0, e.ptr); this->k = (mpz_sizeinbase(this->n, 2) + 7) / BITS_PER_BYTE; if (!mpz_sgn(this->e)) { return NULL; } return &this->public;
ipseckey_cred.c文件中的函数cert_enumerator_enumerate的最后,函数指针lib->creds->create根据以上创建的public_key_t结构数据,以及对端identity信息,和由RRSIG记录中取得的有效期信息,来创建一个证书结构certificate_t。
实际上调用的是文件libstrongswan/plugins/pubkey/pubkey_cert.c中的函数pubkey_cert_wrap,如下所示,其首先由参数中读取到公钥结构数据和有效期,subject信息;随后由函数pubkey_cert_wrap进行处理。
pubkey_cert_t *pubkey_cert_wrap(certificate_type_t type, va_list args) { public_key_t *key = NULL; identification_t *subject = NULL; time_t notBefore = UNDEFINED_TIME, notAfter = UNDEFINED_TIME; while (TRUE) { switch (va_arg(args, builder_part_t)) { case BUILD_BLOB_ASN1_DER: blob = va_arg(args, chunk_t); continue; case BUILD_PUBLIC_KEY: key = va_arg(args, public_key_t*); continue; case BUILD_NOT_BEFORE_TIME: notBefore = va_arg(args, time_t); continue; case BUILD_NOT_AFTER_TIME: notAfter = va_arg(args, time_t); continue; case BUILD_SUBJECT: subject = va_arg(args, identification_t*); continue; ... } if (key) { return pubkey_cert_create(key, notBefore, notAfter, subject);
函数pubkey_cert_create内容如下,分配private_pubkey_cert_t结构,进行相应的赋值操作,最后,返回private_pubkey_cert_t结构的公共部分public,结构类型为pubkey_cert_t。自此,将IPSECKEY记录中的公钥,封装在了证书结构pubkey_cert_t中。
static pubkey_cert_t *pubkey_cert_create(public_key_t *key, time_t notBefore, time_t notAfter, identification_t *subject) { private_pubkey_cert_t *this; chunk_t fingerprint; INIT(this, .public = { .interface = { ... }, .set_subject = _set_subject, }, .ref = 1, .key = key, .notBefore = notBefore, .notAfter = notAfter, .issuer = identification_create_from_encoding(ID_ANY, chunk_empty), ); if (subject) { this->subject = subject->clone(subject); } return &this->public;
在文件libcharon/sa/ikev2/tasks/ike_auth.c中,函数build_r调用authenticator_t结构的build函数创建AUTH载荷,对于此例子,结构authenticator_t由函数authenticator_create_builder的子函数pubkey_authenticator_create_builder创建。
authenticator_t *authenticator_create_builder(ike_sa_t *ike_sa, auth_cfg_t *cfg,
chunk_t received_nonce, chunk_t sent_nonce, chunk_t received_init, chunk_t sent_init, char reserved[3])
{
switch ((uintptr_t)cfg->get(cfg, AUTH_RULE_AUTH_CLASS))
{
case AUTH_CLASS_PUBKEY:
return (authenticator_t*)pubkey_authenticator_create_builder(ike_sa,
received_nonce, sent_init, reserved);
其build函数位于文件libcharon/sa/ikev2/authenticators/pubkey_authenticator.c中,如下所示,auth载荷数据的生成主要由sign_signature_auth函数完成。
METHOD(authenticator_t, build, status_t, private_pubkey_authenticator_t *this, message_t *message)
{
private_key_t *private;
auth_cfg_t *auth;
id = this->ike_sa->get_my_id(this->ike_sa);
auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
private = lib->credmgr->get_private(lib->credmgr, KEY_ANY, id, auth);
if (this->ike_sa->supports_extension(this->ike_sa, EXT_SIGNATURE_AUTH))
{
status = sign_signature_auth(this, auth, private, id, message);
} else {
status = sign_classic(this, auth, private, id, message);
}
由以下函数sign_signature_auth可见,原始数据主要由两部分组成:本端发送的的IKE_SA_INIT报文数据和接收到的对端的Nonce数据。之后,使用本端的私钥对其进行签名。auth载荷中除包含签名数据外,在开始字段,包含有使用的算法标识,此处为 1.2.840.113549.1.1.11,即sha256WithRSAEncryption。
static status_t sign_signature_auth(private_pubkey_authenticator_t *this, auth_cfg_t *auth, private_key_t *private, identification_t *id, message_t *message) { keymat_v2_t *keymat; signature_params_t *params = NULL; chunk_t octets = chunk_empty, auth_data; keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); schemes = select_signature_schemes(keymat, auth, private); if (keymat->get_auth_octets(keymat, FALSE, this->ike_sa_init, this->nonce, this->ppk, id, this->reserved, &octets, schemes)) { enumerator = array_create_enumerator(schemes); while (enumerator->enumerate(enumerator, ¶ms)) { if (!private->sign(private, params->scheme, params->params, octets, &auth_data) || !build_signature_auth_data(&auth_data, params)) { DBG2(DBG_IKE, "unable to create %N signature for %N key", signature_scheme_names, params->scheme, key_type_names, private->get_type(private)); continue; } add_auth_to_message(message, AUTH_DS, auth_data, FALSE); status = SUCCESS;
strongswan版本: 5.8.1
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。