The OSGi Security Layer is an optional layer that underlies the OSGi framework. The layer is based on the Java security architecture. It provides the infrastructure to deploy and manage applications that must run in fine-grained controlled environments.
-
Fine-grained - The control of applications running in an OSGi Framework must allow for detailed control of those applications.
-
Manageable - The security layer itself does not define an API to control the applications. The management of the security layer is left to the life cycle layer.
-
Optional - The security layer is optional.
The Framework security model is based on the Java specification. If security checks are performed, they must be done according to [3] Java Security Architecture. It is assumed that the reader is familiar with this specification. The security layer is optional, see Optional Security.
The OSGi framework can authenticate code in the following ways:
-
By location
-
By signer
At higher layers there are defined services that can manage the permissions that are associated with the authenticated unit of code. These services are:
-
Permission Admin service - Manages the permission based on full location strings.
-
Conditional Permission Admin service - Manages the permissions based on a comprehensive conditional model, where the conditions can test for location or signer.
For signing, this requires the JAR files to be signed; this is described in Digitally Signed JAR Files.
The Java platform on which the Framework runs must provide the Java Security APIs necessary for Java permissions. On resource-constrained platforms, these Java Security APIs may be stubs that allow the bundle classes to be loaded and executed, but the stubs never actually perform the security checks. The behavior of these stubs must be as follows:
-
checkPermission
- Return without throwing aSecurityException
. -
checkGuard
- Return without throwing aSecurityException
. -
implies
- Returntrue
.
This behavior allows code to run as if all bundles have
AllPermission
.
This section defines in detail how JAR files must be signed. This section therefore overlaps with the different JAR file specifications that are part of the different versions of Java. The reason for this duplication is that there are many aspects left as optional or not well-defined in these specifications. A reference was therefore insufficient.
Digitally signing is a security feature that verifies the following:
-
Authenticates the signer
-
Ensures that the content has not been modified after it was signed by the principal.
In an OSGi Framework, the principals that signed a JAR become associated with that JAR. This association is then used to:
-
Grant permissions to a JAR based on the authenticated principal
-
Target a set of bundles by principal for a permission to operate on or with those bundles
For example, an Operator can grant the ACME company the right to use networking on their devices. The ACME company can then use networking in every bundle they digitally sign and deploy on the Operator's device. Also, a specific bundle can be granted permission to only manage the life cycle of bundles that are signed by the ACME company.
Signing provides a powerful delegation model. It allows an Operator to grant a restricted set of permissions to a company, after which the company can create JARs that can use those permissions, without requiring any intervention of, or communication with, the Operator for each particular JAR. This delegation model is shown graphically in Figure 2.1.
Digital signing is based on public key cryptography. Public key cryptography uses a system where there are two mathematically related keys: a public and a private key. The public key is shared with the world and can be dispersed freely, usually in the form of a certificate. The private key must be kept a secret.
Messages signed with the private key can only be verified correctly with the public key. This can be used to authenticate the signer of a message (assuming the public key is trusted, this is discussed in Certificates).
The digital signing process used is based on Java JAR signing. The process of signing is repeated, restricted and augmented here to improve the interoperability of OSGi bundles.
A JAR can be signed by multiple signers. Each signer must store two resources in the JAR file. These resources are:
-
A signature instruction resource that has a similar format like the Manifest. It must have a
.SF
extension. This file provides digests for the complete manifest file. -
A PKCS#7 resource that contains the digital signature of the signature instruction resource. See [10] Public Key Cryptography Standard #7 for information about its format.
These JAR file signing resources must be placed in the META-INF
directory. For signing, the META-INF directory is special because files
in there are not signed in the normal way. These signing resources must
come directly after the MANIFEST.MF
file, and before
any other resources in a JAR stream. If this is not
the case, then a Framework should not accept the signatures and must
treat the bundle as unsigned. This ordering is important because it
allows the receiver of the JAR file to stream the contents without
buffering. All the security information is available before any
resources are loaded. This model is shown in Figure 2.2.
The signature instruction resource contains digests of the Manifest resource, not the actual resource data itself. A digest is a one way function that computes a value from the bytes of a resource in such a way that it is very difficult to create a set of bytes that matches that digest value.
The JAR Manifest must therefore contain one or more digests of the
actual resources. These digests must be placed in their name section of
the manifest. The name of the digest header is constructed with its
algorithm followed by -Digest
. An example is the
SHA-256-Digest
. It is recommended that OSGi Framework
implementations support the following digest algorithms.
-
SHA-1 - Delivers a 160 bit hash. It is defined in [6] Secure Hash Standard.
-
SHA-256 - Delivers a 256 bit hash. It is defined in [6] Secure Hash Standard.
The hash must be encoded with a Base 64 encoding. Base 64 encoding is defined in [7] RFC 1421 Privacy Enhancement for Internet Electronic Mail.
For example, a manifest could look like:
Manifest-Version: 1.0
Bundle-Name: DisplayManifest
↵
Name: x/A.class
SHA-256-Digest: 7CCToQk6yvRusxNl0uSwrv37UY/fdz6aHou29mbswsM=
↵
Name: x/B.class
SHA-256-Digest: C+0CG/cy13WD2sq8dRZm+dNWAHIjm4RQmUVeLLv7DVU=
↵
Graphically this looks like Figure 2.3.
OSGi JARs must be signed by one or more signers that sign all
resources except the ones in the META-INF
directory; the
default behavior of the jarsigner tool. This is a restriction with
respect to standard Java JAR signing; there is no partial signing for an
OSGi JAR. The OSGi specification only supports fully signed bundles. The
reason for this restriction is because partially signing can break the
protection of private packages. It also simplifies the security API
because all code of a bundle is using the same protection domain.
Signature files in nested JAR files (For example JARs on the
Bundle-ClassPath
) must be ignored. These nested JAR files
must share the same protection domain as their containing bundle. They
must be treated as if their resources were stored directly in the outer
JAR.
Each signature is based on two resources. The first file is the
signature instruction file; this file must have a file name with an
extension .SF
. A signature file has the same syntax as the
manifest, except that it starts with Signature-Version: 1.0
instead of Manifest-Version: 1.0
.
The only relevant part of the signature resource is the digest of
the Manifest resource. The name of the header must be the name algorithm
(e.g. SHA-256
) followed by -Digest-Manifest
.
For example:
Signature-Version: 1.0
SHA-256-Digest-Manifest: HmK7445BA7n5UYYI9xZKfSdMAPci44Jn7ZcmoyoiWoM=
The signature resource can contain name sections as well. However, these name sections should be ignored.
If there are multiple signers, then their signature instruction resources can be identical if they use the same digest algorithms. However, each signer must still have its own signature instruction file. That is, it is not allowed to share the signature resource between signers.
The indirection of the signature instruction files digests is
depicted in Figure 2.4 for two signers:
ACME
and DAFFY
.
OSGi bundles are always valid JAR files. However, there are a few restrictions that apply to bundles that do not apply to JAR files.
-
Bundles do not support partially signed bundles. The manifest must contain name sections for all resources but should not have entries for resources in the META-INF directory. Signed entries in the META-INF directory must be verified. Sub directories of META-INF must be treated like any other JAR directory.
-
The name sections in the signature files are ignored. Only the Manifest digest is used.
A bundle can be signed with a signature by multiple signers. A signature contains a pair of a signature file, with a SF extension and a PKCS#7 resource that has the same name as the signature file but with either an RSA or DSA extension.
Such a signature is valid when:
-
The signature file has an entry for the META-INF/MANIFEST.MF resource.
-
The manifest entry must contain a SHA-256 and/or SHA-1 digest for the complete manifest.
-
All listed digests match the manifest.
-
The PKCS#7 resource is a valid signature (either signed using RSA or DSA as indicated by the extension) for the signature resource.
For a complete bundle to be validly signed it is necessary that all signatures are valid. That is, if one of the signatures is invalid, the whole bundle must be treated as unsigned.
Several different available algorithms can perform digital signing. OSGi Framework implementations should support the following algorithms:
-
DSA - The Digital Signature Algorithm. This standard is defined in [8] DSA. This is a USA government standard for Digital Signature Standard. The signature resource name must have an extension of
.DSA
. -
RSA - Rivest, Shamir and Adleman. A public key algorithm that is very popular. It is defined in [9] RSA. The extension of the signature resource name must be
.RSA
.
The signature files for RSA and DSA are stored in a PKCS#7 format. This is a format that has a structure as defined in [10] Public Key Cryptography Standard #7. The PKCS#7 standard provides access to the algorithm specific signing information as well as the certificate with the public key of the signer. The verification algorithm uses the public key to verify that:
-
The digital signature matches the signature instruction resource.
-
The signature was created with the private key associated with the certificate.
The complete signing structure is shown in Figure 2.4.
A certificate is a general term for a signed document containing a name and public key information. Such a certificate can take many forms but the OSGi JAR signing is based on the X.509 certificate format. It has been around for many years and is part of the OSI group of standards. X.509 is defined in [2] X.509 Certificates.
An X.509 certificate contains the following elements:
-
Subject Name - The subject name is a unique identifier for the object being certified. In the case of a person this might include the name, nationality and e-mail address, the organization, and the department within that organization. This identifier is a Distinguished Name, which is defined in Distinguished Names.
-
Issuer Name - The Issuer name is a Distinguished Name for the principal that signed this certificate.
-
Certificate Extensions - A certificate can also include pictures, codification of fingerprints, passport number, and other extensions.
-
Public Key Information - A public key can be used with an encryption technique that requires its private counterpart to decrypt, and vice versa. The public key can be shared freely, the private key must be kept secret. The public key information specifies an algorithm identifier (such as DSA or RSA) and the subject's public key.
-
Validity - A Certificate can be valid for only a limited time.
-
Certifying Authority Signature - The Certificate Authority signs the first elements and thereby adds credibility to the certificate. The receiver of a certificate can check the signature against a set of trusted certifying authorities. If the receiver trusts that certifying authority, it can trust the statement that the certificate makes.
The structure of a certificate is depicted in Figure 2.5.
Certificates can be freely dispersed; they do not contain any secret information. Therefore, the PKCS#7 resource contains the signing certificate. It cannot be trusted at face value because the certificate is carried in the bundle itself. A perpetrator can easily create its own certificate with any content. The receiver can only verify that the certificate was signed by the owner of the public key (the issuer) and that it has not been tampered with. However, before the statement in the certificate can be trusted, it is necessary to authenticate the certificate itself. It is therefore necessary to establish a trust model.
One trust model, supported but not required by the OSGi specifications, is placing the signing certificate in a repository. Any certificate in this repository is treated as trusted by default. However, placing all possible certificates in this repository does not scale well. In an open model, a device would have to contain hundreds of thousands of certificates. The management of the certificates could easily become overwhelming.
The solution is to sign a certificate by another certificate, and this process can be repeated several times. This delegation process forms a chain of certificates. All certificates for this chain are carried in the PKCS#7 file: if one of those certificates can be found in the trusted repository, the other dependent ones can be trusted, on the condition that all the certificates are valid. This model scales very well because only a few certificates of trusted signers need to be maintained. This is the model used in web browsers, as depicted in Figure 2.6.
This specification does not specify access to the trusted repository. It is implementation specific how this repository is populated and maintained.
An X.509 name is a Distinguished Name (DN). A DN is a highly structured name, officially identifying a node in an hierarchical namespace. The DN concept was developed for the X.500 directory service which envisioned a world wide namespace managed by PTTs. Today, the DN is used as an identifier in a local namespace, as in a namespace designed by an Operator. For example, given a namespace that looks like Figure 2.7, the DN identifying Bugs looks like:
cn=Bug,o=ACME,c=US
The traversal of the namespace is reversed from the order in the DN, the first part specifies the least significant but most specific part. That is, the order of the attribute assertions is significant. Two DNs with the same attributes but different order are different DNs.
In the example, a node is searched in the root that has an
attribute c
(countryName
) with a value that is
US
. This node is searched for a child that has an attribute
o
(organizationName
) with a value of
ACME
. And the ACME node is searched for a child node with
an attribute cn
(commonName
) that has a value
"Bugs Bunny"
.
The tree based model is the official definition of a DN from the
X.500 standards. However, in practice today, many DNs contain attributes
that have no relation to a tree. For example, many DNs contain comments
and copyrights in the ou
(organizationalUnit
)
attribute.
The DN from an X.509 certificate is expressed in a binary
structure defined by ASN.1 (a type language defined by ISO). However,
the Distinguished Name is often used in interaction with humans.
Sometimes, users of a system have to acknowledge the use of a
certificate or an employee of an Operator must grant permissions based
on a Distinguished Name of a customer. It is therefore paramount that
the Distinguished Name has a good human readable string representation.
The expressiveness of the ASN.1 type language makes this non-trivial.
This specification only uses DN strings as defined in [1] RFC 2253Lightweight Directory Access Protocol (v3): UTF-8 String Representation of Distinguished Names with a number of extensions that are specified by
the javax.security.auth.x500.X500Principal
class in
CANONICAL
form.
However, the complexity of the encoding/decoding is caused by the use of rarely used types and features (binary data, multi-valued RDNs, foreign alphabets, and attributes that have special matching rules). These features must be supported by a compliant implementation but should be avoided by users. In practice, these features are rarely used today.
The format of a string DN is as follows:
dn ::= rdn ( ',' rdn ) *
rdn ::= attribute ( '+' attribute ) *
attribute ::= name '=' value
name ::= readable | oid
oid ::= number ( '.' number ) * // See 1.3.2
readable ::= <see attribute table>
value ::= <escaped string>
Spaces before and after the separators are ignored, spaces inside
a value are significant but multiple embedded spaces are collapsed into
a single space. Wildcard asterisks ('*' \u002A
) are not
allowed in a value part. The following characters must be escaped with a
reverse solidus ('\' \u005C
):
comma ',' \u002C
plus '+' \u002B
double quote '"' \u0022
reverse solidus '\' \u005C
less then '<' \u003C
greater then '>' \u003E
semicolon ';' \u003B
Reverse solidi ('\' \u005C
) must already be escaped
in Java strings, requiring 2 reverse solidi in Java source code. For
example:
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"
The full Unicode character set is available and can be used in DNs. String objects must be normalized and put in canonical form before being compared.
DN: cn = Bugs Bunny, o = Ð Þ, C=US
Canonical form: cn=bugs bunny,o=ð þ,c=us
Java String: "cn = Bugs Bunny, o = Ð Þ, C=US"
The names of attributes (attributes types as they are also called)
are actually translated into an Object IDentifier (OID). An OID is a
dotted decimal number, like 2.5.4.3
for the cn
(commonName
) attribute name. It is therefore not possible
to use any attribute name because the implementation must know the
aliasing as well as the comparison rules. Therefore only the attributes
that are listed in the following table are allowed (in short or long
form):
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
The following DN:
2.5.4.3=Bugs Bunny,organizationName=ACME,2.5.4.6=US
Is therefore identical to:
cn=Bugs Bunny,o=ACME,c=US
The attribute types officially define a matching rule, potentially allowing cases sensitive and case insensitive. The attributes in the previous list all match case insensitive. Therefore, an OSGi DN must not depend on case sensitivity.
The X.500 standard supports multi-valued RDNs, however, their use
is not recommended. See [12] Understanding and Deploying LDAP Directory Services for the rationale of
this recommendation. Multi-valued RDNs separate their constituents with
a plus sign ('+' \u002B
). Their order is not significant.
For example:
cn=Bugs Bunny+dc=x.com+title=Manager,o=ACME,c=US
Which is the same as
dc=x.com+cn=Bugs Bunny+title=Manager, o=ACME,c=US
Certificates are matched by their Subject DN. Before matching,
DNs, they must first be put in canonical form according to the algorithm
specified in javax.security.auth.x500.X500Principal
.
DNs can also be compared using wildcards. A wildcard asterisk
('*' \u002A
) replaces all possible values. Due to the
structure of the DN, the comparison is more complicated than
string-based wildcard matching.
A wildcard can stand for a number of RDNs, or the value of a single RDN. DNs with a wildcard must be canonicalized before they are compared. This means, among other things, that spaces must be ignored, except in values.
The format of a wildcard DN match is:
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'-' >
The most simple case is a single wildcard; it must match any DN. A wildcard can also replace the first list of RDNs of a DN. The first RDNs are the least significant. Such lists of matched RDNs can be empty.
For example, a DN with a wildcard that matches all nodes
descendant from the ACME
node in Figure 2.7 on page , looks like:
*, o=ACME, c=US
This wildcard DN matches the following DNs:
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
The following DNs must not match:
street = 9C\, Avenue St. Drézéry, o=ACME,c=FR
dc=www, dc=acme, dc=com, c=US
If a wildcard is used for a value
of an RDN, the
value must be exactly *
. The wildcard must match any value,
and no substring matching must be done. For example:
cn=*,o=ACME,c=*
This DN with wildcard must match the following DNs:
cn=Bugs Bunny,o=ACME,c=US
cn = Daffy Duck , o = ACME , c = US
cn=Road Runner, o=ACME, c=NL
But not:
o=ACME, c=NL
dc=acme.com, cn=Bugs Bunny, o=ACME, c=US
Both forms of wildcard usage can be combined in a single matching DN. For example, to match any DN that is from the ACME company worldwide, use:
*, o=ACME, c=*
Matching of a DN takes place in the context of a certificate. This
certificate is part of a certificate chain, see
Certificates. Each certificate has a Subject DN and an
Issuer DN. The Issuer DN is the Subject DN used to sign the first
certificate of the chain. DN matching can therefore be extended to match
the signer. The semicolon (';' \u003B
) must be used to
separate DNs in a chain.
The following example matches a certificate signed by Tweety
Inc.
in the US.
* ; ou=S & V, o=Tweety Inc., c=US
The wildcard matches zero or one certificates, however, sometimes
it is necessary to match a longer chain. The minus sign ('-'
\u002D
) represents zero or more certificates, whereas the
asterisk only represents a single certificate. For example, to match a
certificate where the Tweety Inc.
is in the certificate
chain, use the following expression:
- ; *, o=Tweety Inc., c=US
The previous example matched if the Tweety Inc.
certificate was trusted, or was signed by a trusted certificate. Certain
certificates are trusted because they are known by the Framework, how
they are known is implementation-defined.
The OSGi Framework uses Java permissions for securing bundles. Each bundle is associated with a set of permissions. During runtime, the permissions are queried when a permission is requested through the Security Manager. If a Framework uses postponed conditions, then it must install its own security manager, otherwise it can use any Security Manager.
The management of the bundle's permissions is handled through Conditional Permission Admin, Permission Admin, or another security agent.
Implied permissions are permissions that the framework grants a bundle without any specific action. These permissions are necessary for normal operation. For example, each bundle gets permissions to read, write, and delete the bundle persistent storage area. The standard list of implied permissions is as follows:
-
File Permission for the bundle persistent storage area, for the
READ
,WRITE
, andDELETE
actions. -
Property Permission with the
READ
action fororg.osgi.framework.*
. -
Admin Permission with the
RESOURCE
,METADATA
,CLASS
, andCONTEXT
actions for the bundle itself. -
Capability Permission
REQUIRE
for theosgi.ee
namespace. -
Capability Permission
REQUIRE
for theosgi.native
namespace. -
Package Permission
IMPORT
forjava.*
. -
Service Permission
GET
fororg.osgi.service.condition.Condition
OSGi supports a number of permissions that are granted when the target of the permissions is related to a bundle. For example, Admin Permission can grant a bundle the permission to manage other bundles. This is expressed by using a filter expression for the name of the permission. When the permission is checked, the filter is evaluated with specific permission attributes as well as attributes that describe the bundle's identity. For example, a bundle can get permission to get all services registered by bundles coming from a specific location:
ServicePermission("(location=https://www.acme.com/*)",GET )
This provides a very powerful model because it allows operators to let a group of bundles closely collaborate without requiring ad hoc namespaces for services, packages, and bundles. Using the signer or location as the target for a permission, will allow the maintenance of the permission management to be significantly reduced. It is not necessary to configure for individual bundles: the signer or location is effectively used as a grouping mechanism.
The filter can contain the following keys:
-
id - The bundle ID of a bundle. For example:
(id=256)
-
location - The location of a bundle. Filter wildcards for Strings are supported, allowing the value to specify a set of bundles. For example:
(location=https://www.acme.com/download/*)
-
signer - A Distinguished Name chain. See Certificate Matching for more information how Distinguished Names are matched. Wildcards in a DN are not matched according to the filter string rules, but according to the rules defined for a DN chain. The wildcard asterisk (
'*' \u002A
) must be escaped with a reverse solidus ('\' \u005C
) to avoid being interpreted as a filter wildcard. For example:
(signer=\*,o=ACME,c=NL)
-
name - The symbolic name of a bundle. Filter wildcards for Strings are supported allowing the value to specify a set of bundles. A single symbolic name may also map to a set of bundles. For example:
(name=com.acme.*)
The name parameter of the permission can also be a single wildcard
asterisk ('*' \u002A
). In that case all bundles must
match.
A bundle can be signed by multiple signers, in that case the signer will match against any of the signers' DN. Using multiple signers is both a feature as well as it is a possible threat. From a management perspective it is beneficial to be able to use signatures to handle the grouping. However, it could also be used to maliciously manage a trusted bundle.
For example a trusted bundle signed by T
, could
later have a signature added by an untrusted party U
.
This will grant the bundle the permissions of both T and U, which
ordinarily is a desirable feature. However, If the permissions
associated with signer U
also allow the management of
bundles signed by U
, then U
could
unexpectedly gain the permission to manage this trusted bundle. For
example, it could now start
and stop
this
trusted bundle. This unexpected effect of becoming eligible to be
managed should be carefully considered when multiple signers are used.
The deny policies in Conditional Permission Admin can be used to
prevent this case from causing harm.
[1]RFC 2253Lightweight Directory Access Protocol (v3): UTF-8 String Representation of Distinguished Nameshttps://www.ietf.org/rfc/rfc2253.txt
[2]X.509 Certificateshttps://www.ietf.org/rfc/rfc2459.txt
[3]Java Security Architecturehttps://docs.oracle.com/javase/8/docs/technotes/guides/security/spec/security-spec.doc.html
[4]Java Package Versioning Specificationhttps://docs.oracle.com/javase/8/docs/technotes/guides/versioning/index.html
[6]Secure Hash Standardhttps://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
[7]RFC 1421 Privacy Enhancement for Internet Electronic Mailhttps://www.ietf.org/rfc/rfc1421.txt
[9]RSAhttps://www.ietf.org/rfc/rfc2313.txt which is superseded by https://www.ietf.org/rfc/rfc2437.txt
[10]Public Key Cryptography Standard #7https://www.ietf.org/rfc/rfc2315.txt
[11]Unicode Normalization UAX # 15https://www.unicode.org/reports/tr15/