2.1 介绍
OSGi安全层是一个可选层,也是构成OSGi framework的基础,并且OSGi安全层基于Java2 security架构。另外这个层提供了部署应用和管理应用的基础架构,而这些应用在执行环境中必须受到严格控制。
2.1.1要点
- 细粒度-运行在OSGi Framework下的应用必须有细粒度的控制。
- 可管理-安全层本身不提供应用控制API,而是由生命周期层(Life Cycle层)来代理管理API。
- 可选-安全层是可选的。
2.2 安全层概述
Framework安全层模型基于Java2规范。如果执行安全检测,则必须由Java 2 Security架构([3]Java 2 Security Architecture)来操作,而且此规范假定读者已经熟悉了Java 2 Security。可选情况在2.2.2中详细描述
2.2.1代码认证
OSGi framework能够通过以下方式认证代码:
- 位置认证
- 签名认证
在更高层次上,还定义了管理认证代码单元的授权服务:
- 权限管理服务-基于全路径字符串的权限管理
- 条件权限管理服务-基于条件模型管理权限,能够检测位置和签名。
Jar文件都会有数字签名,详细描述见2.3 Jar文件的数字签名。
2.2.2 安全可选情况
Framework在Java平台上运行,必须提供Java2权限所需的Java Security API,而平台资源受限,这些Java Security API可能只是空实现,因此当Bundle加载或者执行这些API时,也不一定有安全检测。允许空实现的API如下:
- checkPermission–返回时不抛出SecurityException异常.
- checkGuard–返回时不抛出SecurityException异常.
- implies– Returntrue.
2.3 Jar文件的数字签名
在这一节中详细定义了Jar文件如何被数字签名。由于Java不同版本有不一样的Java规范,而且在这些规范中有很多可选或者不明确的定义,故本节也只定义了其中重要的一部分。
数字签名其实是一个安全特性,特征如下:
- 验证签名者
- 确保签名后的内容没有被修改
在OSGi framework中,对jar文件的签名会关联着这个jar,这些联系会用于:
- 在认证的基础上授权给jar文件
- 通过对bundle权限操作来选定这些bundle
例如:操作员在他们设备上赋予使用ACME公司网络的权限,这时如果带有ACME公司签名的bundle部署在操作员设备上,就能使得这些bundle使用ACME公司网络了。同理,能授权一个特定的bundle,只管理带有ACME公司签名的bundle生命周期。
其实签名提供了一种强大的代理模型。它允许操作员赋予一个公司一系列受限的权限,之后这个公司就可以创建JAR文件来使用这些权限了,而且不需要进行任何额外的干预或者通讯。 这种代理模型描述如下图2.1所示:
图2.1代理模型
数字签名基于公钥加密机制。公钥加密机制使用2个有关联的数字作为key,其中一个key为公钥,另外一个key为私钥。公钥是共享的、可被自由传播,而私钥必须要保密。
用私钥加密的消息只能通过公钥才能被正确的验证。这可以用来验证消息的签名者身份(这里假设公钥是可信的,具体细节将在2.3.5证书一节讨论)。
数字签名的处理过程基于Java 2的Jar文件签名。处理数字签名的过程是重复、受限的,而且这个处理过程得到了强化,其目的是为了提高OSGi bundle的互操作性。
2.3.1 JAR文件结构和Manifest
一个Jar文件可以有多个签名信息。每份签名信息必须在Jar文件中存储2条资源信息:
- 签名指令资源文件:和Manifest有相似的结构,采用.SF后缀,这个文件提供了完整的manifest文件摘要。
- PKCS#7资源文件:包含对签名指令文件的数字签名信息。具体参考[11] Public Key Cryptography Standard #7(公钥密码标准)
这些Jar文件的签名资源必须放在META-INF目录下,另外在META-INF目录下的文件采用非常规方法签名。
在jar文件流中,签名资源文件必须跟随在Manifest.mf文件之后、其他资源文件之前。如果不这么做,framework将不会接受签名信息,并且认为bundle是没有被签名的。文件的排列顺序非常重要,这是因为接受Jar文件的方式是文件流而不是缓存区读取,而且在其他资源加载之前,需要加载所有安全信息。这个模型如图2.2
图2.2 JAR中的签名文件
签名指令资源文件包含了manifest资源的摘要信息,这些信息并不是实际的数据。这些摘要信息只是一种对资源文件字节计算的结果,通过这种方式,很难再创建具有相同摘要信息的字节。
Jar的Manifest必须包含一个或者多个实际资源的摘要信息,而且在manifest中存放的是以摘要信息为名称的列表,摘要名称的标记是算法描述加上“-Digest”,例如:SHA1-Digest。OSGi Framework推荐下面几种摘要算法。
- MD5– Message Digest 5,在MD4的基础上改进而来,用来生成128位的哈希值,在参考文献[7]RFC 1321 The MD5 Message-Digest Algorithm.有详细描述
- SHA1-对SHA的一种改进算法,用来生成160位的哈希值,在参考文献有定义:[6]Secure Hash Algorithm 1
哈希值必须采用Base64编码,关于Base64编码定义可以参考参考文献[8] RFC 1421 Privacy Enhancement for Internet Electronic Mail.
例如,下面是一个Manifest的例子:
Manifest-Version: 1.0
Bundle-Name: DisplayManifest
Name: x/A.class
SHA1-Digest: RJpDp+igoJ1kxs8CSFeDtMbMq78=
Name: x/B.class
SHA1-Digest: 3EuIPcx414w2QfFSXSZEBfLgKYA=
图 2.3 JAR文件的签名信息
OSGi的Jar必须对所有的资源文件进行签名,除了META-INF目录下的文件(默认的jar签名工具)。这需要遵守java的jar签名标准,而且没有OSGi jar是部分签名的。OSGi规范只支持完整签名的Bundle,这样做是由于部分签名会破坏package的私有性,因为一个Bundle的所有代码使用了相同的保护域,这会简化安全API的处理。
在嵌套的Jar文件(如Bundle-ClassPath中的Jar)中签名文件会被忽略。这些嵌套Jar文件必须和包含它们的Bundle共享保护域。他们会认为资源文件是存储在外部的Jar文件中。
每份签名信息依据2份资源文件,第1个文件是签名指令文件,这个文件采用.SF后缀结尾。第2个文件的结构类似于Manifest文件,除了开头部分。开头如下:Signature-Version: 1.0 instead of Manifest-Version: 1.0。
和签名文件关联的文件有Manifest资源中的摘要,header头使用算法名称(例如SHA1),后缀为-Digest-Manifest。例如:
Signature-Version: 1.0
SHA1-Digest-Manifest: RJpDp+igoJ1kxs8CSFeDtMbMq78=
MD5-Digest-Manifest: IIsI6HranRNHMY27SK8M5qMunR4=
签名资源也可以包含多个片段,但是,这些片段是被忽略的。
如果签名资源有多个签名者,并且使用了同样的摘要算法,那么可以同时读取出来。但是,对于每一个签名者,必须有单独的签名指令文件,而且不允许在签名者之前共享签名指令文件。
签名指令文件在图2.4中描述(签名者:ACME和DAFFY):
图2.4 Jar中的Manifest,签名指令文件、摘要
2.3.2 Java Jar的文件约束
OSGi Bundle是有效的Jar文件,但是对于Bundle来说还有一些其他的约束。
- Bundle不支持部分签名,除了META-INF文件夹,在Manifest文件中必须包含对所有资源的描述。所有签名字段在META-INF中都会被验证,子文件夹也是。
- 在签名文件中,名称部分会被忽略,只使用Manifest摘要。
2.3.3 有效签名
Bundle能够通过多个签名者的签名。签名信息包含一对一的签名文件,这些签名文件以.SF后缀结尾。PKCS#7资源文件也有相同名字的签名文件但是使用RSA或者DSA后缀。
当一个签名符合下述条件时是有效的:
- 签名文件包含入口资源文件META-INF/MANIFEST.MF
- 完整的Manifest必须包含SHA1或者MD5摘要
- 所有摘要必须匹配manifest
- PCKS#7资源文件是有效签名(签名使用RSA或者DSA后缀),而且用于签名资源文件。
完整的Bundle被有效的签名是必须的,也就是说,如果一个签名是无效的,那么整个Bundle就被视为无签名。
2.3.4 签名算法
有几种不同的算法可以支持数字签名。OSGi Framework实现应该支持以下算法:
- DSA- The Digital Signature Algorithm。这个标准定义在参考文献[9]DSA。这是美国政府的数字签名标准,以.DSA为后缀。
- RSA-Rivest, Shamir and Adleman。公开密钥算法很流行,定义在参考文献[10]RSA。以.RSA作为后缀。
- RSA和DSA签名文件的格式采用PCKS#7标准,定义在参考文献[11]Public Key Cryptography Standard #7。PKCS#7标准提供了利用算法加密信息进行验证的方法,验证算法使用公开密钥算法,公开密钥算法验证如下信息:
- 数字签名匹配签名指令文件
- 签名通过私钥创建与证书关联。
完整的签名结构参考图2.4
2.3.5 证书
证书是签名文件的总称,包含名称和公钥信息,并且证书有多种格式。OSGi Jar签名基于X.509证书标准。
这个标准已经形成多年,目前是OSI组织标准的一部分,X.509定义在参考文献[2]
X.509 Certificates。
X.509证书包含以下内容:
- 标题名称-标题名称唯一标识证书所有者信息。例如:一个人可能拥有名字、国籍、email地址、组织、部门等信息,这个标识符是一个Distinguished Name(唯一标识名),Distinguished Name的定义在2.3.6。
- 认证机构名称-发出证书的认证机构名称是一个唯一标识名(Distinguished Name)
- 证书扩展-证书也可以包含图片、指纹编码信息、护照号码等其他信息
- 公钥信息-公钥用于加密,而私钥用于解密。公钥可以自由发布,而私钥必须得保密。公钥信息置顶了加密的算法和公钥的标题。
- 有效日期-证书只在特定时间内有效。
- 认证机构数字签名-认证机构标识第1个元素增加证书可信度,接受证书可以对照签名来检查证书,如果接收者信任该机构,意味着信任签发证书的实体。
证书结构如图2.5
图2.5证书结构
证书能够被自由分发,并没有包含任何保密信息,因此,PKCS#7资源包含签名证书。表面上证书不能进行校验,因为证书包含在Bundle内部。入侵者可以轻易的创建自己的证书,而接收者只需要验证证书的发行者是否可信即可。无论如何在证书处于可信之前,证书必须被校验,因此有必要建立一个可信模型。
OSGi标准支持可信模型,但是不需要实现。证书放在一个库中,默认是可信的。然而,将证书放到一个库中,使得证书库过于庞大,特别是在一个开发模型中,一个设备拥有成千上万的证书,对于证书的管理是非常困难的。
解决方案是采用一个证书验证其他证书,通过多次迭代处理,形成一个证书链的方式。所有证书链上的证书都在PKCS#7文件中:如果一个证书可在可信证书库中找到,那么与这个证书相关的证书都是可信的,模型的尺度缩小到了一个合理状态,而且这样做只有很少一部分证书需要管理,这也是浏览器中所用到的证书模型,具体描述见图2.6
图2.6证书扇形展开
规范中没有描述如何对可信证书库进行存取,只是描述了证书库的组装和组织结构。
2.3.6 唯一标识名DN(Distinguished Name)
X.509命名采用Distinguished Name(DN)。DN是一个高度结构化的名称,在一个层次化的命名空间对节点进行验证。DN概念来源于X.500目录服务,这种目录服务预想为时间范围的命名空间并且由PTTs管理。现在DN被用在由操作员设计的局部命名空间标识,例如:验证Bugs的DN名称如图2.7
图2.7命名空间中的国籍、公司和个人
名字空间是逆序遍历,开始部分是不重要的信息,但是却是具体的信息。因此,属性的排序是很重要的,2个具有相同属性但是排序不同的DN是不一样的。
在示例中,根节点搜索中有一个属性c(countryName国家)的值为US,然后搜索c节点中有一个属性o(organizationName组织名称)值为ACME,接着搜索o节点中有一个子节点属性为cn(commonName普通名称)值为“Bugs Bunny”。
树模型在X.500标准的DN中有正式定义,但是实际上很多DN有很多和数据结构没有关系的属性,例如:很多DN在ou(organizationalUnit组织单元)节点还有批注和版权信息。
X.509证书中的DN名称用ASN.1(ISO定义的语言类型)二进制结构表达。但是,DN经常用于交互,有时,系统需要确认对一个证书的使用,或者操作员要根据客户证书对其赋予权限。因此,DN名称需要具有很好的可读性非常重要,而采用ASN.1方式的可读性比较差,本规范只用了在RFC 2253(具体定义看参考文献[1]RFC 2253Lightweight Directory Access Protocol (v3): UTF-8 String Representation of Distinguished Names)中用在类javax.security.auth.x500.X500Principal中规定的范式来定义的DN字符串。
然而,由于使用一些不常用的类型和特征使得编码的复杂性提高(二进制数据、多值的RDN、多种语言字母、具有特殊匹配规则的属性)。框架的实现必须支持这些特性,但是用户应该避免这样做。因为这些特性现在已经很少使用了。
DN字符串格式如下:
dn ::= rdn ( ’,’ rdn ) *
rdn ::= attribute ( ’+’ attribute ) *
attribute ::= name ’=’ value
name ::= readable | oid
oid ::= number ( ’.’ number ) * //参考1.3.2
readable ::= <see attribute table>
value ::= <escaped string>
分割符前面和后面的空格是被忽略的,值中间有空格是有用的,值中间的多个空格认为是一个空格,值中间不允许出现通配符(’*’ \u002A)。下面的值必须用反斜杠进行转换(\u005C)
comma ’,’ \u002C
plus ’+’ \u002B
double quote ’"’ \u0022
reverse solidus ’\’ \u005C
less then ’<’ \u003C
greater then ’>’ \u003E
semicolon ’;’ \u003B
由于在Java字符串中反斜杠也需要转义,所以用2个反斜杠表示:
DN: cn = Bugs Bunny, o = ACME++, C=US
Canonical form: cn=bugs bunny,o=acme\+\+,c=us
Java String: "cn=Bugs Bunny,o=ACME\\+\\+,c=US"
在DN中可以使用unicode字符编码,字符串必须规范化并用范式表达后才能进行比较:
属性名称(属性类型)实际转义成对象标志(OID,Object IDentifier),OID采用点分表示法,如2.5.4.3表示属性为cn,由于比较规则中使用的是OID别名,因此在框架实现中不能使用属性名称,而且只有下表列出的属性才可以使用:
commonName cn 2.5.4.3 ITU X.520
surname sn 2.5.4.4
countryName c 2.5.4.6
localityName l 2.5.4.7
stateOrProvinceName st 2.5.4.8
organizationName o 2.5.4.10
organizationalUnitName ou 2.5.4.11
title 2.5.4.12
givenName 2.5.4.42
initials 2.5.4.43
generationQualifier 2.5.4.44
dnQualifier 2.5.4.46
streetAddress street RFC 2256
domainComponent dc RFC 1274
userid uid RFC 1274/2798?
emailAddress RFC 2985
serialNumber RFC 2985
DN如下:
2.5.4.3=Bugs Bunny,organizationName=ACME,2.5.4.6=US
等价于:
cn=Bugs Bunny,o=ACME,c=US
属性类型在匹配规则中正式定义,潜在的允许灵活和不灵活2种情况(cases sensitive and case
insensitive)。在上面列表中的都是不灵活匹配,故OSGi DN不依赖灵活匹配的情况。
在X.500标准中支持多值的RDN,但是,他们并不推荐这样用。可以参考文献[13]
Understanding and Deploying LDAP Directory Services。RDN采用加号连接多个值(’+’ \u002B),值之间没有顺序,如下:
cn=Bugs Bunny+dc=x.com+title=Manager,o=ACME,c=US
等价于
dc=x.com+cn=Bugs Bunny+title=Manager, o=ACME, c=US
2.3.7 证书匹配
证书根据主题DN进行匹配。在匹配之前,DN需要转换成范式,转换的算法在类javax.security.auth.x500.X500Principal中详细描述。
DN可以使用通配符进行比较,一个通配符(’*’ \u002A)可以代替所有可能的值。由于DN的结构不同,这种匹配比基于字符串格式的通配符要复杂很多。
一个通配符可以代替RDN中的一个号码或者是一个单独的RDN值。有通配符的DN必须在比较之前进行规范化,这意味着,除了值中的空格,其他空格都要去掉。
通配符的DN格式:
CertificateMatch::= dn-match ( ’;’ dn-match ) *
dn-match ::= ( ’*’ | rdn-match )
( ’,’ rdn-match ) * | ’-’
rdn-match ::= name ’=’ value-match
value-match ::= ’*’ | value-star
value-star ::= < value, requires escaped ’*’ and ’-’ >
最简单的例子是只有一个通配符的情况,可以匹配任意DN。一个通配符可以代替DN中的第1个RDN,第1个RDN描述了最没有价值的信息,这样匹配的RDN结果有可能为空。
例如:一个带通配符的DN可以和图2.7的ACME节点的所有子节点匹配:
*, o=ACME, c=US
This wildcard DN matches the following DNs:
这个带通配符的DN与下面这些DN匹配:
cn = Bugs Bunny, o = ACME, c = US
ou = Carots, cn=Daffy Duck, o=ACME, c=US
street = 9C\, Avenue St. Drézéry, o=ACME, c=US
dc=www, dc=acme, dc=com, o=ACME, c=US
o=ACME, c=US
与下面的DN不匹配:
street = 9C\, Avenue St. Drézéry, o=ACME, c=FR
dc=www, dc=acme, dc=com, c=US
如果在RDN中使用通配符,那么属性值必须用*,这个通配符匹配任意值,但是不能匹配其他的属性值:
cn=*,o=ACME,c=*
可以匹配的DN:
cn=Bugs Bunny,o=ACME,c=US
cn = Daffy Duck , o = ACME , c = US
cn=Road Runner, o=ACME, c=NL
不能匹配的DN:
o=ACME, c=NL
dc=acme.com, cn=Bugs Bunny, o=ACME, c=US
2种格式的通配符可以同时使用,例如,为了匹配来自ACME公司的所有DN,可以使用:
*, o=ACME, c=*
匹配DN需要根据上下文环境,证书是证书链的一部分,每个证书都有一个主题DN和发行机构DN,发行机构DN作为主题DN验证证书使用的第一个证书,因此,DN匹配可扩展到对签名者匹配,分号(’;’ \u003B)用于分隔证书链的DN。
下面的例子可用于匹配Tweety公司(美国公司)发行的证书:
* ; ou=S & V, o=Tweety Inc., c=US
每一个通配符匹配一个空值或者一个证书,但是有时候需要匹配更长的证书链,减号(’-’ \u002D)用来代表匹配零个或者多个证书,而星号只代表匹配一个证书。例如,为了匹配一个证书链中所有由Tweety公司发布的证书,表达式如下:
- ; *, o=Tweety Inc., c=US
这个例子可以匹配所有由Tweety公司发布的可信证书,证书可信是由于Framework定义实现了。
2.4 权限
OSGi Framework使用Java2权限来做Bundle安全,而每个Bundle都关联一组权限。在运行期,权限查询通过请求Security Manager,如果Framework使用延迟条件,那么需要install自己的Security Manager,否则Framework可以使用任意的Security Manager。
Bunlde权限的管理通过Conditional Permission Admin、Permission Admin或者其他安全代理。
2.4.1默认权限
默认权限是指Framework授予Bunlde除具体行为之外的权限,这些权限是正常操作必须的。例如,每个Bundle需要获取Bundle持久化存储区的read,write,detele权限,完整的默认权限如下:
- 为Bundle持久化存储区授予文件权限(READ,WRITE,DELETE操作)
- org.osgi.framework.*的读属性权限
- Bundle自身的Admin权限(RESOURCE,METADATA,CLASS,CONTEXT操作)
- osgi.eecapability的Capability权限(REQUIRE)
2.4.2权限过滤器
OSGi支持数字权限关联Bundle,例如:能够授予Bundle Admin权限来管理其他Bundle。
这个权限名字使用filter表达式描述,当检查权限时,fliter会评估具体Bundle身份的权限属性。例如,一个Bundle能够从具体位置获得Bundle中的所有service注册信息:
ServicePermission("(location=https://www.acme.com/*)", GET )
这是一个非常强大的模型,由于这个模型允许操作员让一组Bundle密切合作,而且不需要service/package/Bundle的名字空间。另外使用签名或者位置作为目标权限,可以使得权限管理维护量明显降低。个别的Bundle不需要配置:签名者或者位置已经使用有效的分组机制。
filter包含以下的key:
id:Bundle Id,例如:(id=256)
location:Bundle的位置,支持filter通配符字符串,允许指定一组Bundle,例如:(location=https://www.acme.com/download/*)
signer:DN链,具体参考2.3.7证书匹配一节。例:(signer=\*,o=ACME,c=NL)
name:Bundle的名称。支持fliter通配符字符串,允许指定具体的一组Bundle,通过一个简单的符号名来映射Bundle,例如:(name=com.acme.*)
权限的name参数也能使用单一的星号通配符(’*’ \u002A),这样表示所有Bundle都匹配。
2.4.2.1多重签名
一个Bundle可以有多个签名者,这种情况下,签名者可以匹配任意签名者的DN。使用多个签名不仅是属性而且可能有潜在风险,从管理的角度看是有利于使用签名来处理分组,然而,多个签名也能被使用于可信Bundle的恶意管理。
例如,一个可信Bundle签名为T,后续增加了不可信签名部分U。授予Bundle T和U的权限,通常是必须的。于是,如果权限关联签名U,也就允许Bundle通过U来管理,而且U会意外获得管理可信Bunlde的权限。例如,U能够start和stop这个可信Bundle,于是多个签名也带来了意想不到的问题:Bundle管理要仔细,但是Admin条件权限能够防止这种条件下的潜在风险。
2.5 修改记录
新增默认Capability权限
2.6 参考文献
[1]RFC 2253Lightweight Directory Access Protocol (v3): UTF-8 String Representation of Distinguished Names
http://www.ietf.org/rfc/rfc2253.txt
[2]X.509 Certificates
http://www.ietf.org/rfc/rfc2459.txt
[3]Java 2 Security Architecture
Version 1.2, Sun Microsystems, March 2002
[4]The Java 2 Package Versioning Specification
http://docs.oracle.com/javase/1.4.2/docs/guide/versioning/index.html
[5]Manifest Format
http://docs.oracle.com/javase/1.4.2/docs/guide/jar/jar.html#JAR Manifest
[6]Secure Hash Algorithm 1
http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
[7]RFC 1321 The MD5 Message-Digest Algorithm
http://www.ietf.org/rfc/rfc1321.txt
[8]RFC 1421 Privacy Enhancement for Internet Electronic Mail
http://www.ietf.org/rfc/rfc1421.txt
[9]DSA
http://www.itl.nist.gov/fipspubs/fip186.htm
[10]RSA
http://www.ietf.org/rfc/rfc2313.txt which is superseded by
http://www.ietf.org/rfc/rfc2437.txt
OSGi Core Release 5 Page 21
Security Layer Framework API References
[11]Public Key Cryptography Standard #7
http://www.rsasecurity.com/rsalabs/node.asp?id=2129
[12]Unicode Normalization UAX # 15
http://www.unicode.org/reports/tr15/
[13]Understanding and Deploying LDAP Directory Services
ISBN 1-57870-070-1