keytool工具说明见下文。
在Windows环境分析keytool工具执行方式如下:
JDK与JRE的bin目录中存在keytool.exe程序,当执行keytool.exe时,keytool.exe会加载bin/java.dll、bin/client/jvm.dll、bin/server/jvm.dll、lib/rt.jar等,java.exe在执行时也会加载上述文件。且keytool执行时的错误信息为Java类的错误。基于以上现象,说明keytool在执行时依赖Java运行环境。
以下内容主要由Oracle文档翻译而来。
Oracle提供了关于keystore与keytool的文档:
JDK | 操作系统 | URL |
---|---|---|
1.5 | Solaris and Linux | http://docs.oracle.com/javase/1.5.0/docs/tooldocs/solaris/keytool.html |
1.5 | Windows | http://docs.oracle.com/javase/1.5.0/docs/tooldocs/windows/keytool.html |
1.6 | Solaris and Linux | http://docs.oracle.com/javase/6/docs/technotes/tools/solaris/keytool.html |
1.6 | Windows | http://docs.oracle.com/javase/6/docs/technotes/tools/windows/keytool.html |
1.7 | Solaris and Linux | http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/keytool.html |
1.7 | Windows | http://docs.oracle.com/javase/7/docs/technotes/tools/windows/keytool.html |
1.8 | Solaris, Linux, and OS X | http://docs.oracle.com/javase/8/docs/technotes/tools/unix/keytool.html |
1.8 | Windows | http://docs.oracle.com/javase/8/docs/technotes/tools/windows/keytool.html |
密钥库是存储了密钥、X.509证书链及受信任证书的数据库。
密钥库存在不同类型的条目,两种最适用于keytool的条目类型包括密钥条目与受信任证书条目。
每个密钥条目都包含敏感的密钥信息,它们以受保护的形式存储,以防未授权访问。通常,以这种形式存储的条目对应的密钥是秘密密钥,或者是伴随着对应公钥的证书链的私钥。
每个受信任证书条目都包含一个属于另一方的单独的公钥证书。它被称为"受信任证书"是因为密钥库的所有人信任这个证书中的公钥确实属于特定的身份,通过证书中的“主体”(所有人)可以进行身份验证。证书的颁发者对证书进行证实,通过对证书进行签名的方式。
所有的密钥库条目在访问时都需要通过独特的别名。
当使用以下命令向密钥库添加条目时,都需要指定别名:
使用-genseckey命令产生秘密密钥;
使用-genkeypair命令产生密钥对;
使用-importcert命令添加证书或证书链至受信任的证书列表中。
后续的keytool命令在操作对应的条目时,必须使用相同的别名。
keytool可以创建并管理密钥库密钥条目,每个密钥条目包含一个私钥与对应的证书"链"。证书链中的第一个证书包含私钥相应的公钥。
当密钥在最初被创建时(参考-genkeypair命令),链开始仅包含一个元素,即一个自签名证书。自签名证书的发行者(签名者)与证书主体(被证书认证的公钥对应的实体)相同。无论何时使用-genkeypair命令产生一个新的公/私钥对,都会将公钥包含进一个自签名证书中。
之后,当证书签名请求被创建(参考-certreq命令)并被发送至认证机构(CA)后,需要将CA的返回进行导入(参考-importcert命令),自签名证书将会被证书链代替。在证书链底部是由CA认证主体的公钥颁发的证书(回复),证书链中的下一个证书是用于认证CA的公钥。
在许多情况下,CA返回的证书是自签名证书(CA认证自己公钥的证书)且位于链中的最后一个。在其他情况下,CA返回的是证书链。在这种情况下,链底部的证书也是由CA签名验证密钥条目的公钥的证书。但链中的第二个证书由不同的CA进行签名,认证接收CSR的CA的公钥。然后,链中的下一个证书将是用于验证第二个CA的密钥的证书。如此直到达到自签名"根"证书。链中的每个证书(第一个之后的)都需要验证前一个证书的签名者的公钥。
名为"cacerts"的证书文件保存在安全属性目录,java.home/lib/security,java.home是JAVA的运行时环境目录(SDK或Java2运行时环境的顶层目录的jre目录)。
"cacerts"文件代表系统范围的保存CA证书的密钥库。系统管理员可以通过keytool对该文件进行配置与管理,需要指定"JKS"密钥库类型。"cacerts"密钥库文件附带一些CA根证书。
“cacerts"密钥库文件的初始密码是"changeit”。
由于你信任cacerts文件中的CA,作为对其他实体进行证书签名与颁发的实体,你必须仔细管理cacerts文件。cacerts文件应该仅包含你信任的CA证书。验证cacerts文件中捆绑的受信任CA根证书并决定你自己的信任范围是使用者的责任。当需要从cacerts文件中去除一个不受信任的CA证书时,可使用keytool命令的删除选项。你可以在JRE的安装目录中找到cacerts文件。
注意:
jdk的bin目录、jdk的jre\bin目录、jre的bin目录中可能均存在keytool。
当使用jdk的bin目录或jdk的jre\bin目录中的keytool时,会使用jdk的jre\lib\security目录中的cacerts文件;
当使用jre的bin目录中的keytool时,会使用jre的lib\security目录中的cacerts文件。
X.500识别名用于识别实体,如X.509证书中的主体与发布者(签发人)域。keytool支持的内容如下。
当使用-genkeypair的-dname选项指定X.500识别名时,-dname选项的参数名与上述参数的对应关系如下:
示例命令如下:
keytool -genkeypair -dname "CN=xx, OU=xx, O=xx, L=xx, ST=xx, C=xx"
-dname选项的参数名称不区分大小写,如"CN","cn"及"Cn"含义相同。各参数需要按照上述顺序依次出现,但可以省略部分参数。
当通过命令行指定-dname选项时,若某个参数值中出现了逗号,需要使用“\”转义;当通过标准输入指定-dname选项时,参数值中的逗号不需要进行转义。示例如下。
cn=peter schuster, o=Sun Microsystems\, Inc., o=sun, c=us
keytool为密钥与证书管理工具,用于管理包含了加密密钥、X.509证书链和受信任证书的密钥库(数据库)。
keytool命令在Java SE 6中发生了如下改变:
-export, 改名为-exportcert
-genkey, 改名为-genkeypair
-import, 改名为-importcert
假设你需要创建密钥库,用于管理你的公/私钥对,以及你信任的实体的证书。可参考以下步骤。
你需要做的第一件事是创建一个密钥库并产生密钥对,可以使用keytool命令的-genkeypair命令。
目前我们已获得了一个自签名证书。如果证书由CA签名,它更有可能被其他人信任。为了得到被CA签名的证书,首先需要产生证书签名请求,可以使用keytool命令的-certreq命令。
将证书签名请求文件发送至CA,例如VeriSign等。CA将会验证您请求者的身份(通常是离线的),然后会返回一个证书,该证书由他们签名,能验证你的公钥。
你需要将你的自签名证书替换为证书链,证书链中的每个证书对前一个证书的签名者的公钥进行认证,直到"根"CA。
在导入CA的证书回复之前,你需要一个或多个"受信任"证书在你的密钥库或cacerts密钥库文件中。
导入CA证书时,可使用keytool命令的-importcert命令。
当你导入了用于验证接收你的证书签名请求的CA的证书时(或者在"cacerts"文件中已经存在相应的证书时),你可以导入证书回复从而将你的自签名证书替换为证书链。当CA的回复是证书链时,替换后的证书链是CA对你的请求的回复;当CA的回复是单独的证书时,替换后的证书链是由证书回复与受信任证书构造而成,受信任证书需要已经保存在你导入证书回复的密钥库中,或者在"cacerts"密钥库文件中。
导入CA提供的证书回复时,可使用keytool命令的-importcert命令。
客户端可能需要验证你的签名,在这种情况下,可以将你的公钥证书导出。
导出用于认证公钥的证书时,可使用keytool命令的-exportcert命令。
importkeystore命令可以将整个密钥库导入另外的密钥库中,源密钥库中的所有条目,包括密钥与证书均会被导入目标密钥库中。importkeystore命令也可以用于将源密钥库中的单个条目导入目标密钥库中。
alias选项指定密钥库操作的条目对应的别名,默认值为"mykey"。
keyalg选项指定生成密钥对或私钥时使用的算法。
JRE版本 | keyalg默认值 |
---|---|
1.5 | DSA |
1.6 | DSA (使用-genkeypair) |
1.6 | DES (使用-genseckey) |
1.7 | DSA (使用-genkeypair) |
1.7 | DES (使用-genseckey) |
1.8 | DSA (使用-genkeypair) |
1.8 | DES (使用-genseckey) |
keysize选项指定生成的密钥长度。
JRE版本 | keysize默认值 |
---|---|
1.5 | 1024 |
1.6 | 1024 (使用-genkeypair) |
1.6 | 56 (使用-genseckey且-keyalg选项为"DES") |
1.6 | 168 (使用-genseckey且-keyalg选项为"DESede") |
1.7 | 2048 (使用-genkeypair且-keyalg为"RSA") |
1.7 | 1024 (使用-genkeypair且-keyalg为"DSA") |
1.7 | 256 (使用-genkeypair且-keyalg为"EC") |
1.7 | 56 (使用-genseckey且-keyalg为"DES") |
1.7 | 168 (使用-genseckey且-keyalg为"DESede") |
1.8 | 2048 (使用-genkeypair且-keyalg为"RSA") |
1.8 | 1024 (使用-genkeypair且-keyalg为"DSA") |
1.8 | 256 (使用-genkeypair且-keyalg为"EC") |
1.8 | 56 (使用-genseckey且-keyalg为"DES") |
1.8 | 168 (使用-genseckey且-keyalg为"DESede") |
validity选项指定有效期,单位为天,默认值为90。
keystore选项指定密钥库文件位置,默认值为用户目录的.keystore文件。
storetype选项指定密钥库的存储类型,默认值为Java安全属性文件java.security中的"keystore.type"属性值(jks)。使用java.security.KeyStore类的静态方法getDefaultType可获取该默认值。
java.security文件保存在java.home/lib/security目录中,与cacerts文件在同一目录中。
file选项指定进行数据导入导出时对应的文件位置,当执行数据导入操作时,默认值为标准输入;当执行数据导出操作时,默认值为标准输出。
protected选项默认值为false,选项解释见后文。
sigalg选项指定对自签名证书或证书签名请求进行签名时使用的算法,sigalg对应的算法需要与keyalg对应的算法兼容。
在生成公/私钥对时,sigalg选项对应的签名算法从底层私钥的算法衍生而来。
JRE版本 | sigalg选项默认值 |
---|---|
1.5 | SHA1withDSA(底层私钥为DSA类型) |
1.5 | MD5withRSA(底层私钥为RSA类型) |
1.6 | SHA1withDSA (底层私钥为DSA类型) |
1.6 | SHA256withRSA (底层私钥为RSA类型) |
1.7 | SHA1withDSA (底层私钥为DSA类型) |
1.7 | SHA256withRSA (底层私钥为RSA类型) |
1.7 | SHA256withECDSA (底层私钥为EC类型) |
1.8 | SHA1withDSA (底层私钥为DSA类型) |
1.8 | SHA256withRSA (底层私钥为RSA类型) |
1.8 | SHA256withECDSA (底层私钥为EC类型) |
除keytool的-help命令外,-v选项可在其他所有命令中使用。当指定了-v选项时,这表明是详细模式,更多的信息将会被输出。-v选项可用于查看详细的错误信息。
-J选项可在keytool的所有命令中使用。当指定了-J选项时,由-J选项指定的java选项字符串将会直接传送给Java解释器。在指定的java选项中不应出现空格。在控制台中输入java -h或java -X命令,可以查看-J选项的可用解释器选项。例如"keytool -J-version"可查看java版本。
当使用keytool操作密钥库时,以下选项均可能出现。
各版本的JDK支持的密钥库类型可查看Oracle的JAVA密码体系标准算法名称文档。
JRE版本 | URL |
---|---|
1.5 | http://docs.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html#AppA |
1.6 | http://docs.oracle.com/javase/6/docs/technotes/guides/security/StandardNames.html#KeyStore |
1.7 | http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyStore |
1.8 | http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyStore |
JDK1.5支持的密钥库类型包括JKS、PKCS12。
JDK1.6支持的密钥库类型包括JKS、PKCS12、JCEKS。
JDK1.7支持的密钥库类型包括JKS、PKCS12、JCEKS。
JDK1.8支持的密钥库类型包括JKS、PKCS12、JCEKS、DKS、PKCS11。
各版本的JDK支持的密钥库说明可参考Oracle的JAVA密码体系说明文档。
JRE版本 | URL |
---|---|
1.5 | http://docs.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html#KeystoreImplementation |
1.6 | http://docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html#KeystoreImplementation |
1.7 | http://docs.oracle.com/javase/7/docs/technotes/guides/security/crypto/CryptoSpec.html#KeystoreImplementation |
1.8 | http://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#KeystoreImplementation |
各密钥库类型的说明如下。
JKS是内置的默认密钥库实现类型,由Sun Microsystems提供。JKS类型将密钥库以文件形式实现,利用专有的密钥库类型。它对每个私钥使用其私有的密码进行保护,同时对整个密钥库使用一个密码(有可能不同)保护其完整性。
JKS作为默认密钥库类型的配置方式见上文。
注意:
密钥库类型名称不区分大小写。例如,“jks"被认为等同于"JKS”。
"JCEKS"是另一种替代的专用密钥库格式,与"JKS"相比,"JCEKS"使用更健全的加密方式,使用基于口令的3DES加密。
Sun的"JCEKS"实现可以对"JKS"密钥库解析并转换为"JCEKS"格式。你可以将"JKS"类型的密钥库升级为"JCEKS"类型,通过修改密钥库中私钥条目的密码,需要指定"-storetype jceks"作为密钥库类型。示例命令如下:
keytool -keypasswd -alias xxx -storetype jceks
"PKCS12"是另一种选项,这是一种跨平台的密钥库,基于RSA PKCS12个人信息交换标准。这个标准主要用于存储或传输用户的私钥、证书以及各种秘密。
"dks"是域密钥库,它是密钥库的集合,表现为一个逻辑上单独的密钥库。
当密钥库的类型为pkcs11时,为支持PKCS#11令牌的密钥库。
各版本的JDK对于PKCS#11支持的说明文档如下。
JRE版本 | URL |
---|---|
1.5 | http://docs.oracle.com/javase/1.5.0/docs/guide/security/p11guide.html |
1.6 | http://docs.oracle.com/javase/6/docs/technotes/guides/security/p11guide.html |
1.7 | http://docs.oracle.com/javase/7/docs/technotes/guides/security/p11guide.html |
1.8 | http://docs.oracle.com/javase/8/docs/technotes/guides/security/p11guide.html |
密钥库基于提供者实现,以下为Oracle关于实现JAVA密码体系提供者的说明文档。
JRE版本 | URL |
---|---|
1.5 | http://docs.oracle.com/javase/1.5.0/docs/guide/security/HowToImplAProvider.html |
1.6 | http://docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/HowToImplAProvider.html |
1.7 | http://docs.oracle.com/javase/7/docs/technotes/guides/security/crypto/HowToImplAProvider.html |
1.8 | http://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/HowToImplAProvider.html |
见上文"keytool命令默认值"部分。
storepass选项对应的密码用于保护密钥库的完整性。storepass必须为至少6个字节。当需要操作密钥库内容时,所有的命令都需要指定storepass选项。对于这些命令,如果-storepass选项未在命令行中指定,keytool将会提示用户。
当从密钥库检索信息时,密码是可选的;如果不指定密码,被检索信息的完整性将无法检查,且会有警告显示。
providerName选项用于指定加密服务提供者的名字,提供者需要在安全属性文件中。
Oracle对于提供者的说明文档可参考前文JCA说明文档"CryptoSpec.html"或实现JAVA密码体系提供者的说明文档"HowToImplAProvider.html"。
提供者体系结构在设计时围绕以下原则:
Java应用不需要实现安全算法,它们可以从Java平台请求安全服务。安全服务在提供者中实现,在Java平台中已通过标准接口集成提供者。Java应用可以依赖多个独立的用于安全功能的提供者。
提供者在各应用中都可互用,而且一个应用没有绑定给一个特定的提供者,一个提供者也没有绑定给一个特定的应用。
Java平台包含了一些内置的提供者,这些提供者实现了一套如今被广泛使用的基本安全服务。然而,某些应用可能依赖还未实现的新标准,或者专有服务。Java平台支持实现上述服务的自定义提供者的安装。
keytool在执行时依赖Java运行环境,见前文“keytool执行方式简单分析”部分,因此keytool在操作密码库时也依赖相应的提供者。
Oracle在Java密码体系结构说明文档中说明了如何安装提供者。
JRE版本 | URL |
---|---|
1.5 | http://docs.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html#ProviderInstalling |
1.6 | http://docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html#ProviderInstalling |
1.7 | http://docs.oracle.com/javase/7/docs/technotes/guides/security/crypto/CryptoSpec.html#ProviderInstalling |
1.8 | http://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#ProviderInstalling |
Oracle在关于实现JAVA密码体系提供者的说明文档也说明了如何安装提供者。
JRE版本 | URL |
---|---|
1.5 | http://docs.oracle.com/javase/1.5.0/docs/guide/security/HowToImplAProvider.html#Step 5 |
1.6 | http://docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/HowToImplAProvider.html#Step71 |
1.7 | http://docs.oracle.com/javase/7/docs/technotes/guides/security/crypto/HowToImplAProvider.html#Step71 |
1.8 | http://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/HowToImplAProvider.html#Step71 |
当需要使用自定义的提供者时,需要在使用它的客户端中安装。安装后将使Java安全结构可以在客户端请求时找到自定义的算法实现。
安装提供者分为两个步骤:安装提供者包与类;以及配置提供者。
你必须做的第一件事是使你的类可用,以使得它们在被请求时可被找到。你需要将你的提供者类处理为JAR文件。
有两种可行的方式安装提供者类:
当提供者的JAR文件被放置到/lib/ext(指JAVA运行时环境的安装路径)目录后,这个JAR文件被认为是一个被安装(installed)的扩展。
在Java应用的manifest文件中通过Class-Path属性配置的JAR包被认为是被捆绑(bundled)的扩展。
下一个步骤是将提供者添加至你的合法提供者列表中,这个操作通过编辑Java安全属性文件java.security实现,该文件路径见前文。
在Java安全属性文件中,每个提供者都通过以下形式进行配置:
security.provider.n=masterClassName
上述的配置声明了一个提供者,并指定它的优先顺序为n。当请求算法没有指定提供者时,会使用该优先顺序对提供者进行搜索。该优先顺序从1开始。
masterClassName选项必须指定提供者的主类的完整名称。该类必须是Provider类的子类。
Java包含了标准的提供者,如SUN、SunRsaSign、SunJCE等,它们已被作为静态提供者自动配置在java.security文件中。
为了利用其他的JCA提供者,需要在java.security文件中添加一行注册提供者,其优先顺序应比SUN和SunRsaSign的提供者低。
提供者也可以动态注册。在进行动态注册时,Java程序可以调用Security类的addProvider或insertProviderAt方法。动态注册不是持久的,在进行动态注册时,程序需要具有以下权限:
java.security.SecurityPermission "insertProvider.{name}"
上述的{name}应被替换为实际的提供者名称。
示例如下:假设提供者名称为"MyJCE",提供者代码为/localWork目录中的myjce_provider.jar文件,以下为对上述权限进行授权的策略文件的授权语句示例:
grant codeBase "file:/localWork/myjce_provider.jar" {
permission java.security.SecurityPermission
"insertProvider.MyJCE";
};
Oracle关于Sun提供的提供者说明文档如下。
JRE版本 | URL |
---|---|
1.5 | http://docs.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html#ProviderArch |
1.6 | http://docs.oracle.com/javase/6/docs/technotes/guides/security/SunProviders.html |
1.7 | http://docs.oracle.com/javase/6/docs/technotes/guides/security/SunProviders.html |
1.8 | http://docs.oracle.com/javase/6/docs/technotes/guides/security/SunProviders.html |
Sun提供的提供者如下:
SunPKCS11 Provider
SUN Provider
SunRsaSign Provider
SunJSSE Provider
SunJCE Provider
SunJGSS Provider
SunSASL Provider
XMLDSig Provider
SunPCSC Provider
SunMSCAPI Provider
以下为部分Sun提供的提供者及对应的密钥库类型:
Sun提供者 | KeyStore |
---|---|
SUN Provider | JKS |
SunJCE Provider | JCEKS |
SunJSSE Provider | PKCS12 |
当使用keytool命令时,provider选项与storetype选项值需对应,否则会出现错误“java.security.NoSuchAlgorithmException: no such algorithm: [storetype] for provider [provider]”。
providerName与storetype的对应关系见上文,providerName选项可省略,以下为合法的providerName与storetype组合:
-storetype jks -providerName SUN
-storetype jceks -providerName SunJCE
-storetype PKCS12 -providerName SunJSSE
在java.security文件中可找到Sun的提供者对应的Java类。
SUN Provider对应的类为sun.security.provider.Sun,对其进行反编译可看到“KeyStore.JKS”配置。
SunJCE Provider对应的类为com.sun.crypto.provider.SunJCE,对其进行反编译可看到“KeyStore.JCEKS”配置。
SunJSSE Provider对应的类为com.sun.net.ssl.internal.ssl.Provider,对其进行反编译可看到“KeyStore.PKCS12”配置。
注意:
在使用-genkeypair或-genseckey命令创建密钥对或创建秘密密钥时需要指定storetype选项(不指定则使用默认值),使用-list进行查看时需要使用对应的storetype选项,否则会出现错误“java.io.IOException: Invalid keystore format”。
providerClass选项作用如下:当密码服务提供者的主类文件名未出现在安全属性文件中时,可通过providerClass选项指定。
providerArg选项与-providerClass选项结合使用,表示一个作为provider_class_name构造函数可选的字符串输入参数。
protected选项的可选值为true或false。当密码必须通过受保护的认证方式输入时(如 dedicated PIN reader专用密码器),protected选项的值必须为true。
-genkeypair命令可以产生密钥对(一个公钥与关联的私钥),将公钥打包至X.509 v3自签名证书中,证书以单元素证书链的形式存储。该证书链与私钥存储在密钥库的新条目中,通过-alias选项进行标识。
keyalg选项指定了生成密钥对使用的算法。keysize选项指定了需要生成的每个密钥的长度。sigalg选项指定了对自签名证书进行签名的算法,该算法必须与keyalg选项兼容。
dname选项指定与别名关联的X.500识别名,dname选项对应自签名证书的发布者与主体域。如果未在命令行中指定dname选项,将会提示用户输入。
Oracle的keytool文档的“X.500 Distinguished Names”部分对X.500识别名进行了说明,可参考本文对应部分。
keypass选项是用于保护生成的密钥库的私钥的密码。如果未在命令行中指定keypass选项,将会提示用户输入。若用户在出现提示时直接按回车,keypass将会被设置为与密钥库密码相同。keypass必须至少为6个字符。
validity选项指定了证书的有效期,以天为单位。
在早期的版本中,该命令的名字为-genkey。在当前版本与以后的版本中,旧名称仍然被支持,但推荐使用新名字-genkeypair。
-genseckey命令可以创建一个秘密密钥并将其存储在新的SecretKeyEntry中,通过-alias选项进行标识。
keyalg选项指定了生成秘密密钥使用的算法。keysize选项指定了需要生成的每个密钥的长度。keypass选项是用于保护生成的秘密密钥的密码。如果未在命令行中指定keypass选项,将会提示用户输入。若用户在出现提示时直接按回车,keypass将会被设置为与密钥库密码相同。keypass必须至少为6个字符。
-importcert命令读取file选项指定的证书或证书链(证书链为PKCS#7格式回复),并将其保存至密钥库中alias选项指定的条目中。若未指定file选项,则从标准输入读取证书或PKCS#7回复。
keytool可以导入X.509版本1、版本2、版本3的证书,以及PKCS#7格式的证书组成的证书链。被导入的数据需要为二进制格式,或可打印的编码格式(即Base64编码)。当导入Base64编码格式的证书时,证书数据的头部需要以"-----BEGIN"开头,尾部需要以"-----END"开头。
以下两种情况需要将证书导入密钥库:
使用何种类型进行导入由-alias选项的值指示:
当使用-importcert命令导入新的受信任证书时,keytool首先尝试从被导入证书到自签名证书(属于根CA)构造信任链,通过这种方式对被导入证书进行验证。在上述过程中,会使用已保存在密钥库中的受信任证书。
当指定了-trustcacerts选项时,cacerts文件中的证书也会作为受信任证书参与上述验证。
当使用-importcert命令导入证书回复时,证书回复会被密钥库中的受信任证书进行验证,当指定了-trustcacerts选项时,配置在cacerts文件中的证书也会参与验证。
当进行验证的受信任证书已被导入密钥库中而不是保存在cacerts中时,导入受信任证书或证书回复时不需要指定-trustcacerts选项。
在早期的版本中,该命令的名字为-import。在当前版本与以后的版本中,旧名称仍然被支持,但推荐使用新名字-importcert。
-importkeystore命令可以将一个单独的或全部的条目从源密钥库中导入目标密钥库中。
当指定了srcalias选项时,该命令将别名对应的条目导入至目标密钥库中。若未指定destalias选项,则会使用srcalias选项作为目标别名。若源条目受密码保护,需要通过srckeypass选项指定,用于恢复条目。当未指定srckeypass选项时,会尝试使用srcstorepass选项恢复条目。当未指定srcstorepass选项或不正确时,会提示用户输入密码。目标条目将会使用destkeypass作为密码进行保护。若未指定destkeypass选项,则会使用源条目密码进行保护。
当未指定srcalias选项时,源密钥库中的全部条目都会会导入目标密钥库中。每个目标密钥库中的条目都会使用源密钥库对应的别名存储。若源条目受密码保护,srcstorepass选项为源密钥库密码用于恢复条目。若srcstorepass选项未指定或不正确,会提示用户输入密码。当源密钥库中某个条目的类型不受目标密钥库支持时,或将条目存储至目标密钥库时出现错误时,将会提示用户跳过当前条目并继续,还是退出。目标条目会使用源条目的密码进行保护。
当目标别名已在目标密钥库中存在时,会提示用户覆盖对应的条目还是创建新的别名。
-certreq命令可以创建证书签名请求,使用PKCS#10格式。
alias选项关联的私钥与X.500识别名用于创建PKCS#10证书请求。为了访问私钥,需要提供正确的密码。当未在命令行中指定私钥密码,且私钥密码与密钥库密码不同时,将会提示用户输入。
sigalg选项指定了对证书签名请求进行签名的算法。
证书签名请求将会保存在-file选项指定的文件中,若未指定-file选项,证书签名请求将会通过标准输出进行显示。
-exportcert命令从密钥库中读取与-alias选项关联的证书,并将其存储在-file选项指定的文件中。若未指定-file选项,则证书会通过标准输出进行显示。
证书将会默认以二进制形式编码进行输出。当指定了-rfc选项时,证书将会以Internet RFC 1421标准定义的可打印编码格式进行输出,以“-----BEGIN CERTIFICATE-----”开始,以“-----END CERTIFICATE-----”结尾。
若-alias选项对应一个受信任证书,证书将会被显示。若-alias选项对应证书链关联的密钥条目,则会返回证书链中的第一个证书。证书可对-alias选项对应的实体的公钥进行认证。
在早期的版本中,该命令的名字为-export。在当前版本与以后的版本中,旧名称仍然被支持,但推荐使用新名字-exportcert。
-list命令用于打印-alias选项标识的密钥库条目的内容。如果未指定-alias选项,密钥库的全部内容将会被打印。
该命令默认打印证书的MD5指纹。当指定了-v选项时,会使用可读的形式进行打印,包含额外的信息,如所有者、所有者、序列号等。当指定了-rfc选项时,证书内容会以Internet RFC 1421标准定义的可打印编码格式进行输出。
-v与-rfc选项不可以同时指定。
-printcert命令从-file选项指定的文件中读取证书,并将其内容以可读的形式进行指定。若未指定-file选项,则从标准输入读取证书。
被查看的证书可为二进制编码形式或Internet RFC 1421标准定义的可打印编码格式。
-storepasswd命令用于修改保护密钥库内容完整性的密码。新密码必须为至少6个字符。
-keypasswd命令用于修改-alias选项对应的私钥或密钥的密码。新密码必须为至少6个字符。
若未在命令行中指定keypass选项,且私钥密码与密钥库密码不同时,将会提示用户输入。
当未在命令行中通过-new选项指定新密码时,将会提示用户输入。
-delete命令用于从密钥库中删除-alias选项对应的条目。当未指定-alias选项时,将会提示输入。
-changealias命令可以将现有的密钥库条目修改为新的别名。当未指定目标别名时,命令将进行提示。当原始条目受密码保护时,可通过-keypass选项指定该密码。当未指定密钥密码时,会首先使用密钥库密码进行尝试(若已指定了密钥库密码),若密钥密码与密钥库密码不同时,将会提示用户。
-help命令可列出基本命令及其选项。
在将证书导入为受信任证书时,需要进行仔细的检查。首先需要查看证书,可以使用-printcert命令,或使用-importcert命令不指定-noprompt选项。确保显示的证书指纹与预期相符。
然后联系证书的发送者,比较你看到的证书指纹与他们提供的是否一致。仅当指纹一致时才能确保证书在传输过程中未被篡改为其他人的证书(如攻击者)。
keytool的大多数操作密钥库的命令需要输入密钥库密码,某些命令需要输入私钥或密钥的密码。
密码可通过命令行指定(通过-storepass与-keypass选项)。仅当用于测试或在安全的系统中时,才允许在命令行或事指定密码。
当执行需要输入密码的命令时,若未在命令行中指定,命令将会提示输入密码。
以下为密钥库的配置示例(Windows环境,JDK1.6)。
示例命令如下:
rem 在keystore中生成密钥对
set keystore=serverKey.jks
set alias=serverKey_alias
set storepass=server_pass
set keypass=server_pass
set DNAME="CN=server_url, OU=server_OU, O=server_O, L=server_city, ST=server_province, C=CN"
set validity=3650
keytool -genkeypair -alias %alias% -keyalg RSA -keysize 2048 -sigalg SHA256withRSA -keystore %keystore% -storetype jks -storepass %storepass% -keypass %keypass% -dname %DNAME% -validity %validity%
当生成密钥对成功时,无提示信息。当指定的别名已在密钥库中存在时,提示的错误信息为“keytool错误: java.lang.Exception: 没有创建键值对,别名 已经存在”。
示例命令如下:
rem 生成csr
set csr=certreq.csr
set keystore=serverKey.jks
set alias=serverKey_alias
set storepass=server_pass
set keypass=server_pass
keytool -certreq -alias %alias% -file %csr% -keystore %keystore% -sigalg SHA256withRSA -keypass %keypass% -storepass %storepass%
当生成证书签名请求成功时,无提示信息。
证书签名请求生成成功后,将其发送给CA,并等待CA返回CA证书与被CA签名的证书。
假设CA证书包含两级证书,示例命令如下:
rem 导入根CA证书
set keystore=serverKey.jks
set alias=ca_alias
set storepass=server_pass
set cert=cacert.crt
keytool -importcert -alias %alias% -keystore %keystore% -storepass %storepass% -file %cert% -noprompt
rem 导入中级CA证书
set keystore=serverKey.jks
set alias=ca2_alias
set storepass=server_pass
set cert=CA2.crt
keytool -importcert -alias %alias% -keystore %keystore% -storepass %storepass% -file %cert% -noprompt
当导入证书成功后,提示“认证已添加至keystore中”。根CA证书与中级CA证书的导入顺序可互换。
示例命令如下:
rem 导入CA返回的证书回复
set keystore=serverKey.jks
set alias=serverKey_alias
set storepass=server_pass
set cert=server_signed.crt
keytool -importcert -alias %alias% -keystore %keystore% -storepass %storepass% -file %cert%
在导入CA返回的证书回复前,需要将根CA与中级CA的证书导入,否则在导入被中级CA签名的证书回复时,会报错“keytool错误: java.lang.Exception: 无法从回复中建立链接”。
当导入证书回复成功时,提示“认证回复已安装在 keystore中”。
示例命令如下:
rem 导出证书
set keystore=serverKey.jks
set alias=serverKey_alias
set storepass=server_pass
set exportcert=export_server.crt
keytool -exportcert -alias %alias% -keystore %keystore% -file %exportcert% -storepass %storepass% -rfc
当导出证书成功时,提示“保存在文件中的认证 ”。
当指定-rfc选项时,会使用Base64格式输出证书,否则以二进制格式输出。
示例命令如下:
rem 查看证书
set cert=export_server.crt
keytool -printcert -file %cert%
显示的证书内容包含:所有者、发布者、序列号、有效期、证书指纹、签名算法名称、版本、扩展等信息。示例如下。
所有者:CN=xxx, OU=xxx, O=xxx, L=xxx, ST=xxx, C=xxx
发布者:CN=xxx, OU=xxx, O=xxx, L=xxx, ST=xxx, C=xxx
序列号:xxx
有效期: xxx 至 xxx
证书指纹:
MD5:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx
SHA1:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx
签名算法名称:xxx
版本: x
扩展:
1: ObjectId: xxxx Criticality=xxx
SubjectKeyIdentifier [
KeyIdentifier [
0000: xxxx xxxx
0010: xxxx
]
]
2: ObjectId: xxxx Criticality=xxx
BasicConstraints:[
CA:xxx
PathLen: xxx
]
3: ObjectId: xxxx Criticality=xxx
AuthorityKeyIdentifier [
KeyIdentifier [
0000: xxxx xxxx
0010: xxxx
]
]
4: ObjectId: xxxx Criticality=xxx
所有者信息对应使用-genkeypair命令生成密钥对时的-dname选项,有效期对应-validity选项,签名算法名称对应-sigalg选项。
BasicConstraints属性的CA属性表明当前证书是否为CA证书,为true时代表为CA证书,为false时代表非CA证书。
示例命令如下:
rem 在keystore中生成密钥
set keystore=test.jks
set alias=testKey_alias
set storepass=test_pass
set keypass=test_pass
keytool -genseckey -alias %alias% -keyalg DES -keysize 56 -keystore %keystore% -storepass %storepass% -keypass %keypass% -storetype jceks
当生成私钥成功时,无提示信息。
示例命令如下:
rem 查看密钥库内容
set keystore=serverKey.jks
set alias=serverKey_alias
set storepass=server_pass
keytool -list -v -keystore %keystore% -storepass %storepass% -storetype jks
keytool -list -v -keystore %keystore% -storepass %storepass% -storetype jks -alias %alias%
显示的密钥库内容包含:Keystore类型、Keystore提供者、条目数量及每个条目的信息。
密钥库条目的类型包含trustedCertEntry、PrivateKeyEntry、SecretKeyEntry。
当条目类型为trustedCertEntry时,显示的内容包含:别名名称、创建日期、条目类型及证书信息。
当条目类型为PrivateKeyEntry时,显示的内容包含:别名名称、创建日期、条目类型及证书信息。
当条目类型为SecretKeyEntry时,显示的内容包含:别名名称、创建日期、条目类型。
通过-importcert命令生成的条目类型为trustedCertEntry。
通过-genkeypair命令生成的条目类型为PrivateKeyEntry。
通过-genseckey命令生成的条目类型为SecretKeyEntry。
当keytool的list命令指定了alias选项时,只显示指定别名的信息,否则显示全部别名的信息。
若密钥库类型为jks,可不指定-storetype jks,若为其他类型,则需要通过-storetype指定密钥库类型,否则会报错“java.io.IOException: Invalid keystore format”。
示例命令如下:
rem 删除密钥库中的别名
set keystore=serverKey.jks
set alias=ca_alias
set storepass=server_pass
keytool -delete -alias %alias% -keystore %keystore% -storepass %storepass%
当删除别名成功时,无提示信息。
示例命令如下:
rem 密钥库格式转换
set keystore=clientKey.jks
set alias=clientKey_alias
set storepass=client_pass
set keypass=client_pass
set client_p12=client.p12
set pwd_new=123456
keytool -importkeystore -srckeystore %keystore% -destkeystore %client_p12% -srcstoretype JKS -deststoretype PKCS12 -srcstorepass %storepass% -deststorepass %pwd_new% -srcalias %alias% -destalias %alias% -srckeypass %keypass% -destkeypass %pwd_new%
以上命令用于将JKS格式的密钥库转换为PKCS#12格式的密钥库。
生成新密钥库成功时,无提示信息。
指定别名的信息,否则显示全部别名的信息。
若密钥库类型为jks,可不指定-storetype jks,若为其他类型,则需要通过-storetype指定密钥库类型,否则会报错“java.io.IOException: Invalid keystore format”。
示例命令如下:
rem 删除密钥库中的别名
set keystore=serverKey.jks
set alias=ca_alias
set storepass=server_pass
keytool -delete -alias %alias% -keystore %keystore% -storepass %storepass%
当删除别名成功时,无提示信息。
示例命令如下:
rem 密钥库格式转换
set keystore=clientKey.jks
set alias=clientKey_alias
set storepass=client_pass
set keypass=client_pass
set client_p12=client.p12
set pwd_new=123456
keytool -importkeystore -srckeystore %keystore% -destkeystore %client_p12% -srcstoretype JKS -deststoretype PKCS12 -srcstorepass %storepass% -deststorepass %pwd_new% -srcalias %alias% -destalias %alias% -srckeypass %keypass% -destkeypass %pwd_new%
以上命令用于将JKS格式的密钥库转换为PKCS#12格式的密钥库。
生成新密钥库成功时,无提示信息。