7.10 Key分发系统
为了建立跨服务器、跨项目、跨组织机构的信任关系,Limax集成Key分发系统,在PKIX框架的基础上,实现安全可靠的Key分发。获得的Key可以用来签名,加密,确保信任系统间安全共享信息。
-
7.10.1 设计原则
-
基本原理
1. 服务器维护一个定时生成的随机数masterKey的数组,使用timestamp索引。
2. 客户端生成nonce,连同信任域名字group,发送给服务器。
3. 服务器执行key = Hash(masterKey, nonce, group)。timestamp和key发还客户端。
4. 客户端组合timestamp,nonce,group形成keyIdent。
5. 客户端使用key签名或者加密消息,连同keyIdent发送给接收客户端。
6. 接收客户端使用keyIdent向服务器请求对应的key,用来验证或者解密消息。
-
信任关系设计
1. 通过约定证书签署方式,建立信任关系。
2. 用KDSName约定Key分发系统名,必须使用有效的域名。
3. 服务器证书的Subject中的RDN CommonName指定了KDSName,其它RDN不作约定。
4. 同一Key分发系统中的所有服务器证书必须由同一CA签署。
5. 客户端证书的SubjectAltName中必须有一个DNS条目,指定KDSName。
6. 客户端证书的SubjectAltName中可以有多个URI条目,指定group。
7. 同一CA签名的具有相同KDSName, group的客户端证书, 在Key分发系统中相互信任。例如, 同一CA为系统A, B, C, 签署客户端证书, 使用相同KDSNAme, 其中A包含group G0, G1, B包含group G0, G1, C包含group G0。在G0内, A,B,C可以互相通讯,G1内A,B可以互相通讯。
8. 签发客户端的CA不一定需要与签发服务器的CA相同。不同CA签署的具有相同KDSName,group的客户端证书没有信任关系(实现上生成Key的时候客户端证书Issuer同时参与HASH计算)。
-
服务器设计要点
1. 同一CA签署的具有同一KDSName的服务器证书,唯一决定了一个Key服务网络。
2. 定期生成新的masterKey,为消息发送客户端分配key,老的masterKey保留一定时间,保证消息接收客户端能够用keyIdent还原之前的key。
3. Key服务网络设计成基于DHT的P2P网络,masterKey数组在服务网络中分发。P2P方式下服务网络规模不受限制,最大限度保证系统健壮性。
-
客户端设计要点
1. nonce用当前时间生成,有机会cache服务器响应,最大限度降低服务器负荷,减少客户端阻塞的机会。
2. nonce的生成精度可调, 可以在签署证书时直接约定, group使用URI表示, 利用URI中fragment部分。例如, G0#10s, 约定了使用group G0时, 以10s为单位生成nonce, 许可的单位为s, m, h, 分别为秒, 分, 小时, 如果不带单位, 默认为毫秒。fragment不存在或者解析失败使用系统默认参数, fragment不作为group一部分。
3. 客户端发起请求前,预先根据自己的证书检查group合法性,避免增加不必要的网络开销。当然,这不妨碍服务器执行检查。
-
-
7.10.2 服务器运营
-
签署证书
这样的签署的服务器证书约定了Key分发系统key.limax-project.org
-
服务器配置(keyserver.xml)
顶层KeyServer元素,8个属性
location:服务器证书的location
passphrase:location的私钥启用密码,实际运营时,不应该填写该属性,而应该在服务器启动时输入。
trustsPath:信任证书路径, location中已经包含了派生该证书的ROOTCA, 如果要信任其它ROOTCA(或者当前ROOTCA即将过期,应该将新的ROOTCA放置在这里), 则需要配置trustsPath, trustsPath可以是一个文件, 也可以是一个目录, 如果是目录, 遍历一层, 获取文件, 文件允许任何证书格式, 包括CER, DER, PKCS7, PKCS12, KeyStore, 从中获取ROOTCA,忽略所有错误。
revocationCheckerOptions:服务器检测对方证书回收状态的选项,可选的值为DISABLE, NO_FALLBACK, ONLY_END_ENTITY, PREFER_CRLS, SOFT_FAIL, 大小写不敏感,后4个参数的解释见java.security.cert.PKIXRevocationChecker.Option,DISABLE具有最高优先级,如果配置了DISABLE,其它配置无意义。
algorithm:指定了HASH算法,HASH算法决定了服务器返回客户端的Key长度,例如SHA1返回160位Key,SHA-256返回256位Key。
master:指定了P2P网络的入口服务器列表,逗号分隔,可以是域名,可以是IP。
keyLifespan:masterKey生存周期,单位为天,超出生存周期的masterKey被服务器删除。
publishPeriod:masterKey发布周期,单位为天,一个新的周期开始时,服务器向网络中发布一个新的masterKey。没有配置该属性的服务器,不参与发布。理论上,一个服务网络内部只能有一个发布者,现实中可以配置多个保证冗余性,详见高级话题。
-
服务器运行
java –jar limax.jar keyserver [path to keyserver.xml]
keyserver.xml目录下会生成一个keyserver.dht文件,该文件定时更新,保存了P2P网络中DHT邻居信息,有助于重启服务器时,快速建立邻居关系,不要删除。
-
-
7.10.3 客户端开发
-
签署证书
这样签署的客户端证书约定了客户端使用Key分发系统key.limax-project.org,具有两个分组prjA和prjB,其中prjA使用10秒的nonce生成精度。
-
编程接口
-
1. limax.key.KeyDesc,Key描述类,提供2个关键方法。
1.1. byte[] getIdent();,获取Key标识。发送端将标识,合并到签名或者加密的消息中发送。接收端使用Key标识向服务器请求对应的Key。
1.2. byte[] getKey(); 获取Key。发送端用Key签名或者加密消息,接收端使用Key验证或者解密。
-
2. KeyException,使用KeyException.Type getType()返回相关异常。
2.1. SubjectAltNameWithoutDNSName,证书签署错误,SubjectAltName中没有包含DNS类型条目。
2.2. SubjectAltNameWithoutURI,证书签署错误,SubjectAltName中没有包含URI条目。
2.3. MalformedKeyIdent,key标识解码异常,通常是传输过程中被篡改,消息不可信。
2.4. UnsupportedURI,当前客户端证书不支持指定的group。
2.5. ServerRekeyed, 服务器无法使用指定timestamp生成Key。通常是timestamp对应的masterKey过期了, 这种情况下, 信息获取方应该向发送方重新请求消息。
-
3. limax.key.KeyAllocator,Key分配器类
3.1. KeyAllocator(SSLContextAllocator sslContextAllocator), 使用sslContextAllocator构造一个Key分配器, cache容量1024。可能抛出异常类型SubjectAltNameWithoutDNSName, SubjectAltNameWithoutURI。
3.2. KeyAllocator(SSLContextAllocator sslContextAllocator, int cacheCapacity), 使用sslContextAllocator构造一个Key分配器, 指定cache容量。可能抛出异常类型SubjectAltNameWithoutDNSName, SubjectAltNameWithoutURI。
3.3. KeyDesc createKeyDesc(java.net.URI uri),信息发送方,请求一个KeyDesc,使用uri指定的group,uri的fragment部分被忽略。可能抛出异常类型UnsupportedURI。如果抛出KeyException之外的异常,通常是网络错误,可以重试。
3.4. KeyDesc createKeyDesc(byte[] ident),信息接收方, 使用收到的Key标识, 请求KeyDesc。可能抛出异常类型MalformedKeyIdent, UnsupportedURI, ServerRekeyed。如果抛出KeyException之外的异常,通常是网络错误,可以重试。
3.5. String getDNSName(),获取客户端证书中签署的DNSName。
3.6. Map<URI,Long> getURIs(),获取客户端证书中签署的group信息,Map的Value为nonce精度,单位为毫秒。
3.7. void setHost(java.lang.String httpsHost), 指定访问的Key服务网络中的部分Key服务器的域名或者IP地址, 如果没有使用该方法设置服务器地址, 则默认使用getDNSName()返回的域名。如果域名解析出多个IP地址, 这些IP地址被一一访问, 直到没有发生网络错误, 或者全部发生错误, 抛出网络异常。
3.8. String getHost(),返回当前正在使用的Key服务器域名或者IP。
-
4. 系统属性
4.1. limax.key.KeyAllocator.DEFAULT_PRECISION,nonce精度,单位毫秒,默认3600000。
4.2. limax.key.KeyAllocator.TIMEOUT,网络访问超时,单位毫秒,默认3000。
-
-
应用实现要点
1. 如果服务提供者提供了新的ROOTCA,通过SSLContextAllocator.addTrust加入。
2. 根据服务提供者的建议决定是否需要SSLContextAllocator.setRecovationCheckerOptions。
3. 根据服务提供者的提供的配置决定KeyAllocator.setHost。
4. KeyAllocator.createKeyDesc,正确进行异常分类,KeyException类型属于不可恢复异常,可能需要检查证书配置,或者请求消息发送方重发。其它Exception属于超时之类的网络异常(这样的异常应该非常少, 详见高级话题), 应该优先考虑重试操作,唯一例外是证书过期导致连接被reset,需要确认日志中的“Certificate renew fail”警告。
5. 应该为消息设计自己的expire机制,masterKey寿命仅仅应该理解为expire的最大值。
-