Java教程是针对JDK 8编写的。本页面中描述的示例和实践不利用后续版本中引入的改进,并且可能使用不再可用的技术。
有关Java SE 9及后续版本中更新的语言特性的摘要,请参阅Java语言更改。
有关所有JDK版本的新功能、增强功能以及已删除或弃用选项的信息,请参阅JDK发布说明。
本课程解释了为什么需要数字签名、证书和密钥库。本课程还比较了使用工具与使用JDK安全API生成签名的情况。在接下来的两个课程中,演示了这些工具的使用:签署代码并授予权限和文件交换。在生成和验证签名课程中演示了API的使用。
本课程包含以下几个部分:
如果你通过电子方式发送给某人重要文档(或文档),或者要运行的小程序或应用程序,接收者需要一种方法来验证文档或代码是否来自你,并且在传输过程中没有被修改(例如,被恶意用户拦截)。数字签名、证书和密钥库都有助于确保你发送的文件的安全性。
数字签名的基本思想如下:
keytool
或安全API方法来生成私钥。也就是说,你使用jarsigner
工具或安全API方法为文档或代码生成数字签名。接收者需要确保你的公钥本身是真实的,才能使用它来验证你的签名是否真实。因此,你通常会提供一个证书,其中包含了你的公钥以及一个证书颁发机构的密钥,该机构可以为你的密钥的真实性作担保。有关签名和验证的术语和概念的更多信息,以及进一步解释其好处,请参见"JAR文件打包程序"课程中的签署JAR文件部分。
证书包含:
接收者可以通过验证数字签名,使用颁发者(签名者)的公钥,来检查证书是否有效。该公钥可以存储在另一个证书中,该证书的签名也可以通过使用下一个证书颁发者的公钥进行验证,该公钥也可以存储在另一个证书中,依此类推。当您到达一个已经信任的公钥并使用它来验证相应证书上的签名时,您可以停止检查。
如果接收者无法建立信任链,那么他/她可以使用keytool
-import
或-printcert
命令来计算证书的指纹。指纹是一个相对较短的数字,可以唯一可靠地标识证书。(从技术上讲,指纹是证书信息的哈希值,使用消息摘要函数计算得到。)接收者然后可以电话联系证书的所有者,并将接收到的证书的指纹值与发送的证书进行比较。如果指纹相同,则证书相同。
因此,您可以确保证书在传输过程中没有被修改。在处理证书时,另一个可能存在的不确定性是发送者的身份。有时候,一个证书是自签名的,也就是说,使用与证书中的公钥相对应的私钥进行签名;颁发者和主题是相同的。
自签名证书对于开发和测试应用程序非常有用。然而,在部署给用户之前,需要从一个可信的第三方获取证书,这个第三方被称为认证机构(CA)。为此,您需要将自签名的证书签名请求(CSR)发送给CA。CA通过验证CSR上的签名和您的身份,例如通过检查您的驾驶执照或其他信息来验证您的身份。然后,CA通过使用自己的(CA的)私钥颁发证书并对其进行签名,来证明您是公钥的所有者。任何信任颁发CA的公钥的人都可以验证证书上的签名。在许多情况下,颁发CA本身可能具有来自CA层次结构中更高级别的CA的证书,导致证书链。
您信任的实体的证书通常作为“受信任的证书”导入到您的密钥库中。然后,可以使用每个这样的证书中的公钥来验证使用相应私钥生成的签名。这样的验证可以通过以下方式完成:
jarsigner
工具(如果文档/代码和签名出现在JAR文件中),如果您要向他人发送签名的代码或文档,则需要向他们提供包含与用于签署代码/文档的私钥对应的公钥的证书。keytool
-export
命令或API方法可以将您的证书从密钥库导出到文件中,然后可以将文件发送给需要的人。接收到证书的人可以使用API方法或keytool
-import
命令将其导入到密钥库中作为受信任的证书。
如果您使用jarsigner
工具为JAR文件生成签名,该工具会从密钥库中检索您的证书及其支持的证书链。然后,该工具将它们与签名一起存储在JAR文件中。
私钥及其关联的公钥证书存储在受密码保护的数据库中,称为密钥库。密钥库可以包含两种类型的条目:上述可信证书条目和密钥/证书条目,每个条目都包含一个私钥和相应的公钥证书。密钥库中的每个条目都由一个别名标识。
密钥库所有者可以在密钥库中拥有多个密钥,并通过不同的别名访问它们。别名通常根据密钥库所有者在所关联的密钥中使用的特定角色来命名。别名还可以标识密钥的用途。例如,别名signPersonalEmail
可以用于标识用于签署个人电子邮件的密钥库条目,别名signJarFiles
可以用于标识用于签署JAR文件的条目。
可以使用keytool
工具来
还可以使用API方法访问和修改密钥库。
请注意以下与数字签名相关的工具和API使用的问题。
jar
工具创建该文件。JAR文件是一种将多个文件封装在一个位置的好方法。当文件被“签名”时,生成的数字签名字节需要存储在某个地方。当签署JAR文件时,签名可以放在JAR文件本身中。这就是当您使用jarsigner
工具对JAR文件进行签名时所发生的情况。jarsigner
工具能够验证JAR文件签名的真实性,首先需要将接收JAR文件的个人/组织的公钥对应的私钥的证书导入到其密钥库中。生成和验证签名教程展示了如何使用JDK安全API对文档进行签名。该教程展示了一个程序的执行步骤,由拥有原始文档的人执行,该程序会做以下操作:
然后,教程展示了另一个程序的示例,由数据的接收者执行。该程序会做以下操作:
该教程还展示了导入和提供密钥的其他方法,包括证书。
签名代码和授予权限教程展示了如何使用Java安全工具将代码放入JAR文件中,对其进行签名并导出公钥。然后展示了如何使用相同的Java工具来导入接收者的公钥证书,并在策略文件中添加条目,以授予代码访问接收者控制的系统资源所需的权限。
交换文件教程展示了如何使用Java安全工具对文档进行签名,然后使用keytool
导出相应的公钥证书,该证书对应用于签署该文档的私钥。然后展示了如何使用接收者安装公钥证书并使用jarsigner
工具验证签名。
这两个教程有很多共同之处。在两种情况下,代码或文档发送者的前两个步骤是:
jar
工具创建包含文档或类文件的JAR文件。keytool
-genkey
命令生成密钥(如果密钥不存在)。下面的两个步骤是可选的:
keytool
-certreq
命令,然后将生成的证书签名请求发送给认证机构(CA),如VeriSign。keytool
-import
命令导入CA的响应。下面的两个步骤是必需的:
jarsigner
工具和之前生成的私钥对JAR文件进行签名。keytool
-export
命令导出公钥证书。然后将签名的JAR文件和证书提供给接收者。在两种情况下,接收者应将签名的JAR文件和证书导入为受信任的证书,使用keytool
-import
命令。keytool
将尝试从要导入的证书到密钥库中已受信任的证书构建信任链。如果失败,keytool
将显示证书指纹并提示您进行验证。
如果发送的是代码,接收者还需要修改一个策略文件,以允许由导入的证书中的公钥对应的私钥签名的代码访问所需的资源。可以使用策略工具来完成这个步骤。
如果发送的是一个或多个文档,接收者需要使用jarsigner
工具验证JAR文件签名的真实性。
本课程讨论了两个可选的步骤。其他步骤将在下面的两个课程中介绍:签署代码并授予权限和文件交换。
当使用keytool
生成公私钥对时,它会创建一个包含私钥和自签名证书的密钥库条目,用于公钥。(也就是说,证书是使用相应的私钥签名的。)这在开发和测试应用程序时是足够的。
然而,如果证书由认证机构(CA)签名,它更有可能被他人信任。要获得由CA签名的证书,首先需要通过以下命令生成证书签名请求(CSR):
keytool -certreq -alias alias -file csrFile
这里,alias用于访问包含私钥和公钥证书的密钥库条目,csrFile指定此命令创建的CSR的名称。
然后,将此文件提交给CA(例如VeriSign, Inc.)。CA对请求者("subject")进行身份验证,然后签署并返回验证您的公钥的证书。通过签署证书,CA担保您是公钥的所有者。
在某些情况下,CA将返回一个证书链,每个证书链都认证前一个证书链中签名者的公钥。
在向认证机构(CA)提交证书签名请求(CSR)后,您需要使用CA返回给您的证书(或证书链)替换密钥库中的原始自签名证书,从而导入证书链。
但首先,您需要在密钥库中(或在下面描述的cacerts
密钥库文件中)拥有一个"trusted certificate"条目,用于验证CA的公钥。有了这样一个条目,可以验证CA对证书的签名,或者验证CA在响应CSR时发送给您的证书链中的最终证书上的签名。
在导入CA的证书回复之前,您需要在密钥库或cacerts
文件中拥有一个或多个"trusted certificates"。
cacerts
文件代表一个系统范围的包含 CA 证书的密钥库。该文件位于 JRE 安全属性目录 java.home/lib/security
中,其中 java.home 是 JRE 的安装目录。
重要提示:验证你的 cacerts
文件
由于你信任 cacerts
文件中的 CA 作为签署和颁发证书给其他实体的实体,你必须仔细管理 cacerts
文件。 cacerts
文件应该只包含你信任的 CA 的证书。验证捆绑在 cacerts
文件中的可信根 CA 证书并做出你自己的信任决策是你的责任。要从 cacerts
文件中删除一个不受信任的 CA 证书,请使用 keytool
命令的删除选项。你可以在 JRE 安装目录中找到 cacerts
文件。如果你没有编辑此文件的权限,请联系系统管理员。
cacerts
文件包含一些受信任的 CA 证书。如果你将你的 CSR 发送给其中一个受信任的供应商(如 VeriSign),你就不需要将供应商的根证书导入到你的密钥库中作为受信任的证书;你可以继续到下一节查看如何导入 CA 的证书回复。
CA 的证书通常是自签名的,或者由另一个 CA 签署,在这种情况下,你还需要一个验证该 CA 公钥的证书。假设公司 ABC, Inc. 是一个 CA,你获取到一个名为 ABCCA.cer
的文件,据称是来自 ABC 的自签名证书,用于验证该 CA 的公钥。
在将其作为 "受信任" 证书导入之前,请非常小心确保证书有效!首先查看它(使用 keytool
的 -printcert
命令或没有 -noprompt
选项的 keytool
的 -import
命令),确保显示的证书指纹与预期的指纹匹配。你可以联系发送证书的人,并将你看到的指纹与他们展示的指纹或安全公钥存储库展示的指纹进行比较。只有指纹相等才能确保在传输过程中证书没有被替换为其他人(例如攻击者)的证书。如果发生这样的攻击,并且你在导入证书之前没有检查证书,那么你将相信攻击者签署的任何东西。
如果您相信证书是有效的,您可以通过以下命令将其添加到密钥库中:
keytool -import -alias 别名 -file ABCCA.cer -keystore 存储文件
这个命令在密钥库中创建一个“受信任的证书”条目,其名称与 存储文件 中指定的名称相同。该条目包含来自文件 ABCCA.cer
的数据,并且被分配了指定的别名。
一旦您已经根据前一节的描述导入了所需的受信任证书,或者它们已经存在于您的密钥库或 cacerts
文件中,您可以导入证书回复,从而用证书链替换您的自签名证书。这个证书链要么是CA回复中返回的链(如果CA回复是一个链),要么是使用证书回复和已经存在于密钥库或 cacerts
密钥库文件中的受信任证书构建的链(如果CA回复是一个单个证书)。
例如,假设您将证书签名请求发送给了VeriSign。您可以通过以下方式导入回复,假设返回的证书位于 certReplyFile 指定的文件中:
keytool -import -trustcacerts -keystore 存储文件 -alias 别名 -file certReplyFile
在一行上输入此命令。
使用密钥库中的受信任证书来验证证书回复,并且可以选择使用 cacerts
密钥库文件中配置的证书进行验证(如果指定了 -trustcacerts
选项)。使用链中的下一个较高级别的证书来验证链中的每个证书。您只需要信任链中的顶级“根”CA证书。如果您尚未信任顶级证书,keytool
将显示该证书的指纹并询问您是否要信任它。
指定(通过 别名)条目的新证书链将替换与该条目关联的旧证书(或链)。只有在提供了有效的 keypass,即用于保护条目的私钥的密码时,才能替换旧链。如果未提供密码,并且私钥密码与密钥库密码不同,则会提示用户输入密码。
有关生成CSR和导入证书回复的更详细信息,请参阅 keytool
文档: