Java 程序的打包、签名和验证
参考资料
该文中的内容来源于 Oracle 的官方文档。Oracle 在 Java 方面的文档是非常完善的。对 Java 8 感兴趣的朋友,可以直接找到这个总入口 Java SE 8 Documentation ,想阅读什么就点什么。本博客不定期从 Oracle 官网搬砖。这里介绍的工具是 jar 和 jarsigner 。
前言
在前面的 在Linux中安装Oracle JDK 8以及JVM的类加载机制 这一篇中我已经初步讨论过 Java 程序的组成:Java 程序中没有独立函数,只有类和类中的方法,即使是程序的入口点也不是独立函数。 Java 程序的源代码存在于名为 *.java
的源代码文件中,然后经过 javac
命令进行编译,最终可生成名为 *.class
的类文件。 Java 程序的启动器是 java
命令,它负责加载相应的类并执行其中的指令。
Java 程序的这种组织方式和我们常用的文件系统契合度非常好,一个类就是一个文件,类名就是文件名。(当然也有例外,比如内部类,这里不做讨论。)更进一步,Java 中还有 package 的概念,而且一个 package 名(类似于 abc.def.ghi.*.class
)正好对应文件系统的路径(类似于 abc/def/ghi/*.class
)。这种对应关系不是可有可无的,而是强制性的,我们在组织源代码和类的时候,必须遵守这个准则,否则程序将无法运行。文件多了,自然需要将其打包成一个整体,这就需要用到 jar
命令生成文件名为 *.jar
的 jar 包文件,该 jar 包文件就是一个很常见的压缩包文件,它其中的内容完全维持文件系统中的那种树状结构,随时可以解包查看其中的文件。将库或程序打包成 jar 文件进行发布已经是 Java 世界的标准做法,为了安全, jar 文件还可以被签名和验证。这正是 Java 世界的方便所在。
Java 程序中的 package 名和类文件的路径的对应关系
这里写一个 HelloWorld 程序来做示范。本来一个 HelloWorld 程序是可以很简单的,在 Java 中只需要一个 System.out.println("Hello, World!");
即可。但是为了让类多一点,我把它写得稍微复杂了点。我先写了一个 Speaker
类,然后在 HelloWorld
类的 main
方法中调用 speaker.sayHello();
方法来和这个世界打招呼。同时,我的两个类都定义在一个 package 中,如下图源代码中的 package com.xkland.sample;
:
前面讲过, package 名必须和文件的路径一一对应,所以,我将源文件放在了 src
目录的 com/xkland/sample
目录中,其实这不是必须的,源文件可以随便放,只是这么放是一个好习惯。但是类文件所在的路径就必须和 package 名完全一致了,否则程序无法执行。如下图,我使用 javac src/com/xkland/sample/*.java -d dst
命令编译源文件,使用 -d dst
选项就是让 javac
把生成的类文件放到 dst
目录中,而 javac
在 dst
目录中自动生成了和 package 名完全一致的目录树。
然后,我们执行程序的时候,必须在 dst
目录中运行 java com.xkland.sample.HelloWorld
,在其它的目录中运行都不行,即使在 dst/com/xkland/sample
目录下也不行,哪怕这里是 HelloWorld.class
所在的位置。(其实想在 dst
目录以外的地方运行该程序也有办法,那就是把 dst
目录加入到 CLASSPATH 中,这里不做讨论。)这个例子虽简单,但充分展示了 Java 中的 package
和 类文件在文件系统中的路径
之间必须遵守的约定。将类文件打包也必须遵守这样的约定。
使用 jar 命令将程序打包
让类就这样分散在文件系统中毕竟不是最方便的,前面讲过可以把类文件打包,生成一个 jar 文件,这里来进行实战。(其实打包的文件中可以包含任意类型的文件,不仅只是 Java 的类文件,图片视频什么的都可以,文本文件自然不在话下,这些东西都是资源,这里也不做深入讨论。) jar 包还可以作为一个单独的程序运行,使用 java -jar filename.jar
命令即可。由于每一个 jar 包中包含了不止一个类文件,所以要作为单独的程序运行,在生成 jar 包的时候就必须指定程序的入口点,这个可以通过 jar
命令的 -e
参数指定。
使用 jar
命令打包的时候最重要的注意事项也是前面提到的 package 名和类文件的路径的对应。先看下图:
在该图中,我使用了 jar
命令的 -cfe
选项,其中的 c
是创建 jar 文件,f
是指定 jar 文件的文件名,e
是指定程序的入口点。前面提到过,一定要注意 package 名和类文件的路径的对应关系,所以在这个例子中,使用 jar
命令打包时,要么先进入 dst
目录,再运行 jar -cfe HelloWorld.jar com.xkland.sample.HelloWorld com
,要么使用 jar
命令的 -C
指定 jar 包中的类文件的路径从哪个目录开始。我这里用的就是 jar -cfe HelloWorld.jar com.xkland.sample.HelloWorld -C dst com
。这里的 com
目录会自动全部打包进 jar 文件,包括其中的所有子目录和文件,也就是说,对于目录的打包是递归的。而且,运行下面这样的命令效果应该是一样的:
jar -cfe HelloWorld.jar com.xkland.sample.HelloWorld -C dst .
,这里的.
代表当前目录,也就是dst
目录中的所有东西都会进行打包;
像上面这样打包后,使用 java -jar HelloWorld.jar
可以运行程序,使用 jar -tf HelloWorld.jar
可以查看 HelloWorld.jar
中的内容。可以看到,类文件的路径为 com/xkland/sample/HelloWorld.class
和 com/xkland/sample/Speaker.class
,正好和 package 名完全对应。还可以看到 HelloWorld.jar
中有一个 META-INF/MANIFEST.MF
文件,这个文件是 jar 包文件的灵魂,所有的配置信息都在这里,比如程序的入口点是什么就是保存在这里,它是一个纯文本文件,可以直接读写,但是我们实际工作中基本不需要自己手动编写该文件,所以这里不做深入讨论。
如果使用 jar
打包的时候没有选择正确的开始目录,则 jar 包中类文件的路径就会不正确,程序就无法运行。如下图,打包时既没有进入 dst
目录,又没有使用 -C dst
选项,结果打包后程序就无法正确运行了。使用 jar -tf HelloWorld.jar
查看一下,发现所有类文件的路径都不对,因此程序无法运行。
关于 jar 的更多内容,可以直接查看 jar命令的手册,或者查看 Java教程中关于jar的章节 。
jar 包的签名和验证
在我介绍 JDK中的证书生成和管理工具keytool 时,已经简单的讲过网络安全、证书、签名等方面的内容,这里只需要实战一下即可。现在已经有了一个 HelloWorld.jar
文件,尝试一下使用 jarsigner
命令对它进行签名。签名之前,先得有个证书,所以先使用 keytool -genkeypair -alias youxia
为自己创建一个,别名为 youxia
。然后,使用这个证书对 HelloWorld.jar
进行签名,命令为 jarsigner HelloWorld.jar youxia
。最后,可以使用 jarsigner -verify HelloWorld.jar
对 HelloWorld.jar
进行验证。如下图:
不动手不知道,一动手才发现 So Easy!更多的细节可以戳 Signing JAR Files 和 Verifying Signed JAR Files。
jar 文件被签名后,里面多了一些文件,把它解包看一下,如下图:
可以看到:首先是 MANIFEST.MF
文件中多了几行,它为 jar 包中的每一个文件都生成了一个数据摘要,这个摘要是从 jar 包中包含的文件本身计算出来的;其次,多了一个 YOUXIA.SF
文件,其中的内容也是 jar 包中每个文件对应的摘要,但是这个摘要是从 MANIFEST.MF
中的数据项计算出来的,它同时包含有针对整个 MANIFEST.MF
文件计算出的摘要;最后,就是一个无法直接阅读的文件 YOUXIA.DSA
,从图中可以看出这个文件显示为乱码,其中的内容就是 youxia 的公钥以及使用 youxia 的私钥对该 jar 文件进行签名后的结果。具体信息请看 Understanding Signing and Verification。
有了这里的直观的印象,我们就对签名和验证有了更深入的了解。签名和验证是建立在信息摘要算法和非对称加密解密算法的基础上的。数据摘要算法是不可逆的,它只能从数据生成摘要,不能从摘要解密出数据。非对称加密解密算法需要公钥私钥对,使用私钥加密的数据只能使用公钥解密,因此使用私钥对上一步生成的摘要进行加密,就相当于是签名了,因为只能通过相应的公钥进行解密。一般情况下公钥是通过证书发布出去的,而在上面的例子中,签名者的公钥直接放在了 YOUXIA.DSA
文件中,方便验证者使用。
总结
jar
和 jarsigner
这两个命令用起来没有什么难度,主要是理解其中的思想。使用 jar
时,一定要注意 package 名和类文件的路径之间的对应关系;使用 jarsigner
时,要理解公钥私钥、证书、摘要和数字签名,而且 JDK 中提供了非常好用的生成和管理公钥私钥及证书的工具 keytool
。对于这些工具,我们只要亲自动手试一下,就可以加深我们对 Java 安全方面的理解。至于这些命令的细节都不需要多记,用的时候查官方文档即可。
Java 程序的打包、签名和验证的更多相关文章
- Java初学者作业——编写Java程序,实现用户登录验证。
返回本章节 返回作业目录 需求说明: 编写Java程序,实现用户登录验证. 若用户名与密码输入正确,则提示"登录成功,欢迎回来!",若用户名与密码不匹配,则提示"用户名和 ...
- 一招教你IDEA中Java程序如何打包,以及打包后如何运行
前言 编写程序 程序打包 测试运行 IDEA作为目前按最主流的Java程序项目编写工具,越来越受到开发人员的青睐.idea因为其五花八门的功能,让你在开发过程中效率显著提高.那么对于初学者来说,如何通 ...
- 第一次发博,发个简单的Java程序发送手机短信验证
最近在准备一个项目,想的登录时候用手机验证,就通过上网查阅了一下手机验证的实现方法,原来超级简单,下面将一步一步介绍. 1.去中国网建注册一个账号密码,首次注册送五条免费短信和3条免费彩信.具体的网址 ...
- 使用 SecurityManager 和 Policy File 管理 Java 程序的权限
参考资料 该文中的内容来源于 Oracle 的官方文档.Oracle 在 Java 方面的文档是非常完善的.对 Java 8 感兴趣的朋友,可以从这个总入口 Java SE 8 Documentati ...
- Java程序实现密钥库的维护
1 Java程序列出密钥库所有条目 import java.util.*; import java.io.*; import java.security.*; public class ShowAli ...
- Android程序的打包和安装
当我们使用Android Studio的时候,这些步骤都交给它去做了. 编译 classes.dex 文件 编译 resources.arsc 文件 生成资源索引表resources.arsc. 把r ...
- Python中调用Java程序包
<原创不易,转载请标明出处:https://www.cnblogs.com/bandaobudaoweng/p/10785766.html> 开发Python程序,需求中需要用到Java代 ...
- Java程序设计基础作业目录(作业笔记)
持续更新中............. Java程序设计基础笔记 • [目录] 我的大学笔记>>> 第1章 初识Java>>> 1.1.4 学生成绩等级流程图练习 1 ...
- 关于java程序打包为EXE的若干问题
这几天在一个即时通讯系统的打包上,吃尽了苦头,到现在才算解决,现在对遇到的问题进行分析总结. 1.一开始是在export "Runnable JAR file"的时候,弹出了这样的 ...
随机推荐
- SQLSERVER将一个文件组的数据移动到另一个文件组
SQLSERVER将一个文件组的数据移动到另一个文件组 有经验的大侠可以直接忽视这篇文章~ 这个问题有经验的人都知道怎麽做,因为我们公司的数据量不大没有这个需求,也不知道怎麽做实验 今天求助了QQ群里 ...
- .NetCore中的日志(1)日志组件解析
.NetCore中的日志(1)日志组件解析 0x00 问题的产生 日志记录功能在开发中很常用,可以记录程序运行的细节,也可以记录用户的行为.在之前开发时我一般都是用自己写的小工具来记录日志,输出目标包 ...
- 匹夫细说C#:庖丁解牛迭代器,那些藏在幕后的秘密
0x00 前言 在匹夫的上一篇文章<匹夫细说C#:不是“栈类型”的值类型,从生命周期聊存储位置>的最后,匹夫以总结和后记的方式涉及到一部分迭代器的知识.但是觉得还是不够过瘾,很多需要说清楚 ...
- ABP文档 - SignalR 集成
文档目录 本节内容: 简介 安装 服务端 客户端 连接确立 内置功能 通知 在线客户端 帕斯卡 vs 骆峰式 你的SignalR代码 简介 使用Abp.Web.SignalR nuget包,使基于应用 ...
- Jquery mobiscroll 移动设备(手机)wap日期时间选择插件以及滑动、滚动插件
Jquery Mobiscroll是一个用于触摸设备(Android phones, iPhone, iPad, Galaxy Tab)的日期和时间选择器jQuery插件.以及各种滑动插件 可以让用户 ...
- UE4新手引导入门教程
请大家去这个地址下载:file:///D:/UE4%20Doc/虚幻4新手引导入门教程.pdf
- How those spring enable annotations work--转
原文地址:http://blog.fawnanddoug.com/2012/08/how-those-spring-enable-annotations-work.html Spring's Java ...
- 编译器开发系列--Ocelot语言6.静态类型检查
关于"静态类型检查",想必使用C 或Java 的各位应该非常熟悉了.在此过程中将检查表达式的类型,发现类型不正确的操作时就会报错.例如结构体之间无法用+ 进行加法运算,指针和数值之 ...
- 敏捷转型历程 - Sprint3 Planning
我: Tech Leader 团队:团队成员分布在两个城市,我所在的城市包括我有4个成员,另外一个城市包括SM有7个成员.另外由于我们的BA离职了,我暂代IT 的PO 职位.PM和我在一个城市,但他不 ...
- Jenkins的一个bug-同时build一个项目两次导致失败
我们有一个job A, A只是配置了一些参数,它会去触发模板job B. 我一开始点击构建A, 马上发现参数配置不对,于是撤消了构建,但是我没有发现B已经被触发,我重新配置参数,然后再次构建A,这个时 ...