jenkov.tutorials系列(Java加密与解密的具体使用)
Java Cipher(javax.crypto.Cipher)类表示一种加密算法。 密码这一术语是密码学领域中加密算法的标准术语。 这就是为什么Java类称为Cipher而不是例如 加密器/解密器或其他东西。
创建Cipher
在使用Java Cipher之前,您只需创建Cipher类的实例。 通过调用带有参数的getInstance()方法来创建一个Cipher实例,该参数告诉您要使用哪种类型的加密算法。 这是创建Java Cipher实例的示例:
Cipher cipher = Cipher.getInstance("AES");
模式选择
某些加密算法可以在不同的模式下工作。 加密模式指定有关算法应如何加密数据的详细信息。 因此,加密模式会影响部分加密算法。
加密模式有时可以与多种不同的加密算法一起使用-就像附加在核心加密算法上的技术一样。 这就是为什么将这些模式与加密算法本身分开,而将其称为加密算法的“附加组件”。 这是一些最著名的密码模式
-
ECB - Electronic Codebook
-
CBC - Cipher Block Chaining
-
CFB - Cipher Feedback
-
OFB - Output Feedback
-
CTR - Counter
实例化密码时,可以将其模式附加到加密算法的名称中。 例如,要使用密码块链接(CBC)创建AES密码实例,请使用以下代码:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
由于密码块链接也需要“填充方案”,因此填充方案会附加在加密算法名称字符串的末尾。
请记住,默认的Java SDK加密提供程序并不支持所有加密算法和模式。 您可能需要安装像Bouncy Castle这样的外部提供程序,才能使用所需的模式和填充方案创建所需的Cipher实例。
初始化Cipher
您必须先对其进行初始化,然后才能使用Cipher实例。 密码的初始化是通过调用其init()方法完成的。 init()方法采用两个参数:
-
Encryption / decryption cipher operation mode.
-
Encryption / decryption key.
这是在加密模式下初始化密码实例的示例:
Key key = ... // get / create symmetric encryption key
cipher.init(Cipher.ENCRYPT_MODE, key);
这是在解密模式下初始化密码实例的示例:
Key key = ... // get / create symmetric encryption key
cipher.init(Cipher.DECRYPT_MODE, key);
加密和解密数据
为了使用Cipher实例加密或解密数据,您可以调用以下两种方法之一:
-
update()
-
doFinal()
update()和doFinal()有多个覆盖的版本,它们具有不同的参数。 我将在这里介绍最常用的版本。
如果必须加密或解密单个数据块,只需对数据调用doFinal()即可进行加密或解密。 这是一个加密示例:
byte[] plainText = "abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8");
byte[] cipherText = cipher.doFinal(plainText);
在解密数据的情况下,代码实际上看起来几乎相同。 请记住,密码实例必须初始化为解密模式。 这是解密单个密文块的样子:
byte[] plainText = cipher.doFinal(cipherText);
如果您必须加密或解密多个数据块,例如 一个大文件中有多个块,则对每个数据块调用一次update(),最后对最后一个数据块调用doFinal()。 这是加密多个数据块的示例:
byte[] data1 = "abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8");
byte[] data2 = "zyxwvutsrqponmlkjihgfedcba".getBytes("UTF-8");
byte[] data3 = "01234567890123456789012345".getBytes("UTF-8");
byte[] cipherText1 = cipher.update(data1);
byte[] cipherText2 = cipher.update(data2);
byte[] cipherText3 = cipher.doFinal(data3);
最后一个数据块需要调用doFinal()的原因是,某些加密算法需要填充数据以适合特定的密码块大小(例如8字节边界)。 但是-我们不想填充加密的中间数据块。 因此,对中间数据块的update()调用,对最后一个数据块的doFinal()调用。
解密多个数据块时,您还可以为中间数据块调用Cipher update()方法,为最后一个块调用doFinal()方法。 这是使用Java Cipher实例解密多个数据块的示例:
byte[] plainText1 = cipher.update(cipherText1);
byte[] plainText2 = cipher.update(cipherText2);
byte[] plainText3 = cipher.doFinal(cipherText3);
同样,必须将Cipher实例初始化为解密模式,此示例才能正常工作。
加密/解密字节数组的一部分
Java Cipher类的加密和解密方法可以对存储在字节数组中的部分数据进行加密或解密。 您只需将偏移量和长度传递给update()和/或doFinal()方法。 这是一个例子:
int offset = 10;
int length = 24;
byte[] cipherText = cipher.doFinal(data, offset, length);
此示例将从具有索引8和24个字节的字节开始加密(或解密,具体取决于密码的初始化)。
加密/解密为现有的字节数组
到目前为止,本教程中显示的所有加密和解密示例都已将加密或解密的数据返回到新的字节数组中。 但是,也可以将数据加密或解密为现有的字节数组。 这对于减少创建的字节数组的数量很有用。
通过将目标字节数组作为参数传递给update()和/或doFinal()方法,可以将数据加密或解密为现有的字节数组。 这是一个例子:
int offset = 10;
int length = 24;
byte[] dest = new byte[1024];
cipher.doFinal(data, offset, length, dest);
本示例将索引为10和24字节的字节中的数据加密,从偏移量0转发到目标字节数组。如果要为目标字节数组设置不同的偏移量,则有update()和doFinal()版本 需要额外的偏移量参数。 这是在dest数组中使用偏移量调用doFinal()方法的示例:
int offset = 10;
int length = 24;
byte[] dest = new byte[1024];
int destOffset = 12
cipher.doFinal(data, offset, length, dest, destOffset);
重用密码实例
初始化Java Cipher实例是一项昂贵的操作。 因此,重用Cipher实例是一个好主意。 幸运的是,Cipher类在设计时考虑了重用。
当您在Cipher实例上调用doFinal()方法时,Cipher实例将返回到初始化后的状态。 然后,可以使用Cipher实例再次加密或解密更多数据。
这是重用Java Cipher实例的示例:
Cipher cipher = Cipher.getInstance("AES");
Key key = ... // get / create symmetric encryption key
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] data1 = "abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8");
byte[] data2 = "zyxwvutsrqponmlkjihgfedcba".getBytes("UTF-8");
byte[] cipherText1 = cipher.update(data1);
byte[] cipherText2 = cipher.doFinal(data2);
byte[] data3 = "01234567890123456789012345".getBytes("UTF-8");
byte[] cipherText3 = cipher.doFinal(data3);
首先,创建并初始化Cipher实例,然后将其用于加密两个相干数据块。 注意对这两个数据块的update()然后doFinal()的调用。 现在,可以再次使用Cipher实例来加密更多数据。 这是通过对第三个数据块的doFinal()调用完成的。 调用完doFinal()之后,您可以使用相同的Java Cipher实例加密另一个数据块。
MessageDigest
Java MessageDigest类表示一种加密哈希函数,可以根据二进制数据计算消息摘要。当您收到一些加密的数据时,您将无法从数据本身中看到它是否在运输过程中被修改。消息摘要可以帮助减轻该问题。
为了能够检测到加密数据在传输中是否已被修改,发送者可以从数据中计算出消息摘要,并将其与数据一起发送。收到加密的数据和消息摘要后,您可以从数据中重新计算消息摘要,并检查计算出的消息摘要是否与随数据接收的消息摘要匹配。如果两个消息摘要匹配,则有可能在传输期间未修改加密数据。
要使消息摘要用作修改检测机制,必须满足几个条件。但是,确切的条件是密码学理论的一部分,因此您将必须访问该理论以阅读如何正确使用消息摘要。本教程仅说明如何在MessageDigest类中使用消息摘要的Java密码学API表示形式。
创建一个MessageDigest实例
要创建Java MessageDigest实例,请调用MessageDigest类的静态getInstance()方法。 这是创建MessageDigest实例的示例:
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
传递给getInstance()方法的text参数是要使用的具体消息摘要算法的名称。
消息摘要算法
Java密码学API支持以下消息摘要算法(尽管您的具体密码学提供程序可能支持更多!):
-
MD2
-
MD5
-
SHA-1
-
SHA-256
-
SHA-384
-
SHA-512
并非所有这些消息摘要算法都同样安全。 在撰写本文时,建议您使用SHA-256或更高版本以获得最大程度的安全性。
计算摘要
创建Java MessageDigest实例后,就可以使用它从数据中计算消息摘要。 如果您有单个数据块来计算消息摘要,请使用digest()方法。 这是从单个数据块计算消息摘要的样子:
byte[] data1 = "0123456789".getBytes("UTF-8");
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
byte[] digest = messageDigest.digest(data1);
如果您要在同一消息摘要中包含多个数据块,请调用update()方法并结束对digest()的调用。 这是从多个数据块计算消息摘要的样子:
byte[] data1 = "0123456789".getBytes("UTF-8");
byte[] data2 = "abcdefghijklmnopqrstuvxyz".getBytes("UTF-8");
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(data1);
messageDigest.update(data2);
byte[] digest = messageDigest.digest();
Mac
Java Mac(javax.crypto.Mac类可以从二进制数据创建消息身份验证代码(MAC)。MAC是已用密钥加密的消息摘要。只有拥有密钥后,才能验证MAC 。
创建Mac实例
必须先创建Mac实例,然后才能使用Java Mac类。 使用getInstance()方法创建Mac实例。 这是一个Java Mac实例化示例:
Mac mac = Mac.getInstance("HmacSHA256");
传递给Mac getInstance()方法的String参数包含要使用的MAC算法的名称。 在这种情况下,MAC算法为HmacSHA256。
初始化
创建后,必须初始化Java Mac实例。 您可以通过调用Mac实例的init()方法来初始化Mac实例,该方法将Mac实例要使用的密钥作为参数传递。 这是一个Java Mac初始化示例:
byte[] keyBytes = new byte[]{0,1,2,3,4,5,6,7,8 ,9,10,11,12,13,14,15};
String algorithm = "RawBytes";
SecretKeySpec key = new SecretKeySpec(keyBytes, algorithm);
mac.init(key);
Mac的init()方法采用一个Key实例。 在此示例中,使用SecretKeySpec来实现Key接口。
计算Mac
初始化Java Mac实例后,您可以开始使用它计算MAC值。 要计算MAC值,请调用Mac update()或doFinal()方法。 如果只有一个数据块可为其计算MAC,则可以直接调用doFinal(),如下所示:
byte[] data = "abcdefghijklmnopqrstuvxyz".getBytes("UTF-8");
byte[] macBytes = mac.doFinal(data);
如果您有多个数据块来计算其MAC,例如 如果要逐块读取文件,则必须在每个块中调用update()方法,并以对doFinal()的调用结束。 这是一个例子:
byte[] data = "abcdefghijklmnopqrstuvxyz".getBytes("UTF-8");
byte[] data2 = "0123456789".getBytes("UTF-8");
mac.update(data);
mac.update(data2);
byte[] macBytes = mac.doFinal();
Signature
Java Signature类(java.security.Signature)可以为二进制数据创建数字签名。 数字签名是使用私钥/公钥对的私钥加密的消息摘要。 拥有公钥的任何人都可以验证数字签名。
创建实例
必须先创建一个Signature实例,然后才能使用Java Signature类。 您可以通过调用静态getInstance()方法来创建一个Signature实例。 这是一个创建Java签名实例的示例:
Signature signature = Signature.getInstance("SHA256WithDSA");
作为参数传递给getInstance()方法的String是要使用的数字签名算法的名称。
初始化
创建Java签名实例后,需要先对其进行初始化,然后才能使用它。 您可以通过调用签名实例的init()方法来初始化它。 这是一个Java签名初始化示例:
SecureRandom secureRandom = new SecureRandom();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
KeyPair keyPair = keyPairGenerator.generateKeyPair();
signature.initSign(keyPair.getPrivate(), secureRandom);
如您所见,Signature实例使用私钥/公钥对的私钥和SecureRandom实例进行初始化。
创建数字签名
签名实例初始化后,您可以使用它来创建数字签名。 您可以通过多次调用update()方法来创建数字签名,最后再调用sign()。 这是为二进制数据块创建数字签名的示例:
Signature signature = Signature.getInstance("SHA256WithDSA");
signature.initVerify(keyPair.getPublic());
请注意,现在如何将Java Signature实例初始化为验证模式,并将公钥/私钥对中的公钥作为参数传递。
初始化为验证模式后,您可以使用Signature实例来验证数字签名。 这是验证数字签名的样子:
byte[] data2 = "abcdefghijklmnopqrstuvxyz".getBytes("UTF-8");
signature2.update(data2);
boolean verified = signature2.verify(digitalSignature);
KeyPair
Java KeyPair类(java.security.KeyPair表示非对称密钥对。换句话说,是公钥+私钥对。执行非对称加密(例如,加密或签名数据)时通常使用KeyPair实例。
获取KeyPair实例
通常,您将从Java密钥库或Java KeyPairGenerator获得KeyPair实例。
访问密钥对公共密钥
您可以通过调用KeyPair的getPublic()方法来访问它的PublicKey。 她是一个从KeyPair实例获取PublicKey的示例:
PublicKey publicKey = keyPair.getPublic();
访问KeyPair私钥
您可以通过调用get Private()方法来访问密钥对的私钥。 这是从KeyPair实例获取PrivateKey的示例:
PrivateKey privateKey = keyPair.getPrivate();
KeyGenerator
Java KeyGenerator类(javax.crypto.KeyGenerator)用于生成对称加密密钥。 对称加密密钥是通过对称加密算法用于数据加密和解密的密钥。 在此Java KeyGenerator教程中,我将向您展示如何生成对称加密密钥。
创建实例
必须先创建KeyGenerator实例,然后才能使用Java KeyGenerator类。 您可以通过调用静态方法getInstance()来创建KeyGenerator实例,该方法将要为其创建密钥的加密算法的名称作为参数传递。 这是创建Java KeyGenerator实例的示例:
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
本示例创建一个KeyGenerator实例,该实例可以为AES加密算法生成密钥。
初始化
创建KeyGenerator实例后,必须对其进行初始化。 初始化KeyGenerator实例是通过调用其init()方法来完成的。 这是初始化KeyGenerator实例的示例:
SecureRandom secureRandom = new SecureRandom();
int keyBitSize = 256;
keyGenerator.init(keyBitSize, secureRandom);
KeyGenerator init()方法采用两个参数:要生成的密钥的位大小,以及在密钥生成期间使用的SecureRandom。
生成密钥
Java KeyGenerator实例初始化后,就可以使用它来生成密钥。 生成密钥是通过调用KeyGenerator generateKey()方法来完成的。 这是生成对称密钥的示例:
SecretKey secretKey = keyGenerator.generateKey();
KeyPairGenerator
Java KeyPairGenerator类(java.security.KeyPairGenerator)用于生成非对称加密/解密密钥对。 非对称密钥对由两个密钥组成。 第一密钥通常用于加密数据。 第二密钥,用于解密用第一密钥加密的数据。
公钥,私钥类型密钥对
最常见的非对称密钥对类型是密钥对的公钥和私钥类型。 私钥用于加密数据,公钥可以再次用于解密数据。 实际上,您还可以使用公钥加密数据,并使用私钥解密数据。
私钥通常被保密,而公钥则可以公开使用。 因此,如果杰克用他的私钥加密某些数据,那么拥有杰克公钥的每个人都可以解密它。
创建一个KeyPairGenerator实例
要使用Java KeyPairGenerator,必须首先创建一个KeyPairGenerator实例。 通过调用方法getInstance()方法来创建KeyPairGenerator实例。 这是创建Java KeyPairGenerator实例的示例:
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
getInstance()方法采用加密算法的名称来为其生成密钥对。 在此示例中,我们使用名称RSA。
初始化KeyPairGenerator
根据生成密钥对的算法的不同,您可能必须初始化KeyPairGenerator实例。 初始化KeyPairGenerator的方法是调用其initialize()方法。 这是初始化Java KeyPairGenerator实例的示例:
keyPairGenerator.initialize(2048);
本示例初始化KeyPairGenerator以生成2048位大小的密钥。
生成密钥对
要使用KeyPairGenerator生成KeyPair,请调用generateKeyPair()方法。 这是使用KeyPairGenerator生成KeyPair的示例:
KeyPair keyPair = keyPairGenerator.generateKeyPair();
KeyStore
Java KeyStore是可以包含密钥的数据库。 Java KeyStore由KeyStore(java.security.KeyStore)类表示。可以将KeyStore写入磁盘并再次读取。整个密钥库都可以用密码保护,并且密钥库中的每个键条目都可以用自己的密码保护。这使KeyStore类成为安全处理加密密钥的有用机制。
密钥库可以保存以下类型的密钥:
-
私钥
-
公钥+证书
-
秘钥
私钥和公钥用于非对称加密。公钥可以具有关联的证书。证书是一种文件,用于验证声称拥有公钥的个人,组织或设备的身份。证书通常由验证方进行数字签名以作为证明。
秘密密钥用于对称加密。在许多情况下,建立安全连接后会协商对称密钥。因此,与秘密密钥相比,您将更经常地将公共密钥和私有密钥存储在KeyStore中。
创建密钥库
您可以通过调用Java KeyStore实例的getInstance()方法来创建它。这是创建KeyStore实例的示例:
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
本示例创建Java默认类型的KeyStore实例。 也可以通过将不同的参数传递给getInstance()方法来创建其他类型的KeyStore实例。 例如,下面是创建PKCS12类型的KeyStore的示例:
KeyStore keyStore = KeyStore.getInstance("PKCS12");
加载密钥库
必须先加载KeyStore实例,然后才能使用它。 通常将KeyStore实例写入磁盘或其他类型的存储中,以备后用。 这就是为什么KeyStore类假定您必须先读取其数据,然后才能使用它。 但是,可以初始化一个没有数据的空KeyStore实例,这将在以后看到。
通过调用KeyStore load()方法可以从文件或其他存储中加载KeyStore数据。 load()有两个参数:
一个从其加载KeyStore数据的InputStream。
一个包含KeyStore密码的char [](char数组)。
这是加载Java KeyStore的示例:
char[] keyStorePassword = "123abc".toCharArray();
try(InputStream keyStoreData = new FileInputStream("keystore.ks")){
keyStore.load(keyStoreData, keyStorePassword);
}
本示例加载位于keystore.ks文件中的KeyStore文件。
如果您不想将任何数据加载到KeyStore中,只需为InputStream参数传递null。 这是加载空KeyStore的样子:
keyStore3.load(null, keyStorePassword);
您必须始终使用数据或null加载KeyStore实例。 否则,KeyStore将未初始化,并且对其方法的所有调用都将引发异常。
获取密匙
您可以通过Java KeyStore实例的getEntry()方法获取其键。 KeyStore条目映射到标识密钥的别名,并由密钥密码保护。 因此,要访问密钥,必须将密钥别名和密码传递给getEntry()方法。 这是访问KeyStore实例中的键条目的示例:
char[] keyPassword = "789xyz".toCharArray();
KeyStore.ProtectionParameter entryPassword =
new KeyStore.PasswordProtection(keyPassword);
KeyStore.Entry keyEntry = keyStore3.getEntry("keyAlias", entryPassword);
如果知道要访问的密钥条目是私钥,则可以将KeyStore.Entry实例强制转换为KeyStore.PrivateKeyEntry。 看起来是这样的:
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)
keyStore3.getEntry("keyAlias", entryPassword);
强制转换为KeyStore.PrivateKeyEntry之后,您可以通过以下方法访问私钥,证书和证书链:
- getPrivateKey()
-
getCertificate()
-
getCertificateChain()
设置密钥
您还可以将密钥设置为KeyStore实例。 这是在密钥库实例中设置秘密密钥(对称密钥)的示例:
SecretKey secretKey = getSecretKey();
KeyStore.SecretKeyEntry secretKeyEntry = new KeyStore.SecretKeyEntry(secretKey);
keyStore3.setEntry("keyAlias2", secretKeyEntry, entryPassword);
存储 KeyStore
有时您可能希望将KeyStore存储到某个存储(磁盘,数据库等),以便下次可以再次加载它。 您可以通过调用store()方法来存储KeyStore。 这是存储密钥库的示例
char[] keyStorePassword = "123abc".toCharArray();
try (FileOutputStream keyStoreOutputStream = new FileOutputStream("data/keystore.ks")) {
keyStore3.store(keyStoreOutputStream, keyStorePassword);
}
Keytool
Java Keytool是一种命令行工具,可以生成公用密钥/专用密钥对并将其存储在Java KeyStore中。 Keytool可执行文件随Java SDK(或JRE)一起分发,因此,如果安装了SDK,则还将具有Keytool可执行文件。
Keytool可执行文件称为keytool。 要执行它,请打开命令行(cmd,控制台,shell等)。 并将目录更改为Java SDK安装的bin目录。 键入keytool,然后按Enter键。 您应该看到类似以下内容:
C:\Program Files\Java\jdk1.8.0_111\bin>keytool
Key and Certificate Management Tool
Commands:
-certreq Generates a certificate request
-changealias Changes an entry's alias
-delete Deletes an entry
-exportcert Exports certificate
-genkeypair Generates a key pair
-genseckey Generates a secret key
-gencert Generates certificate from a certificate request
-importcert Imports a certificate or a certificate chain
-importpass Imports a password
-importkeystore Imports one or all entries from another keystore
-keypasswd Changes the key password of an entry
-list Lists entries in a keystore
-printcert Prints the content of a certificate
-printcertreq Prints the content of a certificate request
-printcrl Prints the content of a CRL file
-storepasswd Changes the store password of a keystore
Use "keytool -command_name -help" for usage of command_name
C:\Program Files\Java\jdk1.8.0_111\bin>
Keytool 脚本
Keytool命令带有很多参数,可能很难记住正确设置它们。 因此,最好在其中包含Keytool命令的情况下创建一些Keytool CMD或Shell脚本。这些脚本使以后重新执行keytool命令更容易,并且可以稍后再查看如何生成KeyStore。 。
生成密钥对
生成公钥/私钥对是使用Java Keytool的最常见任务之一。 生成的密钥对作为自签名密钥对插入到Java KeyStore文件中。 这是使用Keytool生成密钥对的常规命令行格式:
-genkeypair
-alias alias
-keyalg keyalg
-keysize keysize
-sigalg sigalg
-dname dname
-keypass keypass
-validity valDays
-storetype storetype
-keystore keystore
-storepass storepass
-providerClass provider_class_name
-providerArg provider_arg
-v
-protected
-Jjavaoption
参数在“ Keytool参数”部分中进行了说明。 并非所有这些论点都是必需的。 许多是可选的。 Keytool会告诉您是否缺少必需的参数。
换行符不应包含在最终命令中。 换行仅是为了使此处的命令格式更易于阅读。
这是一个示例keytool -genkeypair命令。 请记住在尝试之前删除换行符!
"C:\\Program Files\Java\jdk1.8.0_111\bin\keytool" -genkeypair
-alias testkey
-keyalg RSA
-keysize 2048
-dname "CN=Jakob Jenkov, OU=Jenkov Aps, O=Jenkov Aps, L=Copenhagen, ST=Unknown, C=DK"
-keypass 123456
-validity 100
-storetype JKS
-keystore keystore.jks
-storepass abcdef
导出证书
Java Keytool也可以导出存储在KeyStore中的证书。 这是Keytool命令查找导出证书的方式:
-exportcert
-alias alias
-file cert_file
-storetype storetype
-keystore keystore
-storepass storepass
-providerName provider_name
-providerClass provider_class_name
-providerArg provider_arg
-rfc
-v
-protected
-Jjavaoption
参数在“ Keytool参数”部分中进行了说明。 并非所有这些论点都是必需的。 许多是可选的。 Keytool会告诉您是否缺少必需的参数。
这是一个Keytool命令示例,该示例导出密钥对的证书。 在命令行上输入命令时,切记要删除换行符。
"C:\\Program Files\Java\jdk1.8.0_111\bin\keytool"
-exportcert
-alias testkey
-keypass 123456
-storetype JKS
-keystore keystore.jks
-file cert.cert
-rfc
-storepass abcdef
导入证书
Java Keytool也可以将证书导入KeyStore。 这是Keytool命令查找导入证书的方式:
-importcert
-alias alias
-file cert_file
-keypass keypass
-noprompt
-trustcacerts
-storetype storetype
-keystore keystore
-storepass storepass
-providerName provider_name
-providerClass provider_class_name
-providerArg provider_arg
-v
-protected
-Jjavaoption
参数在“ Keytool参数”部分中进行了说明。 并非所有这些论点都是必需的。 许多是可选的。 Keytool会告诉您是否缺少必需的参数。
这是一个示例Keytool命令,该命令将证书导入KeyStore。 在命令行上输入命令时,切记删除换行符
"C:\\Program Files\Java\jdk1.8.0_111\bin\keytool"
-importcert
-alias testkey
-keypass 123456
-storetype JKS
-keystore keystore2.jks
-file cert.cert
-rfc
-storepass abcdef
列出密钥库条目
要列出Java KeyStore中的条目,可以使用Keytool -list命令。 这是Keytool -list命令的格式。 换行仅是为了使命令格式更易于阅读。 运行命令之前,请删除换行符。
-list
-alias alias
-storetype storetype
-keystore keystore
-storepass storepass
-providerName provider_name
-providerClass provider_class_name
-providerArg provider_arg
-v
-rfc
-protected
-Jjavaoption
参数在“ Keytool参数”部分中进行了说明。 并非所有这些论点都是必需的。 许多是可选的。 Keytool会告诉您是否缺少必需的参数。
这是一个Keytool -list命令示例。 记住要删除换行符!
"C:\\Program Files\Java\jdk1.8.0_111\bin\keytool"
-list
-storetype JKS
-keystore keystore.jks
-storepass abcdef
此Keytool -list命令将列出给定KeyStore中的所有条目。 运行此Keytool -list命令的输出将类似于以下内容:
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 1 entry
testkey, 19-Dec-2017, PrivateKeyEntry,
Certificate fingerprint (SHA1): 4F:4C:E2:C5:DA:36:E6:A9:93:6F:10:36:9E:E5:E8:5A:6E:F2:11:16
如果在Keytool -list命令中包括-alias参数,则仅列出与给定别名匹配的条目。 这是带有-alias参数的示例Keytool -list命令:
"C:\\Program Files\Java\jdk1.8.0_111\bin\keytool"
-list
-alias testkey
-storetype JKS
-keystore keystore.jks
-storepass abcdef
运行上述Keytool -list命令的输出将类似于以下内容:
y -storetype JKS -keystore keystore.jks -storepass abcdef
testkey, 15-Dec-2017, PrivateKeyEntry,
Certificate fingerprint (SHA1): 71:B0:6E:F1:E9:5A:E7:F5:5E:78:71:DC:08:80:47:E9:5F:F8:6D:25
删除密钥库条目
Keytool具有一个可以删除Java KeyStore中的键条目的命令。 用于删除密钥的Keytool命令为-delete。 这是Keytool -delete命令的格式:
-delete
-alias alias
-storetype storetype
-keystore keystore
-storepass storepass
-providerName provider_name
-providerClass provider_class_name
-providerArg provider_arg
-v
-protected
-Jjavaoption
参数在“ Keytool参数”部分中进行了说明。 并非所有这些论点都是必需的。 许多是可选的。 Keytool会告诉您是否缺少必需的参数。
这是Keytool -delete命令示例。 记住在运行前删除换行符!
"C:\\Program Files\Java\jdk1.8.0_111\bin\keytool"
-delete
-alias testkey
-storetype JKS
-keystore keystore.jks
-storepass abcdef
此Keytool -delete命令将从存储在文件keystore.jks中的KeyStore中删除带有别名testkey的KeyStore条目。
生成证书申请
Java Keytool可以使用-certreq命令生成证书请求。 证书请求是对证书颁发机构(CA)为您的组织创建公共证书的请求。 证书请求生成后,应将其发送到您要为其创建证书的CA(例如Verisign,Thawte或其他CA)。
在为私钥,公钥对生成证书请求之前,您必须已将该私钥,公钥对生成到密钥库中(或将其导入)。
这是用于生成证书请求的命令格式。 试用此命令时,请记住删除所有换行符
-certreq
-alias alias
-sigalg sigalg
-file certreq_file
-keypass keypass
-storetype storetype
-keystore keystore
-storepass storepass
-providerName provider_name
-providerClass provider_class_name
-providerArg provider_arg
-v
-protected
-Jjavaoption
参数在“ Keytool参数”部分中进行了说明。 并非所有这些论点都是必需的。 许多是可选的。 Keytool会告诉您是否缺少必需的参数。
这是一个Java Keytool -certreq命令示例:
"C:\\Program Files\Java\jdk1.8.0_111\bin\keytool"
-certreq
-alias testkey
-keypass 123456
-storetype JKS
-keystore keystore.jks
-storepass abcdef
-file certreq.certreq
此命令将为以别名testkey存储在密钥库文件keystore.jks中的密钥生成证书请求,并将证书请求写入名为certreq.certreq的文件中。
请记住,仅包含换行符以使命令更易于阅读。 自己在命令行上键入命令时忽略它们
Certificate
Java证书类(java.security.cert.Certificate)表示密码身份证书。 Java证书类实例包含名称以及它标识的实体的其他详细信息,还可能包含来自证书颁发机构(CA)的数字签名。
Java Certificate类是一个抽象类,因此尽管您可以将Certificate用作变量类型,但您的变量将始终指向Certificate的子类。
Java证书类具有一个子类-X509Certificate类。 此类表示X.509证书,该证书在HTTPS和TLS中用作身份证书。
获取证书实例
您可以通过以下方式获取证书实例:
-
From a CertificateFactory.
-
From a KeyStore.
getEncoded()
Java证书的getEncoded()方法以字节数组形式返回证书的编码版本。 例如,如果证书是X509Certificate,则返回的字节数组将包含证书实例的X.590(ASN.1 DER)编码版本。 这是一个getEncoded()示例:
byte[] encodedCertificate = certificate.getEncoded();
getPublicKey()
Java Certificate getPublicKey()方法返回此Certificate实例的PublicKey。 这是一个getPublicKey()示例:
PublicKey certificatePublicKey = certificate.getPublicKey();
getType()
Java Certificate getType()方法返回Certificate实例的类型。 这是一个getType()示例:
String certificateType = certificate.getType();
verify()
Java证书类包含三个verify()方法。 这些方法可用于验证证书是否已使用与预期公钥匹配的私钥进行了真正的签名。 这是一个Java证书verify()示例:
// get expected public key from somewhere else (not Certificate instance !!)
PublicKey expectedPublicKey = ... ;
try{
certificate.verify(expectedPublicKey);
} catch (InvalidKeyException e) {
// certificate was not signed with given public key
} catch (NoSuchAlgorithmException |
NoSuchProviderException |
SignatureException |
CertificateException e){
// something else went wrong
}
verify()方法返回void。 如果验证失败,则将引发InvalidKeyException。 如果未抛出异常,则可以将Certificate实例视为已验证。
CertificateFactory
Java CertificateFactory类(java.security.cert.CertificateFactory)能够从X.509(ASN.1 DER)之类的二进制证书编码创建Java证书实例。
Java CertificateFactory也可以创建CertPath实例。 CertPath是证书链,其中链中的每个证书由链中的下一个证书签名。 有关CertPath类的更多信息,请参见Java CertPath教程。
创建一个CertificateFactory实例
在创建证书实例之前,必须创建一个Java CertificateFactory实例。 这是创建CertificateFactory的示例:
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
本示例创建一个CertificateFactory实例,该实例能够创建X.509证书实例(X509Certificate-Certificate的子类)。
创建证书实例
创建CertificateFactory实例后,即可开始创建Certificate实例。 您可以通过generateCertificate()方法进行操作。 这是一个generateCertificate()示例:
InputStream certificateInputStream = new FileInputStream("my-x509-certificate.crt");
Certificate certificate = certificateFactory.generateCertificate(certificateInputStream);
创建一个CertPath实例
Java CertificateFactory也可以创建一个CertPath实例。 您可以通过调用CertificateFactory generateCertPath()方法来创建一个CertPath实例。 这是一个generateCertPath()示例:
InputStream certificateInputStream = new FileInputStream("my-x509-certificate-chain.crt");
CertPath certPath = certificateFactory.generateCertPath(certificateInputStream);
网友评论