英文版本:
http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html
内容
- 简介
- META-INF 目录
- 键值对和分段
- JAR 清单
-
- 概述
- 清单规范
- 主要属性
- 同项属性
- 签名 JAR 文件
-
- 概述
- 签名文件
-
- 签名验证
- Magic属性
- 数字签名
- 清单和签名文件注意事项
- JAR 索引
-
- 概述
- 索引文件规范
- 向后兼容性
- 服务提供商
-
- 概述
- 供应商程序控制文件
- 范例
- API 详细信息
- 另请参阅
简介
JAR文件是一个基于当前流行ZIP文件格式的一种文件格式,用于将多个文件聚合成一个。一个JAR文件本质上是一个zip文件,包含一个可选的META-INF目录。使用命令行jar工具或Java平台的 java.util.jar API可以创建一个JAR文件。JAR文件没有命名限制,它可以是特定平台上的任何合法文件名称。
在许多情况下,JAR文件不只是简单的JAVA类文件和/或资源档案。它们被用来为应用程序和扩展添砖加瓦。META-INF目录(如果存在)被用来存储包和扩展配置数据,包括安全,版本,扩展和服务。
META-INF目录
META-INF 目录下面被Java2平台识别和解释的文件/目录,用来控制应用、扩展、类装载器和服务:
- MANIFEST.MF
清单文件,用于定义扩展和包相关数据
- INDEX.LIST
这个文件通过jar工具的可选项"-i"产生,其中包含了应用程序或扩展中定义的包的位置信息。它是JarIndex 的部分实现,用于加快类装载器装载类的进程。
- x.SF
JAR文件的签名文件。'x' 代表基本文件名称。
- x.DSA
签名块文件与签名文件有着相同的基本文件名称。这个文件储存着相应签名文件的数字签名。
- services/
这个目录存储所有的服务提供商的配置文件。
在我们开始个人配置文件的内容细节之前,需要定义一些格式约定。在大多数情况下,清单文件和签名文件包含的信息代表所谓的受RFC标准启发的“键:值对”。我们也称呼这些组对为标题或属性。
键值对组叫做“分段”,分段之间用换行分隔。
任何形式的二进制数据都表示为Base64。二进制文件所需的附加部分导致行长度超过72字节。二进制的例子有摘要和签名。
实现应支持多达65535字节的头部值。
本文档中所有规格,使用相同的语法,终端符号用等宽字体显示,非终端符号用斜体显示。
清单:
分段: *头 +换行
非空分段: + 头 +换行
换行: CR LF | LF | CR (不跟随 LF)
头: 名称 : 值
名称: 基本字符 * 头字符
值: 空格 * 其他字符 换行 * 延续
延续: 空格 * 其他字符 换行
基本字符: {A-Z} | {a-z} | {0-9}
头字符: 基本字符 | - | _
其他字符: 任何UTF-8字符除了NUL,CR和LF
; 还有:为了阻止零碎文件通过不间断的email发送,头部将不能以这四个字母开头"From"
一个JAR文件清单包括一个主要分段,接着是一个JAR文件条目的分段列表,分段之间用换行分割。主要分段和个别分段都遵循上述指定分段语法。每个分段都拥有自己的指定限制和规则。主要分段包含JAR文件本身的安全性和配置信息以及应用程序和扩展,是这个JAR文件的一部分。 它还定义了适用于个别的清单项的主要属性。这个分段中没有名为“Name”的属性。这个分段由一个空行结束。
个别分段为包含在JAR文件中的包或文件定义变量属性。并非所有的JAR文件中的文件都需要作为项在清单列出,但是所有需要签名的文件必须列出。清单文件本身并不在列出。每个分段必须以“Name”属性名开头,值必须是文件的相对路径,或者引用文档外部数据的绝对URL。
如果这些是同一个文件项的多个独立分段,那么这些分段的属性将会被合并. 如果某一属性在不同的分段中有不同的值,将只认最后一个。
不能理解的属性会被忽略。这些属性可能包含应用程序使用的具体实现信息。
清单规范:
清单文件: 主要分段 换行 *个别分段
主要分段: 版本信息 换行 *主要属性
版本信息: 清单版本 : 版本号
版本号 : 数字+{.数字+}*
主要属性: (任何合法主要属性) 换行
个别分段: 名称 : 值 换行 *同项属性
同项属性: (任何合法同项属性) 换行
换行 : CR LF | LF | CR (不跟随 LF)
数字 : {0-9}
在上面的清单中,主要分段中出现的属性称为主要属性,而出现在个别分段中的属性称为同项属性。某些属性可以同时出现在主要分段和个别分段中,在这种情况下,指定项上同项属性的值会覆盖主要属性的值。这两种类型的属性定义如下:
主要属性
主要属性是出现在清单的主要分段中的属性。它们分为以下不同组别:
- 一般主要属性
- Manifest-Version:
定义清单文件版本,该值是一个合法的版本号,如上述规范所述。
-
- Created-By:
在这个清单文件顶部生成Java实现的版本和厂商的定义。此属性由JAR工具生成。
-
- Signature-Version:
定义JAR文件的签名版本。该值是一个有效的版本号字符串。
-
- Class-Path :
这个属性的值指定这个应用程序或扩展所需扩展或库的相对URL。URLs由一个或多个空格分隔。应用程序或扩展类加载器使用这个属性值来构建其内部搜索路径。
- 独立应用程序的属性
独立应用程序应用此属性捆绑成可执行的JAR文件,可以被java运行环境运行”java -jar x.jar“直接调用。
-
- Main-Class:
这个属性的值定义启动器在启动时需要加载的主应用类的相对路径。该值不能有类的扩展名.class。
- applets的属性定义
捆绑成JAR文件的applet使用此属性定义所需扩展需求,版本和位置信息。(见扩展版本)
-
- Extension-List
该属性表示applet所需扩展。此属性列出的每个扩展名都有一系列的附加属性,applet使用它们去指定applet所需扩展的版本和供应商。
-
- <extension>-Extension-Name :
该属性是扩展的唯一名称。Java Plug-in会比较该属性和已安装扩展清单的Extension-Name属性的值来确定该扩展是否要安装。
-
- <extension>-Specification-Version
该属性指定applet所需的最小扩展规范版本。Java Plug-in会比较该属性和已安装扩展的Specification-Version属性的值来确定该扩展是否要更新到最新版本。
-
- <extension>-Implementation-Version
该属性指定applet所需的最小扩展版本号。Java Plun-in会比较该属性和已安装扩展的Implementation-Version属性值来确定是否需要下载最新实现。
-
- <extension>-Implementation-Vendor-Id
如果applet需要从指定提供商那里得到一个实现的话,该属性用来确认这个扩展实现的提供商。Java Plug-in会比较该属性和已安装的扩展的Implementation-Vendor-Id属性的值。
-
- <extension>-Implementation-URL
该属性指定一个用来获取需求版本未安装的扩展的最新版本的URL。
同项属性
同项属性仅适用清单条目相关的个别JAR文件条目。如果在主要分段中也出现同样的属性,此时,同项属性的值会覆盖主要属性的值,例如,如果JAR文件a.jar中有如下清单内容:
Manifest-Version: 1.0
Created-By: 1.2 (Sun Microsystems Inc.)
Sealed: true
Name: foo/bar/
Sealed: false
这意味着a.jar中所有的包档案都是封闭的,foo.bar除外。
同项属性分为以下几组:
- 文件内容属性定义:
-
- Content-Type :
该属性用来指定一个JAR文件中指定文件的数据的MIME类型和子类型。该值必须是一个type/subtype类型的字符串。例如"image/bmp"是一个带有bmp(表示位图)子类型图像类型。这表示文件将是以位图形式保存的图像。RFC 1521和1522讨论和定义了MIME类型的定义。
- 包版本和封闭信息的属性定义:
-
These are the same set of attributes defined above as main attributes that defines the extension package versioning and sealing information. When used as per-entry attributes, these attributes overwrites the main attributes but only apply to the individual file specified by the manifest entry.
- 对象的属性定义:
-
- Java-Bean:
- 定义指定jar文件条目是否为一个Java Beans对象。该值必须是"true"或"false",忽略大小写。
- 签名属性定义:
- 该属性用于签名和验证的目的。更多细节在这里。
- x-Digest-y:
The name of this attribute specifies the name of the digest algorithm used to compute the digest value for the corresponding jar file entry. The value of this attribute stores the actual digest value. The prefix 'x' specifies the algorithm name and the optional suffix 'y' indicates to which language the digest value should be verified against.- Magic:
- 这是一个可选属性,该属性值是以逗号分割的内容集合的字符串。详细描述在此。
This is an optional attribute that can be used by applications to indicate how verifier should compute the digest value contained in the manifest entry. The value of this attribute is a set of comma separated context specific strings. Detailed description is here.
- 扩展名识别属性定义
该属性用来通过扩展名定义它们的唯一身份。
-
- Extension-Name:
该属性指定在JAR文件中包含的扩展名。该名称必须是扩展的唯一标识,例如主要包名称的扩展名。
- 扩展、包版本和封闭信息的属性
这些属性定义的扩展功能是JAR文件的一部分。这些属性的值适用于所有的JAR文件包,但是可以被同项属性覆盖。
-
- Implementation-Title :
该值是一个定义扩展实现标题的字符串。
-
- Implementation-Version :
该值是一个定义扩展实现版本的字符串。
-
- Implementation-Vendor :
该值是一个定义维护该扩展实现的组织的字符串。
-
- Implementation-Vendor-Id :
该值是一个定义维护该扩展实现的组织ID的字符串。
-
- Implementation-URL :
该值定义扩展实现的下载URL。
-
- Specification-Title :
该值是一个定义扩展规范标题的字符串。
-
- Specification-Version :
该值是一个定义扩展规范版本的字符串。
-
- Specification-Vendor :
该值是一个定义维护扩展实现的组织的字符串。
-
- Sealed :
该值定义此JAR文件是否需要封闭。其值可以是‘true’或‘false’,大小写忽略,。如果设置成‘true’,则JAR文件中所有的包默认封闭,除非它们以另外单独定义。
签名验证
The signature over the signature file is first verified when the manifest is first parsed. For efficiency, this verification can be remembered. Note that this verification only validates the signature directions themselves, not the actual archive files.
The manifest main attributes are also verified when verification information is available in the signature file. If an x-Digest-Manifest-Main-Attributes
entry exists in the signature file, it is compared against a digest calculated against the main attributes in the manifest file. If this calculation fails, then jar validation fails. This decision can be remembered, for efficiency. If an x-Digest-Manifest-Main-Attributes
entry does not exist in the signature file, its nonexistence does not affect jar validation and the manifest main attributes are not verified.
To validate a file, a digest value in the signature file is compared against a digest calculated against the corresponding entry in the manifest file. Then, a digest value in the manifest file is compared against a digest calculated against the actual data referenced in the "Name:
" attribute, which specifies either a relative file path or URL.
样例清单文件:
Created-By: 1.3 (Sun股份有限公司)
Name: common/class1.class
MD5-Digest: (MD5 数字的base64表示法)
Name: common/class2.class
MD5-Digest: (MD5 数字的base64表示法)
SHA-Digest: (MD5 数字的base64表示法)
对应的签名文件:
MD5-Digest-Manifest-Main-Attributes: (MD5 数字的base64表示法)
Name: common/class1.class
MD5-Digest: (MD5 数字的base64表示法)
Name: common/class2.class
MD5-Digest: (MD5 数字的base64表示法)
数字签名
数字签名是.SF
签名文件的一个签名版本。这些是不可解读的二进制文件。
数字签名文件和.SF文件的文件名相同但扩展名不同.依赖于数字签名类型的扩展名是多种多样的。
.DSA (PKCS7 signature, DSA)
每一个 .SF
文件可能有多个数字签名, 但那些签名必须由同一个合法实体产生。
文件名扩展可能是1-3个基本字符.未能识别的扩展将被忽略.
范例
假设我们有一个服务类名叫java.io.spi.CharCodec。它有两个抽象方法:
public abstract CharEncoder getEncoder(String encodingName);
public abstract CharDecoder getDecoder(String encodingName);
当它不能够解释给定的编码时,每个方法返回一个适当的对象或null. 典型的 CharCodec 供应商会支持一种以上的编码。
如果sun.io.StandardCodec是一个CharCodec服务的供应者,那么它的jar文件将包含文件META-INF/services/java.io.spi.CharCodec.这个文件包含单行:
sun.io.StandardCodec # Standard codecs for the platform
为一个给定的编码名称定位编码器, 内部I/O代码应该这么写:
CharEncoder getEncoder(String encodingName) {
Iterator ps = Service.providers(CharCodec.class);
while (ps.hasNext()) {
CharCodec cc = (CharCodec)ps.next();
CharEncoder ce = cc.getEncoder(encodingName);
if (ce != null)
return ce;
}
return null;
}
供应商检查机制常常运行在调用者的安全环境上。可信的系统代码应该从授权的安全环境内部调用这个类的方法。
-------------------------我是分割线-----------------------------
概述
A JAR file can be signed by using the command line jarsigner tool or directly through the java.security
API. Every file entry, including non-signature related files in the META-INF
directory, will be signed if the JAR file is signed by the jarsigner tool. 签名涉及到的文件有:
META-INF/MANIFEST.MF
META-INF/*.SF
META-INF/*.DSA
META-INF/*.RSA
META-INF/SIG-*
Note that if such files are located in META-INF
subdirectories, they are not considered signature-related. Case-insensitive versions of these filenames are reserved and will also not be signed.
Subsets of a JAR file can be signed by using the java.security
API. A signed JAR file is exactly the same as the original JAR file, except that its manifest is updated and two additional files are added to the META-INF
directory: a signature file and a signature block file. When jarsigner is not used, the signing program has to construct both the signature file and the signature block file.
For every file entry signed in the signed JAR file, an individual manifest entry is created for it as long as it does not already exist in the manifest. Each manifest entry lists one or more digest attribute and an optional 未知属性.
签名文件
Each signer is represented by a signature file with extension .SF
. The major part of the file is similar to the manifest file. It consists of a main section which includes information supplied by the signer but not specific to any particular jar file entry. The main section entry, x-Digest-Manifest-Main-Attributes
(where x
is a digest algorithm), contains the digest value for the main attributes of the manifest.
The main section is followed by a list of individual entries whose names must also be present in the manifest file. Each individual entry must contain at least the digest of its corresponding entry in the manifest file.
Paths or URLs appearing in the manifest file but not in the signature file are not used in the calculation.
未知属性
Another requirement to validate the signature on a given manifest entry is that the verifier understand the value or values of the Magic key-pair value in that entry's manifest entry.
未知属性是可选的, but it is required that a parser understand the value of an entry's Magic key if it is verifying that entry's signature.
The value or values of the Magic attribute are a set of comma-separated context-specific strings. The spaces before and after the commas are ignored. Case is ignored. The exact meaning of the magic attributes is application specific. These values indicate how to compute the hash value contained in the manifest entry, and are therefore crucial to the proper verification of the signature. The keywords may be used for dynamic or embedded content, multiple hashes for multilingual documents, etc.
这里有两个声明文件中可能使用到未知属性的例子:
Name: http://www.scripts.com/index#script1
SHA-Digest: (base64 representation of SHA hash)
Magic: JavaScript, Dynamic
Name: http://www.tourist.com/guide.html
SHA-Digest: (base64 representation of SHA hash)
SHA-Digest-French: (base64 representation of SHA hash)
SHA-Digest-German: (base64 representation of SHA hash)
Magic: Multilingual
在第一个例子中, these Magic values may indicate that the result of an http query is the script embedded in the document, as opposed to the document itself, and also that the script is generated dynamically. These two pieces of information indicate how to compute the hash value against which to compare the manifest's digest value, thus comparing a valid signature.
In the second example, the Magic value indicates that the document retrieved may have been content-negotiated for a specific language, and that the digest to verify against is dependent on which language the document retrieved is written in.
数字签名
签名算法的数字签名文件 not listed above must reside in the META-INF
directory and have the prefix "SIG-
". 对应的签名文件 (.SF
file) 也必须有相同的前缀.
For those formats that do not support external signed data, the file shall consist of a signed copy of the .SF
file. Thus some data may be duplicated and a verifier should compare the two files.
Formats that support external data either reference the .SF
file, or perform calculations on it with implicit reference.
Following is a list of additional restrictions and rules that apply to manifest and signature files.
- 解析之前:
If the last character of the file is an EOF character (code 26), the EOF is treated as whitespace. Two newlines are appended (one for editors that don't put a newline at the end of the last line, and one so that the grammar doesn't have to special-case the last entry, which may not have a blank line after it).
- 属性:
-
在所有节(部分)的所有场景中,不能被理解的属性会被忽略。属性名称是大小写敏感的. Programs which generate manifest and signature files should use the cases shown in this specification however.
在一个节(部分)中,属性名称不能重复.
- 版本:
Manifest-Version and Signature-Version must be first, and in exactly that case (so that they can be recognized easily as magic strings). Other than that, the order of attributes within a main section is not significant.
- 排序:
-
独立的声明实体的顺序不是很重要。The order of individual signature entries is not significant, except that the digests that get signed are in that order.
- 行长度:
No line may be longer than 72 bytes (not characters), in its UTF8-encoded form. If a value would make the initial line longer than this, it should be continued on extra lines (each starting with a single SPACE).
- 错误:
If a file cannot be parsed according to this spec, a warning should be output, and none of the signatures should be trusted.
- 限制:
-
Because header names cannot be continued, the maximum length of a header name is 70 bytes (there must be a colon and a SPACE after the name).NUL, CR, and LF can't be embedded in header values, and NUL, CR, LF and ":" can't be embedded in header names.
每个文件的实现必须支持相当于65535字节(不是字符)的头部值和65535个头部。 They might run out of memory, but there should not be hard-coded limits below these values.
- 签名者:
It is technically possible that different entities may use different signing algorithms to share a single signature file. This violates the standard, and the extra signature may be ignored.
- 算法:
本版本没有强制执行摘要算法或签名算法。然而,MD5或SHA摘要算法必须至少支持其一。
JAR 索引
概述
Since 1.3, JarIndex is introduced to optimize the class searching process of class loaders for network applications, especially applets. Originally, an applet class loader uses a simple linear search algorithm to search each element on its internal search path, which is constructed from the "ARCHIVE" tag or the "Class-Path" main attribute. The class loader downloads and opens each element in its search path, until the class or resource is found. If the class loader tries to find a nonexistent resource, then all the jar files within the application or applet will have to be downloaded. For large network applications and applets this could result in slow startup, sluggish response and wasted network bandwidth. The JarIndex mechanism collects the contents of all the jar files defined in an applet and stores the information in an index file in the first jar file on the applet's class path. After the first jar file is downloaded, the applet class loader will use the collected content information for efficient downloading of jar files.
The existing jar tool is enhanced to be able to examine a list of jar files and generate directory information as to which classes and resources reside in which jar file. This directory information is stored in a simple text file named INDEX.LIST in the META-INF directory of the root jar file. When the classloader loads the root jar file, it reads the INDEX.LIST file and uses it to construct a hash table of mappings from file and package names to lists of jar file names. In order to find a class or a resource, the class loader queries the hashtable to find the proper jar file and then downloads it if necessary.
Once the class loader finds a INDEX.LIST file in a particular jar file, it always trusts the information listed in it. If a mapping is found for a particular class, but the class loader fails to find it by following the link, an InvalidJarIndexException is thrown. When this occurs, the application developer should rerun the jar tool on the extension to get the right information into the index file.
To prevent adding too much space overhead to the application and to speed up the construction of the in-memory hash table, the INDEX.LIST file is kept as small as possible. For classes with non-null package names, mappings are recorded at the package level. Normally one package name is mapped to one jar file, but if a particular package spans more than one jar file, then the mapped value of this package will be a list of jar files. For resource files with non-empty directory prefixes, mappings are also recorded at the directory level. Only for classes with null package name, and resource files which reside in the root directory, will the mapping be recorded at the individual file level.
索引文件声明
The INDEX.LIST file contains one or more sections each separated by a single blank line. Each section defines the content of a particular jar file, with a header defining the jar file path name, followed by a list of package or file names, one per line. All the jar file paths are relative to the code base of the root jar file. These path names are resolved in the same way as the current extension mechanism does for bundled extensions.
The UTF-8 encoding is used to support non ASCII characters in file or package names in the index file.
声明
index file : version-info blankline section*
version-info : JarIndex-Version: version-number
version-number : digit+{.digit+}*
section : body blankline
body : header name*
header : char+.jar newline
name : char+ newline
char : any valid Unicode character except NULL, CR andLF
blankline: newline newline
newline : CR LF | LF | CR (not followed by LF)
digit: {0-9}
运行jar -i.会产生 INDEX.LIST 文件。查看 jar 帮助页面获取更多细节。
向后兼容
The new class loading scheme is totally backward compatible with applications developed on top of the current extension mechanism. When the class loader loads the first jar file and an INDEX.LIST file is found in the META-INF directory, it would construct the index hash table and use the new loading scheme for the extension. Otherwise, the class loader will simply use the original linear search algorithm.
服务供应商
概述
Files in the META-INF/services directory are service provider configuration files. A service is a well-known set of interfaces and (usually abstract) classes. A service provider is a specific implementation of a service. The classes in a provider typically implement the interfaces and subclass the classes defined in the service itself. Service providers may be installed in an implementation of the Java platform in the form of extensions, that is, jar files placed into any of the usual extension directories. Providers may also be made available by adding them to the applet or application class path or by some other platform-specific means.
A service is represented by an abstract class. A provider of a given service contains one or more concrete classes that extend this service class with data and code specific to the provider. This provider class will typically not be the entire provider itself but rather a proxy that contains enough information to decide whether the provider is able to satisfy a particular request together with code that can create the actual provider on demand. The details of provider classes tend to be highly service-specific; no single class or interface could possibly unify them, so no such class has been defined. The only requirement enforced here is that provider classes must have a zero-argument constructor so that they may be instantiated during lookup.
供应商程序配置文件
一个服务提供者 identifies itself by placing a provider-configuration file in the resource directory META-INF/services. The file's name should consist of the fully-qualified name of the abstract service class. The file should contain a newline-separated list of unique concrete provider-class names. Space and tab characters, as well as blank lines, are ignored. The comment character is '#' (0x23); on each line all characters following the first comment character are ignored. The file must be encoded in UTF-8.