config setup
conn %default
ikelifetime=60m
keylife=20m
rekeymargin=3m
keyingtries=1
keyexchange=ikev1
conn rw
left=PH_IP_MOON
leftcert=moonCert.pem
leftid=@moon.strongswan.org
leftsubnet=10.1.0.0/16
leftfirewall=yes
# right=%any
right=PH_IP_SUN
rightid=@sun.strongswan.org
rightsubnet=10.1.0.0/16
auto=add
Responder:
config setup
conn %default
ikelifetime=60m
keylife=20m
rekeymargin=3m
keyingtries=1
keyexchange=ikev1
conn home
left=PH_IP_SUN
leftcert=sunCert.pem
leftid=@sun.strongswan.org
leftsubnet=10.1.0.0/16
leftfirewall=yes
right=PH_IP_MOON
rightid=@moon.strongswan.org
rightsubnet=10.1.0.0/16
auto=add
与证书认证相关的报文开始于第一条报文,主动方需要获取认证类型,在main_mode.c的build_i中会get_auth_mode,
最终或调用到credential_manager.c里的get_private,在这里,将会确定一次信任链moon->strongswanCA,这个时候
会把moon->CA,CA->CA的关系存到cert_cache.c中。
后面跳到第四条报文,由接收起方发出如下载荷:
接收方 KE No
CERTREQ NAT-D NAT-D -----> 发起方
接收方在isakmp_cert_pre.c的build_certreqs里,其代码如下:
/**
* Build certificate requests
*/
static void build_certreqs(private_isakmp_cert_pre_t *this, message_t *message)
{
enumerator_t *enumerator;
ike_cfg_t *ike_cfg;
peer_cfg_t *peer_cfg;
certificate_t *cert;
auth_cfg_t *auth;
ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
if (!ike_cfg->send_certreq(ike_cfg))
{
return;
}
/* check if we require a specific CA for that peer */
peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
if (peer_cfg)
{ /*默认的配置里,我们没有指定新的CA,因此不会走到add_certreqs的流程 */
enumerator = peer_cfg->create_auth_cfg_enumerator(peer_cfg, FALSE);
if (enumerator->enumerate(enumerator, &auth))
{
add_certreqs(this, auth, message);
}
enumerator->destroy(enumerator);
}
if (!message->get_payload(message, PLV1_CERTREQ))
{
/* otherwise add all trusted CA certificates */
enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
CERT_ANY, KEY_ANY, NULL, TRUE);
while (enumerator->enumerate(enumerator, &cert))
{ /* 遍历了lib->credmgr的sets链表,去找所有证书,在add_certreq里会判断,只会添加CA证书,并且加入的是证书的subject */
add_certreq(this, message, cert);
}
enumerator->destroy(enumerator);
}
}
然后打包消息发送出去。
发起方在收到消息后,需要解析这个报文,在isakmp_cert_pre.c中,调用process_certreqs进行处理
static void process_certreqs(private_isakmp_cert_pre_t *this, message_t *message)
{
enumerator_t *enumerator;
payload_t *payload;
auth_cfg_t *auth;
auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);/* 注意,这里找到ike_sa->my_auth,注意,此时auth->entries里面的元素是空的 */
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
switch (payload->get_type(payload))
{
case PLV1_CERTREQ:
{
certificate_t *cert;
this->ike_sa->set_condition(this->ike_sa,
COND_CERTREQ_SEEN, TRUE);
cert = find_certificate(this, (certreq_payload_t*)payload); /* 根据证书载荷找到CA证书 */
if (cert)
{ /* 找到了,那么把CA证书保存到ike_sa->my_auth->entries中 */
auth->add(auth, AUTH_RULE_CA_CERT, cert);
}
break;
}
default:
break;
}
}
enumerator->destroy(enumerator);
}
后面进入到主动方打包第五条报文的阶段
第五条报文 ID CERT SIG CERTREQ N(INITIAL_CONTACT) -->
同样的,主动方需要先build_certreqs,这里和第四条报文一样。后面是先构造sig载荷,然后再构造cert载荷。
填充SIG载荷在main_mode.c的build_i的流程中
case MM_KE:
{
id_payload_t *id_payload;
identification_t *id;
id = this->ph1->get_id(this->ph1, this->peer_cfg, TRUE);
if (!id)
{
DBG1(DBG_CFG, "own identity not known");
return send_notify(this, INVALID_ID_INFORMATION);
}
this->ike_sa->set_my_id(this->ike_sa, id->clone(id));/* 保存自己的ID moon.strongswan.cn 到ike_sa->my_id中,之前my_id为空 */
id_payload = id_payload_create_from_identification(PLV1_ID, id);
message->add_payload(message, &id_payload->payload_interface);
if (!this->ph1->build_auth(this->ph1, this->method, message, /* 构造SIG载荷 */
id_payload->get_encoded(id_payload)))
{
return send_notify(this, AUTHENTICATION_FAILED);
}
add_initial_contact(this, message, id);
this->state = MM_AUTH;
return NEED_MORE;
}
如上代码所示,调用phase1.c的build_auth构造SIG,最终调用到pubkey_v1_authenticator.c的build构造。
逻辑上,这里需要找到本地证书的私钥,对hash数据进行签名。因此build时需要先找到私钥,通过如下查找
private = lib->credmgr->get_private(lib->credmgr, this->type, id, auth);
这里查找比较绕,跟进去看一下,调用的是credential_manager.c的get_private去找。
这里的入参type=KEY_RSA,auth是ike_sa->my_auth,id是moon_strongswan.cn
在get_private函数里,auth为真,这一次在创建trust_chain的时候,在匹配issued_by时,会使用之前的cache。
注意,创建完trust_chain后,会把本地证书和CA证书加到ike_sa->my_auth中。
找到private之后就做签名的处理。
签名成功后,会保存auth_cfg,ike_sa->my_auth中会有3个条目,两个是本地CA(相同的条目),一个是本地证书。
然后ike_sa->my_auths里会再存一份ike_sa->my_auth相同的auth_cfg
此后构造cert报文。这里使用my_auth中的本地证书构造报文。
METHOD(credential_manager_t, get_private, private_key_t*,
private_credential_manager_t *this, key_type_t type, identification_t *id,
auth_cfg_t *auth)
{
enumerator_t *enumerator;
certificate_t *cert;
private_key_t *private = NULL;
auth_cfg_t *trustchain;
auth_rule_t rule;
/* check if this is a lookup by key ID, and do it if so */
if (id && id->get_type(id) == ID_KEY_ID) /* M_I_5里type为KEY_RSA,因此不会走到这 */
{
private = get_private_by_keyid(this, type, id);
if (private)
{
return private;
}
}
if (auth)
{ /* M_I_5里auth虽然不为空,首次来的时候,里面只有CA的证书 *//* 在M_I_1中,get_auth_method会走到这里 */
/* try to find a trustchain with one of the configured subject certs */
enumerator = auth->create_enumerator(auth);
while (enumerator->enumerate(enumerator, &rule, &cert))
{
if (rule == AUTH_RULE_SUBJECT_CERT)
{ /* get_auth_method里传下来的auth是配置里的auth,是有本地证书的,*/
private = get_private_by_cert(this, cert, type);
if (private)
{
trustchain = build_trustchain(this, cert, auth);
if (trustchain)
{
auth->merge(auth, trustchain, FALSE);
prefer_cert(auth, cert->get_ref(cert));
trustchain->destroy(trustchain);
break;
}
private->destroy(private);
private = NULL;
}
}
}
enumerator->destroy(enumerator);
if (private)
{
return private;
}
/* M_I_5里,首次找也是没有的 */
/* if none yielded a trustchain, enforce the first configured cert */
cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
if (cert)
{
private = get_private_by_cert(this, cert, type);
if (private)
{
trustchain = build_trustchain(this, cert, auth);
if (trustchain)
{
auth->merge(auth, trustchain, FALSE);
trustchain->destroy(trustchain);
}
return private;
}
}
/* try to build a trust chain for each certificate found */
enumerator = create_cert_enumerator(this, CERT_ANY, type, id, FALSE);
while (enumerator->enumerate(enumerator, &cert))
{ /* 通过ID找到本地证书 */
private = get_private_by_cert(this, cert, type);/* 找私钥 */
if (private)
{ /* 构造证书信任链 */
trustchain = build_trustchain(this, cert, auth);
if (trustchain)
{
auth->merge(auth, trustchain, FALSE);
trustchain->destroy(trustchain);
break;
}
private->destroy(private);
private = NULL;
}
}
enumerator->destroy(enumerator);
}
/* if no valid trustchain was found, fall back to the first usable cert */
if (!private)
{
enumerator = create_cert_enumerator(this, CERT_ANY, type, id, FALSE);
while (enumerator->enumerate(enumerator, &cert))
{
private = get_private_by_cert(this, cert, type);
if (private)
{
if (auth)
{
auth->add(auth, AUTH_RULE_SUBJECT_CERT, cert->get_ref(cert));
}
break;
}
}
enumerator->destroy(enumerator);
}
return private;
}
/**
* find a private key of a given certificate
*/
static private_key_t *get_private_by_cert(private_credential_manager_t *this,
certificate_t *cert, key_type_t type)
{
private_key_t *private = NULL;
identification_t *keyid;
chunk_t chunk;
public_key_t *public;
public = cert->get_public_key(cert);
if (public)
{ /* 这里的私钥,是通过指纹匹配的,指纹之前配置时就生成好的 */
if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &chunk))
{
keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
private = get_private_by_keyid(this, type, keyid);
keyid->destroy(keyid);
}
public->destroy(public);
}
return private;
}
/**
* build a trustchain from subject up to a trust anchor in trusted
*/
static auth_cfg_t *build_trustchain(private_credential_manager_t *this,
certificate_t *subject, auth_cfg_t *auth)
{
certificate_t *issuer, *current;
auth_cfg_t *trustchain;
int pathlen = 0;
bool has_anchor;
trustchain = auth_cfg_create();
has_anchor = auth->get(auth, AUTH_RULE_CA_CERT) != NULL; /* ike_sa的auth传进来的时候已经有本地CA了,配置auth传下来没有 */
current = subject->get_ref(subject);
while (TRUE)
{ /* auth_contans_cacert是要判断auth里是否有CA证书,且CA证书和currrent是同一个证书 */
if (auth_contains_cacert(auth, current)) /* 第一次current为要认证的证书,这里为假,第二次为第一次的签发者,以此类推 */
{
trustchain->add(trustchain, AUTH_RULE_CA_CERT, current);
return trustchain;
}
if (subject == current)
{ /* 第一次是相等的 ,因此会把本地证书加到信任链中 */
trustchain->add(trustchain, AUTH_RULE_SUBJECT_CERT, current);
}
else
{
if (!has_anchor && issued_by(this, current, current, NULL))/* 如果没有指定根CA,那么此时又自认证成功,那么认为成功 */
{ /* If no trust anchor specified, accept any CA */
trustchain->add(trustchain, AUTH_RULE_CA_CERT, current);
return trustchain;
}
trustchain->add(trustchain, AUTH_RULE_IM_CERT, current);
}
if (pathlen++ > MAX_TRUST_PATH_LEN) /* 证书链的层次 */
{
break;
}
issuer = get_issuer_cert(this, current, FALSE, NULL);/* 找本证书的签发者 */
if (!issuer)
{
if (!has_anchor)
{ /* If no trust anchor specified, accept incomplete chains */
return trustchain;
}
break;
}
if (has_anchor && issuer->equals(issuer, current))/* 如果指定了根CA,去比较最终的签发者 */
{
issuer->destroy(issuer);
break;
}
current = issuer;
}
trustchain->destroy(trustchain);
return NULL;
}
接收方收到第五条报文后,先process_certreqs,把找到的本地CA存放到ike_sa->my_auth中。
然后process_certs,
从载荷中取出证书,注意,这时候我们只有一个证书,没有测试过证书链的情况。把证书取出来后,
放到了ike_sa->other_auth中。
其后在main_mode.c的process_r里去验证SIG载荷。
case MM_KE:
{
id_payload_t *id_payload;
identification_t *id;
id_payload = (id_payload_t*)message->get_payload(message, PLV1_ID);
if (!id_payload)
{
DBG1(DBG_IKE, "IDii payload missing");
return send_notify(this, INVALID_PAYLOAD_TYPE);
}
id = id_payload->get_identification(id_payload);
this->ike_sa->set_other_id(this->ike_sa, id);
while (TRUE)
{
DESTROY_IF(this->peer_cfg);
this->peer_cfg = this->ph1->select_config(this->ph1,
this->method, FALSE, id);
if (!this->peer_cfg)
{
return send_notify(this, AUTHENTICATION_FAILED);
}
this->ike_sa->set_peer_cfg(this->ike_sa, this->peer_cfg);
if (this->ph1->verify_auth(this->ph1, this->method, message,
id_payload->get_encoded(id_payload)))
{
break;
}
}
if (!charon->bus->authorize(charon->bus, FALSE))
{
DBG1(DBG_IKE, "Main Mode authorization hook forbids IKE_SA, "
"cancelling");
return send_notify(this, AUTHENTICATION_FAILED);
}
this->state = MM_AUTH;
if (has_notify_errors(this, message))
{
return FAILED;
}
return NEED_MORE;
}
下面的逻辑就是要认证对方发送的SIG载荷。在pubkey_v1_authenticator.c:process中,
需要先获取证书的pubkey,注意,调用如下:
sig = sig_payload->get_hash(sig_payload);
auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, this->type,
id, auth);
while (enumerator->enumerate(enumerator, &public, ¤t_auth))
{
if (public->verify(public, scheme, hash, sig))
{
DBG1(DBG_IKE, "authentication of '%Y' with %N successful",
id, key_type_names, this->type);
status = SUCCESS;
auth->merge(auth, current_auth, FALSE);
auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
break;
}
else
{
DBG1(DBG_IKE, "signature validation failed, looking for another key");
status = FAILED;
}
}
enumerator->destroy(enumerator);
这里需要注意的是credential_manager.c的create_public_enumerator函数,这里创建的enumerator是比较怪的。
说一下逻辑:
先调用public_enumerate
---> trusted_enumerate
第一次,this->candidates为空,创建一个candidates,这个实际是一个enumerator,遍历当前
credential_manager里的sets里的cert。然后找已经信任的证书中是否有这个证书,第一次,自然没有。
this->pretrusted为空。然后遍历candidates。下面分析一下这个candidates的enumerator
从最外层看,candidates是一个nested的enumerator,即它有两层遍历,先遍历外层,再遍历内层。
那么他的外层是什么呢?外层通过create_sets_enumerator来创建。是一个sets的遍历器,看他的遍历函数为
sets_enumerate,这里面又遍历,先遍历exclusive,然后遍历global,再遍历local。在当前的调用中,他的
实际工作就是,先遍历credential_manager下的sets,取出每一个set。
内层的遍历器是通过create_cert构造的,它调用的set的构造器创建,这个enumerator实际最终调用了各个set的
构造器,比如credential_manager.c的sets各自的构造器,又或者auth_wrapper的构造器
如果内部遍历成功了,那么取出对应的数据
给上层调用者,如果不成功,再遍历之前的auth_wrapper构造的local_set,再进行遍历。实际遍历过程中,真正
匹配到的数据在local_set中。
在这个时候,最终从auth_cfg(即ike_sa->other_auth)中获取到了对方的证书。然后开始调用verify_trust_chain
去验证一下这个证书是否可信。
先找到证书的颁发者,由于我们的不是证书链,因此直接找到上级CA,注意,这里在找颁发者时,会保存对应关系到
cert_cach中。然后调用check_certificate来检查证书
是否是有效的,先检查证书和签发者两者的签发日期。
这里调用了validators,此时只有两个,一个是
constraints_validator_t,另一个是
revocation_validator_t,
两个都没有check_lifetime的回调,因此继续往下。
然后又继续使用validator验证,使用的先是revocation_validator,
validate:先oscp又crl,都无法验证,返回true。
然后constraint,这个不太懂,需要再找一下,似乎是证书的一些限制。后面就是用pubkey去verify SIG。
然后在ike_sa->other_auth里填了一堆东西:
$63 = {type = AUTH_RULE_AUTH_CLASS, value = 0x1}
$52 = {type = AUTH_HELPER_SUBJECT_CERT, value = 0x7f2d40016f10}
$53 = {type = AUTH_RULE_RSA_STRENGTH, value = 0x800}
$54 = {type = AUTH_RULE_RSA_STRENGTH, value = 0x800}
$55 = {type = AUTH_RULE_SUBJECT_CERT, value = 0x7f2d40016f10}
$56 = {type = AUTH_RULE_CA_CERT, value = 0x19d5340}
$57 = {type = AUTH_RULE_SIGNATURE_SCHEME, value = 0x5}
$58 = {type = AUTH_RULE_OCSP_VALIDATION, value = 0x1}
$59 = {type = AUTH_RULE_CRL_VALIDATION, value = 0x3}
出去check_constraints,这里又向other_auth里插入了一个, type = AUTH_RULE_IDENTITY, value = 0x7f2d4000bae0
后面拿配置的对方的auth和生成的ike_sa->other_auth进行比较。配置的里面两个条目AUTH_RULE_AUTH_CLASS 0x1,
AUTH_RULE_AUTH_IDENTITY,value是对方的id,两个匹配都是一样的,认为通过,然后save_auth_cfg。
然后把ike_sa->other_auth里的所有东西merge到一个新的auth里,放在了ike_sa->other_auths里。
后面开始构造第六条报文,需要创建SIG和CERT载荷
和发送方一样,不再描述。
发起方收到第六条报文后,验证SIG,也是类似。