Abbreviation for Java Cryptography Architecture. An API and framework for using cryptography in Java.
Java
openjdk 11.0.2
OS
Windows 10 (64bit)
To use JCA, you need basic knowledge about cryptographic technology (what kind of technology you have and what kind of mechanism it is). If you do not know this, you may not understand the meaning of class structure and correct usage. In the worst case, it may be used in a problematic way in actual usage situations, and a security hole may be embedded.
So, first of all, it is necessary to study cryptography in the first place.
See here for a description of cryptography.
Jshell is used for verification. Frequently used processes are defined as functions in advance and used without any particular explanation.
Predefined functions
import java.nio.*
import java.nio.file.*
import java.io.*
//Convert a byte array to a string in hexadecimal notation
String toHexString(byte[] bytes) {
ByteBuffer buffer = ByteBuffer.wrap(bytes);
StringBuilder sb = new StringBuilder();
for (int i=0; i<bytes.length; i+=4) {
sb.append(String.format("%08x", buffer.getInt()));
}
return sb.toString();
}
//Output byte array to file
void writeFile(String path, byte[] bytes) throws IOException {
Files.write(Paths.get(path), bytes);
}
//Read the file as a byte array
byte[] readFile(String path) throws IOException {
return Files.readAllBytes(Paths.get(path));
}
Since the operation verification is performed by JShell, the close ()
of the input / output stream is omitted to simplify the description.
Don't forget to do close ()
when you actually use it.
JCA is designed with the basic policy of achieving the following.
The various cryptographic technologies provided by JCA (encryption, hash functions, digital signatures, etc ...) consist of multiple implementations due to various circumstances (such as US law and the emergence of new cryptographic technologies). There is. This component that provides the implementation of cryptographic technology is called a ** encryption service provider ** (or simply a provider).
Implementation independence refers to making you unaware of the existence of providers that provide these implementations.
In other words, if you just declare "I want to use the cipher" without ** "I want to use the cipher, so I support it ... Specify the provider" **. After that, JCA will find a provider that supports it and resolve the implementation class.
Implementation interoperability refers to the ability to combine multiple providers.
In other words, use a provider to implement a hash function, use a provider to generate random numbers, use a *** provider for key generation, and use a @@@ provider for digital signatures and encryption. You can do it.
Usually, there are multiple algorithms in one cryptographic technique. For example, in the case of hash function (cryptographic technology), there are multiple algorithms such as SHA-1, SHA-2, SHA-3, MD5.
Algorithm independence means that cryptographic technology can be used without depending on a specific algorithm.
For example, the hash function is abstracted by a class called MessageDigest
.
This class defines an API that does not depend on a specific algorithm.
It is necessary to specify the algorithm when creating an instance of MessageDigest
, but the subsequent hashing process can be realized by the same implementation regardless of the algorithm.
Algorithm extensibility refers to the ease with which new cryptographic algorithms can be added and used when they are invented and implemented.
A component that provides an implementation of one or more cryptographic techniques is called a ** cryptographic service provider **. When we simply say "provider" in the context of JCA, we mean an encryption service provider.
At least one provider is installed in the JDK. Providers can be added statically or dynamically.
The providers that are actually installed by default in the JDK are described below (because it is an Oracle Java document, could it be different from OpenJDK?). JDK Provider Document
When creating an instance of a class related to a specific cryptographic technique, specify an algorithm to create the instance.
For example, to create an instance of MessageDigest
by specifying the SHA-256 algorithm, implement as follows.
jshell
jshell> import java.security.*
jshell> var md = MessageDigest.getInstance("SHA-256")
md ==> SHA-256 Message Digest from SUN, <initialized>
JCA then asks the installed providers one by one, "Does SHA-256 support MessageDigest
? "
It then gets the concrete implementation from the first supported provider found.
This allows applications to use cryptographic technology without being aware of the specific provider. It is possible to get an instance by specifying the name of the provider, but it is not recommended.
A class called java.security.Provider is prepared as a class representing the provider. ing.
All Provider
s installed in the current execution environment are getProviders () of java.security.Security
. It can be obtained with java.base/java/security/Security.html#getProviders ()).
jshell
jshell> import java.util.stream.*
jshell> Stream.of(Security.getProviders()).forEach(System.out::println)
SUN version 11
SunRsaSign version 11
SunEC version 11
SunJSSE version 11
SunJCE version 11
SunJGSS version 11
SunSASL version 11
XMLDSig version 11
SunPCSC version 11
JdkLDAP version 11
JdkSASL version 11
SunMSCAPI version 11
SunPKCS11 version 11
If you want to refer to it as a document, 4 JDK Provider Document I think you should look around.
A class that provides a specific cryptographic technology is called a ** engine class **.
Specific engine classes include (only some):
class | Functions to be provided |
---|---|
SecureRandom | Random number generator with unpredictability for cryptography |
MessageDigest | Hash function for cryptography |
Signature | Digital signature creation and verification |
Cipher | Encryption and decryption |
Mac | Message authentication code |
KeyGenerator | Private key generation |
KeyPairGenerator | Key pair generation |
KeyStore | Keystore that manages keys |
jshell
jshell> var md = MessageDigest.getInstance("SHA-256")
md ==> SHA-256 Message Digest from SUN, <initialized>
The engine class provides a static
factory method calledgetInstance ()
.
By specifying the name of the algorithm in the argument of this factory method, you can get the instance that implements the algorithm.
The algorithm name is case insensitive **.
Each engine class has an algorithm that must be supported by specification.
Which algorithms are required to be supported is described in the Javadoc for each engine class.
For example, in the case of the MessageDigest
class, the following three are required as of Java 11.
MessageDigest (Java SE 11 & JDK 11 )
jshell
jshell> var provider = Security.getProviders()[0]
provider ==> SUN version 11
jshell> var services = provider.getServices()
services ==> [SUN: SecureRandom.DRBG -> sun.security.provider. ... ImplementedIn=Software}
]
jshell> services.stream().map(s -> s.getType() + ": " + s.getAlgorithm()).forEach(System.out::println)
SecureRandom: DRBG
SecureRandom: SHA1PRNG
Signature: SHA1withDSA
Signature: NONEwithDSA
Signature: SHA224withDSA
Signature: SHA256withDSA
Signature: SHA1withDSAinP1363Format
Signature: NONEwithDSAinP1363Format
Signature: SHA224withDSAinP1363Format
Signature: SHA256withDSAinP1363Format
KeyPairGenerator: DSA
MessageDigest: MD2
MessageDigest: MD5
MessageDigest: SHA
MessageDigest: SHA-224
MessageDigest: SHA-256
MessageDigest: SHA-384
MessageDigest: SHA-512
MessageDigest: SHA-512/224
MessageDigest: SHA-512/256
MessageDigest: SHA3-224
MessageDigest: SHA3-256
MessageDigest: SHA3-384
MessageDigest: SHA3-512
AlgorithmParameterGenerator: DSA
AlgorithmParameters: DSA
KeyFactory: DSA
CertificateFactory: X.509
KeyStore: PKCS12
KeyStore: JKS
KeyStore: CaseExactJKS
KeyStore: DKS
Policy: JavaPolicy
Configuration: JavaLoginConfig
CertPathBuilder: PKIX
CertPathValidator: PKIX
CertStore: Collection
CertStore: com.sun.security.IndexedCollection
Java.security.Provider.Service is a class that represents the individual cryptographic functions provided by the provider. There is a class called security / Provider.Service.html).
This Service
can be obtained from the various Getter methods provided by Provider
.
Service
provides methods to get information about the cryptographic techniques it supports.
For example, from getType (), " You can get the type of cryptography that the service supports, such as MessageDigest "
getAlgorithm () From the method, " You can get the name of a specific algorithm, such as SHA-256 "
.
If you want to refer to it as a document, use Java Security Standard Algorithm Name. I think you should see it.
If you want to check the supported algorithms for each specific provider, [JDK Provider Document](https://docs.oracle.com/javase/jp/11/security/oracle-providers.html#GUID- FE2D2E28-C991-4EF9-9DBE-2A4982726313) I think you should look at it.
The engine class provides the API related to the cryptographic technology provided by the class in an algorithm-independent manner.
In other words, both SHA-256 and MD5 can generate hash values with the same implementation by using MessageDigest
.
jshell
//Calculate hash value with MD5
jshell> var md5 = MessageDigest.getInstance("MD5")
md5 ==> MD5 Message Digest from SUN, <initialized>
jshell> md5.update("hoge".getBytes())
jshell> md5.digest()
$7 ==> byte[16] { -22, 112, 62, 122, -95, -17, -38, 0, 100, -22, -91, 7, -39, -24, -85, 126 }
// SHA-Calculate hash value at 256
jshell> var sha256 = MessageDigest.getInstance("SHA-256")
sha256 ==> SHA-256 Message Digest from SUN, <initialized>
jshell> sha256.update("hoge".getBytes())
jshell> sha256.digest()
$10 ==> byte[32] { -20, -74, 102, -41, 120, 114, 94, -55, 115, 7, 4, 77, 100, 43, -12, -47, 96, -86, -69, 118, -11, 108, 0, 105, -57, 30, -94, 91, 30, -110, 104, 37 }
↑ is an example of jshell generating hash values with MD5 and SHA-256.
The implementation of the part that calculates the hash value is the same for both algorithms, only the algorithm specified when creating the instance of MessageDigest
is different.
The classes provided by JCA are largely with javax.crypto packages. java.security It is provided separately in packages.
There is a historical reason for this. There was a time when the United States severely restricted the export of cryptography.
[Export of Cryptography from the United States-Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%A1%E3%83%AA%E3%82%AB%E5 % 90% 88% E8% A1% 86% E5% 9B% BD% E3% 81% 8B% E3% 82% 89% E3% 81% AE% E6% 9A% 97% E5% 8F% B7% E3% 81 % AE% E8% BC% B8% E5% 87% BA% E8% A6% 8F% E5% 88% B6)
The JCA package structure complies with this regulation.
The java.security
package contains classes for exportable technologies (MessageDigest
and Signature
), and the javax.crypto
package contains classes for non-exportable technologies (Cipher
and KeyAgreement
). It has been put in.
It seems that the providers were also divided according to these, [SUN Provider](https://docs.oracle.com/javase/jp/11/security/oracle-providers.html#GUID-3A80CC46-91E1-4E47- AC51-CB7B782CEA7D) provides the functionality provided by java.security
in SunJCE Provider -593C-4C38-A0D0-68FA7681E0A7) seems to implement each of the functions provided by javax.crypto
.
SunJCE seemed to have been offered as an extension when it was tightly regulated, but now it's relaxed and bundled with the JDK.
reference:Java encryption| 1.General security|Security Developer Guide
Use the java.security.MessageDigest class to use the hash function To do.
jshell
jshell> var md = MessageDigest.getInstance("SHA-256")
md ==> SHA-256 Message Digest from SUN, <initialized>
jshell> byte[] hash = md.digest("hello world".getBytes())
hash ==> byte[32] { -71, 77, 39, -71, -109, 77, 62, 8, -91 ... -84, -30, -17, -51, -23 }
digest () The value you want to hash to the method If you pass a byte
array, the hash value will be returned as a byte
array.
jshell
jshell> md.update("hello".getBytes())
jshell> md.update(" world".getBytes())
jshell> var hash = md.digest()
hash ==> byte[32] { -71, 77, 39, -71, -109, 77, 62, 8, -91 ... -84, -30, -17, -51, -23 }
update () Use the method to input Can be divided into multiple parts.
If you had to enter all the byte
s at once, you would have to put all the data into a byte
array.
There is no problem if the data size of the hash target is small, but if you want to hash a large file etc., all the data will be expanded on the memory, so it will be strict.
In such a case, you can pass the input data to ʻupdate ()` little by little so that you don't have to load all the data into memory at once.
Executing digest ()
initializes the state of MessageDigest
, so the instance can be reused.
The result of digest ()
is a byte
array, so it's hard to understand as it is.
A hexadecimal string is often used as a string representation of a hash value, so try converting it.
jshell
// ※toHashString()Is a predefined function (see top of page)
jshell> toHexString(hash)
$6 ==> "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
When calculating the hash value from a file, you can take the data from FileInputStream
and input it to MessageDigest
by yourself, but a class is provided to make it easier to implement.
Let's actually calculate the SHA-256 hash value of the OpenJDK 12 Windows version zip file.
jshell
jshell> var md = MessageDigest.getInstance("SHA-256")
md ==> SHA-256 Message Digest from SUN, <initialized>
//Generate InputStream of file
jshell> var in = new BufferedInputStream(new FileInputStream("openjdk-12_windows-x64_bin.zip"))
in ==> java.io.BufferedInputStream@10bbd20a
//Generate DigestInputStream
jshell> var dis = new DigestInputStream(in, md)
dis ==> [Digest Input Stream] SHA-256 Message Digest from SUN, <initialized>
//Read all information from InputStream (nullOutputStream because no read result is needed()Throw away)
jshell> dis.transferTo(OutputStream.nullOutputStream())
$31 ==> 196405895
//Calculate hash value
jshell> var hash = md.digest()
hash ==> byte[32] { 53, -88, -48, 24, -12, 32, -5, 5, -2, ... -13, -119, 124, 78, -110 }
Each time DigestInputStream
reads data from the stream, the read data is passed toʻupdate ()
of MessageDigest
.
jshell
jshell> var md = MessageDigest.getInstance("SHA-256")
md ==> SHA-256 Message Digest from SUN, <initialized>
//Generate InputStream of file
jshell> var in = new BufferedInputStream(new FileInputStream("openjdk-12_windows-x64_bin.zip"))
in ==> java.io.BufferedInputStream@5a1c0542
//Generate DigestOutputStream (nullOutputStream because no written information is needed()Throw away)
jshell> var dos = new DigestOutputStream(OutputStream.nullOutputStream(), md)
dos ==> [Digest Output Stream] SHA-256 Message Digest from SUN, <initialized>
//Read all information from InputStream and write to DigestOutputStream
jshell> in.transferTo(dos)
$24 ==> 196405895
//Calculate hash value
jshell> var hash = md.digest()
hash ==> byte[32] { 53, -88, -48, 24, -12, 32, -5, 5, -2, ... -13, -119, 124, 78, -110 }
On the other hand, DigestOutputStream
is passed to ʻupdate ()of
MessageDigest` every time data is written to the stream.
By the way, if you convert the calculated hash value to a hexadecimal string,
jshell
jshell> toHexString(hash)
$12 ==> "35a8d018f420fb05fe7c2aa9933122896ca50bd23dbd373e90d8e2f3897c4e92"
Hash value published on OpenJDK site is `35a8d018f420fb05fe7c2aa9933122896ca50bd23dbd373e90d8e2f3897c4e Can be calculated.
There are various "keys" in cryptography (private key, public key, private key, etc ...).
JCA also defines types that represent various keys accordingly (SecretKey
, PublicKey
, PrivateKey
, etc ...).
There are various types of keys, but they all have one common interface in their parents. That is the Key interface, which is the top-level type that represents the "key". It becomes.
In Key
, three methods for accessing the key information are defined.
Information such as algorithms can be obtained from Key
, but specific data that composes the key cannot be obtained.
The inability to access specific key data in this way is called ** opaque expression ** by JCA.
On the other hand, there are also types that can access the specific data that makes up the key. That is KeySpec (key specification).
Since the purpose of KeySpec
itself is to express that" this type is a key specification ", the API for actually accessing the key data is not defined.
The actual API is defined in the KeySpec
implementation class.
For example, in DSAPrivateKeySpec, the prime number used in key generation You can now access information about $ p $ and the private key $ x $.
Access to the specific data that makes up the key in this way is called ** transparent representation ** by JCA.
The actual Key
and KeySpec
and some class hierarchies look like this:
JCA provides two ways to create a key: Generator
and Factory
.
Generator
provides the ability to create a new key.
For example, you can generate a new key by specifying parameters such as key length.
On the other hand, Factory
mainly provides the function to convert between keys and key specifications.
It seems that some keys can generate the same key from two different key specifications.
Specifically, the following classes exist.
Generator
Factory
Both the common key cryptography and public key cryptography encryption / decryption processing are javax.crypto.Cipher. /crypto/Cipher.html) Use the class.
jshell
jshell> var cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher ==> Cipher.AES/CBC/PKCS5Padding, mode: not initialize ... orithm from: (no provider)
The character string passed to getInstance ()
of Cipher
is not a simple algorithm name, but the block cipher mode etc. can also be specified.
The specific format is ʻalgorithm / mode / padding`.
ʻAlgorithmis the name of an encryption algorithm such as AES, DES, or RSA.
modeis a block cipher mode that specifies ECB, CBC, etc.
padding` specifies how to pad the missing data when the data to be encrypted is not an integral multiple of the block size.
It is also possible to specify only ʻalgorithm like
Cipher.getInstance ("AES") `.
In this case, the default values determined by the provider are used for the mode and padding.
In most cases ** the default mode will be ECB **, so it's always a good idea to specify the mode and padding.
In order to start using Cipher
, you must first initialize it.
For initialization, specify "type of processing to be performed" and "parameters for that". For example, "encryption" and "key" will be passed for initialization.
jshell
//Generate key
jshell> var keyGen = KeyGenerator.getInstance("AES")
keyGen ==> javax.crypto.KeyGenerator@365185bd
jshell> var key = keyGen.generateKey()
key ==> javax.crypto.spec.SecretKeySpec@fffe87d2
//Cipher initialization
jshell> cipher.init(Cipher.ENCRYPT_MODE, key)
//Get initialization parameters
jshell> var params = cipher.getParameters()
params ==>
iv:
[0000: F1 D7 23 45 DA A6 7B 42 66 AF ... 46 ..#E...Bf.z..a.F
]
Use the ʻinit () `method for initialization.
Specify the initialization type in the first argument.
The initialization type is [Constants defined in Cipher](https://docs.oracle.com/javase/jp/11/docs/api/java.base/javax/crypto/Cipher.html#field. Use summary).
ʻENCRYPT_MODEis in encryption mode and
DECRYPT_MODE` is in decryption mode.
Initialization parameters are passed after the second argument. Basically, specify the key used for encryption / decryption.
Additional parameters may be required depending on the encryption algorithm. In that case, receive additional parameters such as ʻAlgorithmParameterSpec` [init ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/javax/crypto/Cipher.html Use the #init (int, java.security.Key, java.security.spec.AlgorithmParameterSpec)) method. AlgorithmParameterSpec is an interface that represents algorithm-specific parameters. There are implementation classes specific to various encryption algorithms.
Even for algorithms that require additional parameters, the provider may set default values for you. In that case, it can be initialized only by the processing mode and the ʻinit () `method that receives the key. The parameters used at this time are getParameters () It can be obtained by the method.
jshell
//Cipher initialization
jshell> cipher.init(Cipher.ENCRYPT_MODE, key)
//Get initialization parameters
jshell> var params = cipher.getParameters()
params ==>
iv:
[0000: F1 D7 23 45 DA A6 7B 42 66 AF ... 46 ..#E...Bf.z..a.F
]
Here, the key for AES is generated and Cipher
is initialized in encrypted mode.
Since the mode at the time of Cipher
generation was set to CBC, the parameter of the initialization vector (IV: ** I ** nitialization ** V ** ector) is originally required.
However, since the specification of the initialization vector was omitted in ʻinit () `, the provider generated the initialization vector nicely.
Since the initialization vector is also required for the initialization of the decoding mode, it is necessary to obtain it with getParameters ()
.
If the encryption algorithm used did not require any additional parameters, getParameters ()
returns null
.
Initialization resets all internal states of Cipher
.
In other words, the instance of Cipher
that was once used for encryption can now be reused for decryption.
Conversely, if you initialize to decryption mode during encryption, the information set during the encryption process will be lost, so be careful.
jshell
jshell> cipher.doFinal("Java Cryptography Architecture".getBytes())
$63 ==> byte[32] { -24, 54, 9, 79, -2, 118, 101, 69, -63, 30, -104, 77, -21, -24, -28, -4, 31, -56, -125, -121, 24, -115, 55, -92, -68, -35, -6, 90, -108, -122, -16, 21 }
doFinal () Cipher to method Ciphertext / plaintext (byte
array) can be obtained by passing the value (byte
array) that you want to convert / decrypt.
Similar to MessageDigest
, input is [update ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/javax/crypto/Cipher.html#update (byte) It can also be split using% 5B% 5D)).
However, unlike the case of MessageDigest
, the result of encryption / decryption is returned every time ʻupdate ()`.
jshell
jshell> cipher.update("Java ".getBytes())
$59 ==> byte[0] { }
jshell> cipher.update("Cryptography ".getBytes())
$60 ==> byte[16] { -24, 54, 9, 79, -2, 118, 101, 69, -63, 30, -104, 77, -21, -24, -28, -4 }
jshell> cipher.update("Architecture".getBytes())
$61 ==> byte[0] { }
jshell> cipher.doFinal()
$62 ==> byte[16] { 31, -56, -125, -121, 24, -115, 55, -92, -68, -35, -6, 90, -108, -122, -16, 21 }
In addition, if you are using a block cipher, an empty byte
array is returned until the input size is the size of the block.
For AES, the block size is 128 bits (16 bytes), so the return value of ʻupdate ()` is an empty array until the input reaches 16 bytes.
When doFinal ()
is called, the instance of Cipher
returns at the last initialization.
That is, it can be reused to encrypt / decrypt another input as is.
CipherInputStream/CipherOutputStream
Similar to DigestInputStream
/ DigestOutputStream
, there is a class that links Cipher
and ʻInputStream / ʻOutputStream
.
Each data and encryption / decryption flow is as shown in the figure below.
First, when using CipherInputStream
.
jshell
//Create an InputStream to be encrypted
jshell> var is = new ByteArrayInputStream("Java Cryptography Architecture".getBytes())
is ==> java.io.ByteArrayInputStream@5fcd892a
//Create CipherInputStream
jshell> var cis = new CipherInputStream(is, cipher)
cis ==> javax.crypto.CipherInputStream@b9afc07
//Create OutputStream of output destination
jshell> var out = new ByteArrayOutputStream()
out ==>
//Extracts all information from InputStream and writes the encryption result to OutputStream
jshell> cis.transferTo(out)
$67 ==> 32
//Check the result written to OutputStream
jshell> out.toByteArray()
$68 ==> byte[32] { -24, 54, 9, 79, -2, 118, 101, 69, -63, 30, -104, 77, -21, -24, -28, -4, 31, -56, -125, -121, 24, -115, 55, -92, -68, -35, -6, 90, -108, -122, -16, 21 }
Then when using CipherOutputStream
.
jshell
//Create an InputStream to be encrypted
jshell> var is = new ByteArrayInputStream("Java Cryptography Architecture".getBytes())
is ==> java.io.ByteArrayInputStream@133e16fd
//Create an OutputStream to output the ciphertext
jshell> var out = new ByteArrayOutputStream()
out ==>
//Create CipherOutputStream
jshell> var cos = new CipherOutputStream(out, cipher)
cos ==> javax.crypto.CipherOutputStream@51b279c9
//Pour the encryption target into CipherOutputStream
jshell> is.transferTo(cos)
$92 ==> 30
// doFinal()Close CipherOutputStream to execute()
jshell> cos.close()
//Check the ciphertext
jshell> out.toByteArray()
$94 ==> byte[32] { -24, 54, 9, 79, -2, 118, 101, 69, -63, 30, -104, 77, -21, -24, -28, -4, 31, -56, -125, -121, 24, -115, 55, -92, -68, -35, -6, 90, -108, -122, -16, 21 }
For both CipherInputStream
and CipherOutputStream
, doFinal ()
of Cipher
is executed at close ()
.
So, if you don't forget to execute close ()
, the result will be halfway [^ 1](Since it is a stream, you don't have to worry because it uses basic try-with-resources etc. I think).
[^ 1]: CipherInputStream
is not half-finished even withoutclose ()
, but the implementation seems to bedoFinal ()
when the input stream reaches the end.
In the Javadoc of CipherInputStream
, it is written as follows. ..
Programmers using this class will never use any methods that are not defined in this class or that are not overridden (such as new methods or constructors that were later added to one of the superclasses). Please **. The design and implementation of those methods may not take into account the security implications of CipherInputStream.
On the other hand, ʻInputStream` is Java 9 transferTo () has been added.
This transferTo ()
is not overridden by CipherInputStream
, so it meets the conditions that should not be used.
However, this transferTo ()
is implemented in OpenJDK 11 as follows.
InputStream.java
public long transferTo(OutputStream out) throws IOException {
Objects.requireNonNull(out, "out");
long transferred = 0;
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int read;
while ((read = this.read(buffer, 0, DEFAULT_BUFFER_SIZE)) >= 0) {
out.write(buffer, 0, read);
transferred += read;
}
return transferred;
}
Only read ()
of ʻInputStream and
write () of ʻOutputStream
are used here.
If you try to write a process without using transferTo ()
, you end up writing the same implementation that this transferTo ()
is doing.
And the method used at that time is read ()
overridden by CipherInputStream
orwrite ()
of CipherOutputStream
.
In that case, I personally think that there is no problem using transferTo ()
after all.
However, since this implementation is limited to OpenJDK, there is a possibility that other Java implementations may have security problems.
Use at your own risk.
AES Try implementing AES encryption and decryption.
jshell
//Create KeyGenerator with AES algorithm
jshell> var keyGen = KeyGenerator.getInstance("AES")
keyGen ==> javax.crypto.KeyGenerator@4d50efb8
//Key generation
jshell> var key = keyGen.generateKey()
key ==> javax.crypto.spec.SecretKeySpec@177c6
Use KeyGenerator to generate the key. For SunJCE providers, the default key length is 128 bits.
jshell
jshell> keyGen.init(256)
You can specify the key length with the ʻinit ()` method. Only 128, 192, or 256 key lengths can be specified (AES specification).
jshell
//Generate Cipher with AES
jshell> var cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher ==> Cipher.AES/CBC/PKCS5Padding, mode: not initialize ... orithm from: (no provider)
//Initialize in encryption mode
jshell> cipher.init(Cipher.ENCRYPT_MODE, key)
//Get initialization vector information
jshell> var params = cipher.getParameters()
params ==>
iv:
[0000: 88 8E 06 A0 89 80 0D 11 FF ED ... 45 ...........&.'.E
]
//encryption
jshell> var c = cipher.doFinal("Java Cryptography Architecture".getBytes())
c ==> byte[32] { -35, -80, -57, 91, -75, -46, -123, 49, ... 22, 90, -121, 124, -108 }
//Check if it is encrypted
jshell> new String(c)
$24 ==> "Nun[Ome?1ju Sehi "Re"\017}First time PW ”\026Z?|?"
Since the initialization vector information is required at the time of decoding, get the parameter information with getParameters ()
.
jshell
//Initialize in decryption mode
jshell> cipher.init(Cipher.DECRYPT_MODE, key, params)
//Decryption
jshell> var p = cipher.doFinal(c)
p ==> byte[30] { 74, 97, 118, 97, 32, 67, 114, 121, 112 ... , 99, 116, 117, 114, 101 }
//Check if it can be decrypted
jshell> new String(p)
$23 ==> "Java Cryptography Architecture"
When ʻinit () `, you must specify not only the key but also the parameters used to initialize the encryption.
jshell
//Output key to file
jshell> writeFile("secret-key", key.getEncoded())
//Output the parameters used during encryption to a file
jshell> writeFile("aes-params", params.getEncoded())
//Ciphertext generation
jshell> var c = cipher.doFinal("Hello World!!".getBytes())
c ==> byte[16] { -77, 124, 124, -4, -92, 80, -89, 102, ... 1, 114, 7, 117, -104, 11 }
//Output ciphertext to file
jshell> writeFile("aes-cryptogram", c)
The encoded data of the private key is Key
's [getEncoded ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/Key.html# It can be obtained with getEncoded ()).
For parameter information, see getEncoded () in ʻAlgorithmParameters`. )) can be obtained.
jshell
//Read the secret key information from the file and generate SecretKeySpec
jshell> Key key = new SecretKeySpec(readFile("secret-key"), "AES")
key ==> javax.crypto.spec.SecretKeySpec@178a8
//Get an instance of AlgorithmParameters and initialize it with the information read from the file
jshell> var params = AlgorithmParameters.getInstance("AES")
params ==>
jshell> params.init(readFile("aes-params"))
//Initialize with the information generated and read by Cipher
jshell> var cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher ==> javax.crypto.Cipher@df27fae
jshell> cipher.init(Cipher.DECRYPT_MODE, key, params)
//Read the ciphertext from the file and decrypt it
jshell> var m = cipher.doFinal(readFile("aes-cryptogram"))
m ==> byte[13] { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 33 }
//Check the result
jshell> new String(m)
$34 ==> "Hello World!!"
The private key can be restored using SecretKeySpec (algorithm Specify "AES"
).
Although this class is KeySpec
, it also implements Key
, so it can be used as a key as it is.
Parameters can be restored with AlgorithmParameters.
First, use getInstance () to Get an instance by specifying "AES"
.
Then, in init () Set the parameter data.
jshell
//Generate KeySpec for password-based encryption
jshell> char[] password = { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' }
password ==> char[8] { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' }
jshell> var keySpec = new PBEKeySpec(password)
keySpec ==> javax.crypto.spec.PBEKeySpec@5a8e6209
//Generate Key from KeySpec using SecretKeyFactory
jshell> var keyFac = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_128")
keyFac ==> javax.crypto.SecretKeyFactory@3234e239
jshell> var key = keyFac.generateSecret(keySpec)
key ==> com.sun.crypto.provider.PBEKey@855e49d8
PBE uses a key called PBEKey.
PBEKey
generates PBEKeySpec based on the password. Then use SecretKeyFactory to convert to PBEKey
and get it. ..
The password must be specified in an array of char
.
The reason for this is described in the Javadoc of PBEKeySpec
, but it seems that String
cannot be cleared later because the value is invariant.
In the instance acquisition of SecretKeyFactory
, PBEWithHmacSHA256AndAES_128
is specified in the algorithm.
The algorithm name is in the format PBEWith <digest | prf> And <encryption>
.
If digest | prf
is MD5 or SHA1, it will be PBES1 as in RFC8018, and ifHmac *
, it will be PBES2.
jshell
//Generate Cipher for password-based encryption and initialize in encryption mode
jshell> var cipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_128/CBC/PKCS5Padding")
cipher ==> javax.crypto.Cipher@5891e32e
Cipher
also gets an instance with the same algorithm.
(SunJCE provider Cipher
supports only CBC / PKCS5Padding
modes and padding in PBE, so I feel that it is not necessary to specify it explicitly, but just in case)
jshell
//Creating a pseudo-random number generator
jshell> var random = new SecureRandom()
random ==> Hash_DRBG,SHA-256,128,reseed_only
//Generation of salt
jshell> var salt = new byte[64];
salt ==> byte[64] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... , 0, 0, 0, 0, 0, 0, 0, 0 }
jshell> random.nextBytes(salt)
Generate salt.
Salt uses a cryptographically secure pseudo-random number generator (SecureRandom) Generate.
In RFC8018, the salt size is set to at least 64 bits, so I generated it in 64 bits for the time being.
jshell
//Declaration of number of iterations
jshell> int iterationCount = 1000
iterationCount ==> 1000
//Generate AlgorithmParameterSpec for password-based encryption
jshell> var keyParamSpec = new PBEParameterSpec(salt, iterationCount)
keyParamSpec ==> javax.crypto.spec.PBEParameterSpec@eec5a4a
jshell> cipher.init(Cipher.ENCRYPT_MODE, key, keyParamSpec)
Generate PBEParameterSpec using the number of iterations and the salt value. To do.
Then, initialize Cipher
with the key created earlier.
jshell
//Encryption execution
jshell> var c = cipher.doFinal("Hello World!!".getBytes())
c ==> byte[16] { 101, -52, -106, 26, 67, 118, -20, 22, ... 42, -71, 115, 123, -122 }
//Check the encryption result
jshell> new String(c)
$18 ==> "efu?\032Cv?\026\031\000*S{?"
After that, encryption is performed in the same way as other algorithms.
jshell
//Get the parameters used during encryption
jshell> var params = cipher.getParameters()
params ==> PBEWithHmacSHA256AndAES_128
jshell> PBEParameterSpec pbeParamSpec = params.getParameterSpec(PBEParameterSpec.class)
pbeParamSpec ==> javax.crypto.spec.PBEParameterSpec@35a50a4c
//Get IvParameterSpec
jshell> IvParameterSpec ivSpec = (IvParameterSpec)pbeParamSpec.getParameterSpec()
ivSpec ==> javax.crypto.spec.IvParameterSpec@281e3708
// IV (Initialization vector)Check the value of
jshell> var iv = ivSpec.getIV()
iv ==> byte[16] { -67, 47, 111, -2, 34, -17, -89, 74, -7 ... 46, -124, 99, 79, 23, 88 }
Since it was encrypted with CBC, it is necessary to obtain the initialization vector for decryption.
The initialization vector information (ʻIvParameterSpec) that was automatically set during encryption is [PBEParameterSpec](https://docs.oracle.com/javase/jp/11/docs/api/java.base/javax/crypto). It can be obtained by the
getParameterSpec ()` method of /spec/PBEParameterSpec.html).
jshell
//Generate PBEParameterSpec for decryption
jshell> var ivSpec = new IvParameterSpec(iv)
ivSpec ==> javax.crypto.spec.IvParameterSpec@dbd940d
jshell> PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, iterationCount, ivSpec)
pbeParamSpec ==> javax.crypto.spec.PBEParameterSpec@17695df3
//Initialize in decryption mode (* key generation is the same as encryption)
jshell> cipher.init(Cipher.DECRYPT_MODE, key, pbeParamSpec)
//Perform decryption
jshell> var m = cipher.doFinal(c)
m ==> byte[13] { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 33 }
//Check the decryption result
jshell> new String(m)
$21 ==> "Hello World!!"
At the time of decryption, Key
and ʻAlgorithmParameterSpec are generated again using the information of" password "," salt "and" number of iterations ", and
Cipheris initialized. (Since the IV information must be the one used at the time of encryption, the generation of
PBEParameterSpec` is slightly different.)
RSA Try using RSA for public key cryptography.
jshell
//Get the KeyPairGenerator to generate the key pair
jshell> var keyPairGen = KeyPairGenerator.getInstance("RSA")
keyPairGen ==> java.security.KeyPairGenerator$Delegate@42dafa95
//Generate key pair
jshell> var keyPair = keyPairGen.generateKeyPair()
keyPair ==> java.security.KeyPair@1dfe2924
Use KeyPairGenerator to generate a key pair for public key cryptography. To do.
Since RSA is used, the algorithm name is passed " RSA "
.
When you run generateKeyPair ()
, you will see KeyPair containing the public and private key sets. /KeyPair.html) can be obtained.
jshell
jshell> keyPairGen.initialize(4096)
jshell> keyPairGen.generateKeyPair().getPublic()
$9 ==> Sun RSA public key, 4096 bits
params: null
modulus: 58218231801097229661983999673649965349728454764714757172625245890343045...
public exponent: 65537
Key with the initialize () method of KeyPairGenerator
You can specify the length.
jshell
//Get Cipher for RSA
jshell> var cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding")
cipher ==> javax.crypto.Cipher@35a50a4c
//Initialize in encryption mode
jshell> cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic())
//Encryption execution
jshell> var c = cipher.doFinal("Hello World!!".getBytes())
c ==> byte[256] { 44, 48, -56, -42, -117, -48, 17, -76, ... , -15, 63, 46, -115, -42 }
//Check the encryption result
jshell> new String(c)
$14 ==> ",0 width\021\030?0\022:Li 斃 w?9 ァ,Man| ノ_? rut moon m to Щa]\002)Ma\024L5XD?Crane 訶({x?Shi\024,5, S@wM-?杣 woo g1\025 "02 Wren Herato J Bon Festival?\t ゥ Rem F F 悊^?\"Miu\023 ゥ&Mojo Mojo.Mri\037 ku i?9m ED Care gg\023q 亥 X4\021pM3 廱 ヤ\017G\02.4 billion\017 So?;Q\177 Winter U5wgL B Shallow>\006e?g?9l vinegar stick:\t] 垉 R\020 So?nmi?!?&s 㽴?Ora\016`H\f6 pickled ?Re?4\nfU 呶?.Sai"
You can get the public key with getPublic ()
of KeyPair
, so you can encrypt it.
The procedure is the same as for AES.
jshell
//Get the parameters generated during encryption
jshell> var params = cipher.getParameters()
params ==> MD: SHA-256
MGF: MGF1SHA-1
PSource: PSpecified
//Initialize in decryption mode
jshell> cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate(), params)
//Decryption execution
jshell> var m = cipher.doFinal(c)
m ==> byte[13] { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 33 }
//Check the decryption result
jshell> new String(m)
$18 ==> "Hello World!!"
Since the private key can be obtained by getPrivate ()
of KeyPair
, decryption is performed using it.
Again, the usage of Cipher
is the same as for AES.
jshell
//Generate a private key for MAC
jshell> var keyGen = KeyGenerator.getInstance("HmacSHA256")
keyGen ==> javax.crypto.KeyGenerator@17579e0f
jshell> var key = keyGen.generateKey()
key ==> javax.crypto.spec.SecretKeySpec@588390c
//Create an instance of MAC
jshell> var mac = Mac.getInstance("HmacSHA256")
mac ==> javax.crypto.Mac@7a765367
//Initialize MAC
jshell> mac.init(key)
//Calculate MAC value
jshell> var mv = mac.doFinal("Hello World!!".getBytes())
mv ==> byte[32] { 0, -103, -60, 37, -74, 97, 115, -50, 7 ... 80, 57, -84, -59, 90, 23 }
Use the Mac class to calculate the MAC value.
The MAC requires a private key, so use KeyGenerator
to generate the key.
After creating an instance of Mac
, first initialize it with the ʻinit () method using the private key. Next, pass the value (
byte array) for which you want to calculate the MAC value to
doFinal () . The calculated MAC value is returned as a
byte` array.
jshell
jshell> mac.update("Hello ".getBytes())
jshell> mac.update("World!!".getBytes())
jshell> mac.doFinal()
$9 ==> byte[32] { 0, -103, -60, 37, -74, 97, 115, -50, 77, 6, 107, 27, 15, -41, -15, 111, -99, -50, -85, -68, 11, -66, 69, -54, 44, -92, 80, 57, -84, -59, 90, 23 }
Similar to Cipher
etc., you can use ʻupdate ()` to divide and set the value to be calculated.
jshell
//Generate an RSA key pair
jshell> var keyPairGen = KeyPairGenerator.getInstance("RSA")
keyPairGen ==> java.security.KeyPairGenerator$Delegate@42dafa95
jshell> var keyPair = keyPairGen.generateKeyPair()
keyPair ==> java.security.KeyPair@1dfe2924
First, generate an RSA key pair as you would with an RSA cipher.
jshell
//Get Signature instance
jshell> var signature = Signature.getInstance("SHA256WithRSA")
signature ==> Signature object: SHA256WithRSA<not initialized>
//Initialize in signature mode
jshell> signature.initSign(keyPair.getPrivate())
//Register the data to be signed
jshell> signature.update("Hello World!!".getBytes())
//Generate digital signature
jshell> var sign = signature.sign()
sign ==> byte[256] { 29, -91, 95, 8, -19, -56, 118, -29, 1 ... 7, -1, 53, 58, -99, -117 }
To create a digital signature, use the Signature class.
Signature
has two initialization methods.
One is initSign () , The other is initVerify () become.
If you want to create a signature, use ʻinitSign () `to initialize it. At this time, the signature key (private key) used for signature is passed as an argument.
After the initialization is completed, the next step is to register the data to be signed with the ʻupdate ()method. Finally, when you execute the [sign ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/Signature.html#sign ()) method, it will be digital. The signature is returned as a
byte` array.
jshell
//Initialize Signature in verification mode
jshell> signature.initVerify(keyPair.getPublic())
//Register the data to be verified
jshell> signature.update("Hello World!!".getBytes())
//Perform verification
jshell> signature.verify(sign)
$12 ==> true
If you want to verify, initialize Signature
with ʻinitVerify ()`.
At this time, pass the verification key (public key) as an argument.
Also here, register the data to be verified with ʻupdate (). And finally [verify ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/Signature.html#verify (byte% 5B% 5D) ) Perform validation with the method. At this time, pass a
byte` array of digital signatures as an argument.
If the validation is successful, true
is returned.
jshell
//Generate key pair for DSA
jshell> var keyPairGen = KeyPairGenerator.getInstance("DSA")
keyPairGen ==> sun.security.provider.DSAKeyPairGenerator$Current@f6c48ac
jshell> var keyPair = keyPairGen.generateKeyPair()
keyPair ==> java.security.KeyPair@1f36e637
//Get Signature instance for DSA
jshell> var signature = Signature.getInstance("SHA256WithDSA")
signature ==> Signature object: SHA256WithDSA<not initialized>
//Initialize in signature mode
jshell> signature.initSign(keyPair.getPrivate())
//Register the data to be signed
jshell> signature.update("Hello World!!".getBytes())
//Generate digital signature
jshell> var sign = signature.sign()
sign ==> byte[63] { 48, 61, 2, 29, 0, -72, 46, -94, 42, 12 ... 4, 59, -38, -5, -48, 127 }
//Initialize in verification mode
jshell> signature.initVerify(keyPair.getPublic())
//Register the data to be verified
jshell> signature.update("Hello World!!".getBytes())
//Perform verification
jshell> signature.verify(sign)
$12 ==> true
Just by changing the algorithm name etc. for DSA, the implementation method can be the same as for RSA.
keytool keytool | Java Platform, Standard Edition Tool Reference
The JDK ships with a command line tool called ** keytool **. You can use keytool to generate and manage keys and certificates.
keytool help
$ keytool -h
Key and certificate management tools
command:
-certreq Generates a certificate request
-changealias Change aliases for entries
-delete Deletes the entry
-exportcert Export the certificate
-genkeypair Generate a key pair
-genseckey Generates a private key
-gencert Generate a certificate from a certificate request
-importcert Import a certificate or certificate chain
-importpass Import password
-importkeystore Imports one or all entries from another keystore
-change the key password for the keypasswd entry
-list Lists the entries in the keystore
-printcert Prints the contents of the certificate
-printcertreq Prints the contents of the certificate request
-printcrl Prints the contents of the CRL file
-storepasswd Change the keystore store password
To display this help message"keytool -?、-h or--help"Use the
command_For how to use name,"keytool -command_name --help"Use the.
To specify a preconfigured options file-conf <url>Use the option.
keytool <command> <option>
keytool can be used by passing the command to execute and its options.
All commands and options are in the form -name
.
You can see the list of commands with -h
.
In addition, help for each command can be found in detail by passing -h
to the command's options.
-certreq command help
$ keytool -certreq -h
keytool -certreq [OPTION]...
Generate a certificate request
option:
-alias <alias>Another name for the entry to process
-sigalg <alg>Signature algorithm name
-file <file>Output file name
-keypass <arg>Key password
-keystore <keystore>Keystore name
-dname <name>Identification name
-ext <value> X.509 extension
-storepass <arg>Keystore password
-storetype <type>Keystore type
-providername <name>Provider name
-addprovider <name>Add security provider by name(SunPK CS11 etc.)
[-providerarg <arg>] -Configure addprovider arguments
-providerclass <class>Add a security provider with a fully qualified class name
[-providerarg <arg>] -Configure the argument of providerclass
-providerpath <list>Provider classpath
-v Detailed output
-protected Password with protection mechanism
To display this help message"keytool -?、-h or--help"Use the
keytool stores information such as keys and certificates in a file called a keystore. Until Java 8, the keystore was created in Oracle's own file format called JKS. Java 9 and later are created in the file format defined by the PKCS # 12 standard (RFC7292).
Each keytool command now receives an option called -keystore
.
This option specifies the location of the keystore file.
If not specified, home directory / .keystore
is used as the keystore file path by default.
As you can see from the list of commands, there is no dedicated command for creating a new keystore. The keystore file is automatically created if there is no file when you first try to generate a key or the like.
The keystore itself is encrypted with password-based encryption, and you need to enter the password to access the contents. The keystore password is prompted when you first create the keystore. The password length must be at least 6 digits.
Keystore information is also accessible from Java programs. The class for that will be KeyStore (details will be described later).
Keys and certificates stored in the keystore are collectively called ** entries **.
The keystore is a kind of key-value store, and each entry is managed by giving it a name called ** alias **. In order to display / edit the information of a specific entry, the entry is specified by an alias.
There are two types of entries.
--Key entry --Trusted certificate entry
A "key entry" is an entry that stores a private key or a set of private keys and their paired public key certificates. The public key certificate may be a self-signed certificate or it may be chained to the root certificate.
On the other hand, the "trusted certificate entry" contains a ** single ** public key certificate. Only one public key certificate is stored in this entry (not chained), even if it is signed by another public key.
$ keytool -list
Keystore type: PKCS12
Keystore provider: SUN
Keystore contains 8 entries
hogekey,2019/04/10, SecretKeyEntry,
pbekey,2019/04/10, SecretKeyEntry,
rsakey,2019/04/13, PrivateKeyEntry,
Certificate finger print(SHA-256): CB:D1:0C:85:5F:1D:50:CC:62:C6:68:11:9F:7E:7D:4D:F7:1B:45:40:44:71:99:B3:41:2B:71:7A:E1:A6:02:FB
dsakey,2019/04/13, PrivateKeyEntry,
Certificate finger print(SHA-256): E9:70:1D:B2:8C:8B:18:C2:7C:A4:A0:DC:32:A5:37:06:ED:1B:DF:30:65:32:77:B5:43:77:4D:9D:42:15:70:B2
qiita-cert,2019/04/15, trustedCertEntry,
Certificate finger print(SHA-256): 57:28:2C:9D:D0:43:19:08:A4:CC:D3:52:CF:5F:32:16:EC:9D:DA:4A:4E:D1:5C:1F:3A:EB:39:3F:76:A3:91:D4
You can check the list of entries with the -list
command.
The entry that stores the pair of private key and public key is written as PrivateKeyEntry
(key entry).
The entry for the private key is written as SecretKeyEntry
(this is also the key entry).
The entry for a trusted certificate is written as trustedCertEntry
.
$ keytool -list -alias hogekey
hogekey,2019/04/10, SecretKeyEntry,
$ keytool -list -alias rsakey
rsakey,2019/04/13, PrivateKeyEntry,
Certificate finger print(SHA-256): CB:D1:0C:85:5F:1D:50:CC:62:C6:68:11:9F:7E:7D:4D:F7:1B:45:40:44:71:99:B3:41:2B:71:7A:E1:A6:02:FB
If you specify the alias of the entry you want to display individually with the -alias
option, you can output only the information of that alias.
Detailed information can be output by adding the -v
option.
$ keytool -list -alias hogekey -v
alias: hogekey
Created date: 2019/04/10
Entry type: SecretKeyEntry
$ keytool -list -alias rsakey -v
alias: rsakey
Created date: 2019/04/13
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
owner: CN=Alice, C=JP
Issuer: CN=Alice, C=JP
Serial number: 6a59c918
Validity start date: Sat Apr 13 08:41:13 JST 2019 end date: Fri Jul 12 08:41:13 JST 2019
Certificate finger print:
SHA1: 1F:11:0E:8C:30:0B:DA:D8:2C:79:AD:C7:4B:2B:70:62:85:94:94:CA
SHA256: CB:D1:0C:85:5F:1D:50:CC:62:C6:68:11:9F:7E:7D:4D:F7:1B:45:40:44:71:99:B3:41:2B:71:7A:E1:A6:02:FB
Signature algorithm name: SHA256withRSA
Subject public key algorithm:2048-bit RSA key
version: 3
Expansion:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 92 68 47 49 D0 26 A7 1D 04 51 64 3E 96 5B B3 05 .hGI.&...Qd>.[..
0010: 40 7B 3B E2 @.;.
]
]
$ keytool -storepasswd
New keystore password:
Please re-enter your new keystore password:
You can change the keystore password with the -storepasswd
command.
$ keytool -delete -alias foo
You can delete any entry with the -delete
command.
The entry to be deleted is specified by the -alias
option.
$ keytool -changealias -alias from-alias -destalias to-alias
You can change the alias of any entry with the -changealias
command.
-alias
specifies the alias to change.
-destalias
specifies the name of the alias after the change.
Java keeps a keystore that records the root certificate in the execution environment.
The specific file will be $ {JAVA_HOME} / lib / security / cacerts
.
This is also a keystore, so you can use keytool to see and edit it.
Check the contents of cacerts
$ keytool -list -cacerts
Please enter the keystore password:
Keystore type: JKS
Keystore provider: SUN
The keystore contains 93 entries
verisignclass2g2ca [jdk],2018/06/13, trustedCertEntry,
Certificate finger print(SHA-256): 3A:43:E2:20:FE:7F:3E:A9:65:3D:1E:21:74:2E:AC:2B:75:C2:0F:D8:98:03:05:BC:50:2C:AF:8C:2D:9B:41:A1
digicertassuredidg3 [jdk],2017/12/01, trustedCertEntry,
Certificate finger print(SHA-256): 7E:37:CB:8B:4C:47:09:0C:AB:36:55:1B:A6:F4:5D:B8:40:68:0F:BA:16:6A:95:2D:B1:00:71:7F:43:05:3F:C2
verisignuniversalrootca [jdk],2017/12/01, trustedCertEntry,
Certificate finger print(SHA-256): 23:99:56:11:27:A5:71:25:DE:8C:EF:EA:61:0D:DF:2F:A0:78:B5:C8:06:7F:4E:82:82:90:BF:B8:60:E8:4B:3C
...
Certificate finger print(SHA-256): 43:48:A0:E9:44:4C:78:CB:26:5E:05:8D:5E:89:44:B4:D8:4F:96:62:BD:26:DB:25:7F:89:34:A4:43:C7:01:61
addtrustqualifiedca [jdk],2017/12/01, trustedCertEntry,
Certificate finger print(SHA-256): 80:95:21:08:05:DB:4B:BC:35:5E:44:28:D8:FD:6E:C2:CD:E3:AB:5F:B9:7A:99:42:98:8E:B8:F4:DC:D0:60:16
The default password is changeit
.
By the way, the documentation says, "System administrators need to change the password and default permissions for this file after installing the SDK."
If you specify the option -cacerts
, it will be in the same state as if you specified the cacerts file as the keystore, so you can access it without specifying the file path with -keystore
.
If you implement SSL communication etc. in Java, certificate verification is performed using this cacerts by default. It may also be referred to when importing a certificate, which will be described later.
python
$ keytool -genseckey -keyalg AES -keysize 256 -alias HogeKey
Please enter the keystore password:**********
Please re-enter your new password:**********
To generate a secret key for symmetric key cryptography, use the -genseckey
command.
You will be prompted for a password, which will be the password for encrypting / decrypting the keystore file itself. Especially this time it was the first access (I created the keystore for the first time), so I am also prompted to re-enter the new password. From the next time onward, you will only have to enter the password once.
AES is specified as the key algorithm in -keyalg
, and the key length is 256.
-alias
specifies the alias to be attached to the registered private key.
It can be omitted, but in that case it will be the value mykey
.
I tried to create an entry by specifying the password-based key algorithm in -keyalg
.
$ keytool -genseckey -keyalg PBEWithHmacSHA256AndAES_128 -keysize 256 -alias pbeKey
Please enter the keystore password:*******
Please enter the password to save:**************
Retype password:**************
I was able to generate an entry without any error, so I feel like I was successful. But when I get this entry from a Java program, it looks like this:
jshell
(Omitted)
jshell> var entry = keystore.getEntry("pbeKey", password)
entry ==> Secret key entry with algorithm PBEWithMD5AndDES
The algorithm is PBEWithMD5AndDES
. .. ..
Does that mean there is no formal support?
$ keytool -genkeypair -keyalg RSA -keysize 2048 -dname CN=Alice,C=JP -alias rsakey
To generate a key pair for public key cryptography, use the -genkeypair
command.
As with symmetric key cryptography, you can specify the key algorithm with -keyalg
and the key length (number of bits) with -keysize
.
(To create a DSA key pair, specify DSA
in -keyalg
)
In -genkeypair
, the public key is generated in the form of a self-signed certificate (X.509).
-dname
specifies the X.500 distinguished name set for the issuer and subject of the certificate.
If you omit the -dname
specification, you will be prompted to enter the X.500 distinguished name from the command line.
Describe the X.500 distinguished name in the form of <attribute name> = <value>
separated by commas, such as CN = Amazon, OU = Server CA 1B, O = Amazon, C = US
.
The following values can be specified for the attribute name.
Attribute name | meaning |
---|---|
CN |
Common name Owner's name etc. |
OU |
Department name (organization unit) Names of departments and sections |
O |
Organization name Company name etc. |
L |
Locality name City name etc. |
S |
State name State name, etc. (prefecture level in Japan?) |
C |
Country code (country) A two-digit code that identifies the country ( JP , US Such) |
Some real examples.
Confirmed site | value of subject |
---|---|
Qiita https://qiita.com/ |
CN=qiita.com |
Oracle Japan https://www.oracle.com/jp/index.html |
CN=www-cs-01.oracle.com OU=Content Management Services IT O=Oracle Corporation L=Redwood Shores S=California C=US |
Amazon https://www.amazon.co.jp/ |
CN=www.amazon.co.jp O=Amazon.com, Inc. L=Seattle S=Washington C=US |
Cabinet Office https://www.cao.go.jp/ |
CN=*.cao.go.jp O=Cabinet Office L=Chiyoda-ku S=Tokyo C=JP |
Panasonic https://panasonic.jp/ |
CN=panasonic.jp O=Panasonic Corporation L=Kadoma S=Osaka C=JP |
It is not necessary to specify all attribute names.
However, the order is restricted and must be in the order CN
, ʻOU, ʻO
, L
, S
, C
.
When specified with the -dname
option, the option value must be enclosed in quotation marks ("
) if you want to include blank spaces.
-Example of enclosing dname in quotation marks
$ keytool ... -dname "CN=Alice, S=Osaka Fu, C=JP"
Also, half-width commas have a special meaning to separate attributes, so if you want to include half-width characters in the attribute value, you need to escape with a backslash.
Use half-width comma for attribute value
$ keytool ... -dname CN=Hoge\,Fuga
The validity period of the certificate can be controlled by the following two options.
-startdate
-validity
-startdate
is the start date and time of the certificate validity period.
-validity
specifies the validity period of the certificate in days.
In other words, the validity period of the certificate is between the date and time specified by -startdate
and the number of days specified by -validity
.
The default value of -startdate
is the execution date and time when the key was created, and the default value of -validity
is 90
.
Therefore, the default validity period of the certificate is 90 days from the date and time when the key pair was created.
-startdate
can be specified either relative or absolute.
In the relative method, the start date and time is specified by the time relative to the execution date and time. Specifically, it is described as follows.
Specifying a relative start date and time
$ keytool ... -startdate +10y-1m+5d+8H
For this designation, + 10y
means" plus 10 years ", -1m
means "minus 1 month", + 5d
means "plus 5 days", and + 8H
means" plus 8 hours ". There is.
The relative specification format is ([+-] nnn [ymdHMS]) +
.
In other words, specify how much the year, month, day, hour, minute, and second are shifted by plus / minus.
On the other hand, the absolute specification is specified as follows.
Specifying the absolute start date and time
$ keytool ... -startdate "2019/12/01 12:00:00"
This is the one I saw.
The format is [yyyy / mm / dd] [HH: MM: SS]
.
If omitted, the execution date and time will be used.
In other words, if you specify only the date, the hour, minute, and second will be the execution time. If only hours, minutes, and seconds are specified, the date will be the execution date.
$ keytool -exportcert -alias alice-key -file alice-cert
Certificate is a file<alice-cert>Saved in
You can use the -exportcert
command to output the certificate in the specified entry in X.509 format.
(The private key entry cannot be specified because there is no certificate)
The default is output to standard output.
Specify the -file
option to output to a file.
The default output format is binary format.
Specify the -rfc
option for PEM format.
Example of outputting in PEM format
$ keytool -exportcert -alias alice-key -rfc
-----BEGIN CERTIFICATE-----
MIIC2zCCAcOgAwIBAgIEFj7+/DANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDEwJj
...Abbreviation...
7ticcWecmUspMZ2dx/lO
-----END CERTIFICATE-----
$ keytool -printcert -file alice-cert
owner: CN=alice
Issuer: CN=ca
Serial number: 163efefc
Validity start date: Sat Apr 13 11:45:48 JST 2019 end date: Fri Jul 12 11:45:48 JST 2019
Certificate finger print:
SHA1: 07:E5:76:25:83:D1:C2:C5:66:C3:87:9E:E9:7A:B0:E8:1C:07:63:83
SHA256: 55:ED:F8:4B:CC:3F:C9:A9:3F:5D:D8:B3:CC:51:33:58:3E:36:4A:10:06:1F:E7:19:94:25:73:66:30:E7:85:EF
Signature algorithm name: SHA256withRSA
Subject public key algorithm:2048-bit RSA key
version: 3
Expansion:
...
You can check the contents of the specified certificate file by executing the -printcert
command with the -file
option.
If you do not specify -file
, it reads information from standard input by default.
The default is output in a human-readable format as described above.
Specify the -rfc
option to output in PEM format.
When output in PEM format
$ keytool -printcert -file alice-cert -rfc
-----BEGIN CERTIFICATE-----
MIIC2zCCAcOgAwIBAgIEFj7+/DANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDEwJj
...Abbreviation...
7ticcWecmUspMZ2dx/lO
-----END CERTIFICATE-----
The -printcert
command is not related to the keystore and can be executed without specifying -keystore
.
$ keytool -printcert -sslserver qiita.com
Certificate #0
====================================
owner: CN=qiita.com
Issuer: CN=Amazon, OU=Server CA 1B, O=Amazon, C=US
Serial number: 793de44ec0e815de65a3fbb3e35a1e2
Validity start date: Sun Mar 31 09:00:00 JST 2019 end date: Thu Apr 30 21:00:00 JST 2020
Certificate finger print:
SHA1: C0:D8:EE:56:3B:9F:68:22:0B:36:F3:9E:2A:D3:69:4E:3C:1D:61:44
SHA256: 57:28:2C:9D:D0:43:19:08:A4:CC:D3:52:CF:5F:32:16:EC:9D:DA:4A:4E:D1:5C:1F:3A:EB:39:3F:76:A3:91:D4
Signature algorithm name: SHA256withRSA
Subject public key algorithm:2048 bit RSA key
version: 3
...
Certificate #3
====================================
owner: CN=Starfield Services Root Certificate Authority - G2, O="Starfield Technologies, Inc.", L=Scottsdale, ST=Arizona, C=US
Issuer: OU=Starfield Class 2 Certification Authority, O="Starfield Technologies, Inc.", C=US
Serial number: a70e4a4c3482b77f
...
You can print the certificate for the specified SSL server by running the -printcert
command with the -sslserver
option.
The host and port can be specified in -sslserver
in the form host: port
.
The port number is optional and defaults to 443.
When running in a proxy environment, specify the proxy information with the -J-Dhttps.proxyHost
and -J-Dhttps.proxyPort
options (eg -J-Dhttps.proxyHost = proxy.host.name). -J-Dhttps.proxyPort = 8080
).
This also defaults to a human-readable format.
Specify the -rfc
option for PEM format.
If you want to save it locally as a certificate file, you can redirect the output to a file with the -rfc
option ( -file
is an input file option, so even if you specify this, it will be in the file. Is not saved).
$ keytool -certreq -alias alice-key
-----BEGIN NEW CERTIFICATE REQUEST-----
MIICkDCCAXgCAQAwGzELMAkGA1UEBhMCSlAxDDAKBgNVBAMTA1RvbTCCASIwDQYJ
...Abbreviation...
wX7FTUBXCD8CjXFxosmZ97YJf7Xy89cmdArgVNE9T9pJJpht
-----END NEW CERTIFICATE REQUEST-----
You can use the -certreq
command to create a certificate signing request.
In the -alias
option, specify the alias of the target public key entry.
The default is output to standard output.
If you want to output to a file, redirect or specify the output destination file with the -file
option.
Output the certificate signing request to a file
$ keytool -certreq -alias alice-key -file alice-certreq
#Check the contents of the keystore
$ keytool -list
...
Keystore contains 2 entries
ca-key,2019/04/13, PrivateKeyEntry,
...
alice-key,2019/04/13, PrivateKeyEntry,
...
#Generate a certificate from a certificate signing request file
$ keytool -gencert -infile alice-certreq -alias ca-key -outfile alice-cert
You can use the -gencert
command to generate a certificate (in X.509 format) from a certificate signing request file.
The above example generates a certificate for the certificate signing request file ʻalice-certrequsing the key pair entry
ca-key` that exists in the keystore.
-infile
specifies the certificate signing request file to be processed.
If not specified, input from standard input.
-alias
specifies the key entry used to sign the certificate.
-outfile
specifies the file to output the resulting certificate.
If not specified, it will be output to standard output.
By default, the certificate is output in binary format.
You can output in PEM format by specifying the -rfc
option.
-When the rfc option is specified
$ keytool -gencert -infile alice-certreq -alias ca-key -rfc
-----BEGIN CERTIFICATE-----
MIIC2zCCAcOgAwIBAgIEF+xeBDANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDEwJj
...Abbreviation...
es0hcACS0jLw0VoJYjSB
-----END CERTIFICATE-----
Use the -importcert
command to import the certificate.
The format of the data that can be imported can be a PKCS # 7 format certificate chain or a single or sequence of X.509 certificates.
This command has two functions.
The -importcert
command performs one of these functions, depending on the conditions.
It operates as a certificate response import if the following conditions are met:
--An entry with the name specified by -alias
already exists in the keystore
--The entry is a private key entry
On the other hand, if the following conditions are met, it operates as a trusted certificate import.
--The entry with the name specified by -alias
does not exist in the keystore
If the entry with the name specified by -alias
already exists in the keystore as a private key entry, the certificate you are trying to import is considered a certificate response.
In other words, it is treated as trying to import the certificate response signed and returned by the CA in response to the certificate signing request generated from the public key certificate in the key entry.
Therefore, keytool works to replace the public key certificate contained in the key entry with the certificate response specified for import.
The certificate response is validated before the actual import.
The verification is roughly carried out in the following two ways.
-alias
?Whether the certificate response corresponds to the key entry specified by -alias
is determined by whether the certificate response contains the same public key in the key entry.
If the public key certificate in the key entry does not exist in the certificate response, the certificate response is completely irrelevant and an error occurs.
The method of determining whether or not the certificate response is reliable is roughly divided into two depending on the number of certificates existing in the certificate response.
If the certificate response contains multiple certificates, it is tested to see if a valid certificate chain can be built with those certificates.
First, find the certificate with the same public key as the import destination key entry in the certificate response. Then, with that certificate at the top, the certificates whose signature verification is OK are connected one after another.
If the trailing certificate is a self-signed certificate, it will be checked to see if the same certificate exists in the keystore. If the same self-signed certificate exists in the keystore, the certificate chain is considered legitimate and the constructed certificate chain is imported into the key entry. If the same self-signed certificate does not exist, keytool will ask you if you want to continue the import.
On the other hand, if the certificate at the end is not a self-signed certificate, it is checked whether there is a certificate in the keystore that can verify the signature. If the appropriate certificate is found in the keystore, the certificate found in the keystore is added to the end of the certificate chain, and the certificate chain is imported into the key entry. If the certificate is not found, keytool will ask you if you want to continue the import.
If there is only one certificate in the certificate response, the verification behavior changes slightly.
In this case, keytool attempts to reproduce the certificate chain using the certificate that exists in the import destination keystore.
If the certificate chain can be finally reproduced up to the root, the chain reproduced as a reliable thing is registered in the key entry. If the chain cannot be reproduced (if you cannot find a public key that can be verified in the middle), keytool will ask you if you want to continue the import.
If the entry specified by -alias
does not exist in the keystore, it will be imported as a trusted certificate.
In this case, even if there are multiple certificates in the data to be imported, only the first one is imported.
At this time as well, verification is performed to see if the certificate is valid. This validation behaves the same as if the certificate response was a single certificate.
In other words, whether or not the certificate is valid is determined by whether or not the certificate chain can be reproduced using the certificate in the keystore.
If the chain can be reproduced, the import will be completed as it is. If it cannot be reproduced, keytool will ask you if you want to continue the import.
If you specify -trustcacerts
in the option of the -importcert
command, the root certificate in cacerts will also be used during validation.
(By default, only the certificate in the target keystore is used)
#Check Alice's keystore
$ keytool -keystore alice.keystore -list
...
Keystore contains 1 entry
alice-key,2019/04/18, PrivateKeyEntry,
Certificate finger print(SHA-256): C7:55:BB:57:C4:3F:57:02:BE:2E:57:1E:7C:6F:F4:A9:55:E5:90:92:6B:7D:DF:5F:14:FF:F2:65:16:81:E1:56
#Check the CA keystore
$ keytool -keystore ca.keystore -list
...
Keystore contains 2 entries
middle-ca-key,2019/04/18, PrivateKeyEntry,
Certificate finger print(SHA-256): CE:79:D5:32:D0:1E:5E:3A:55:BB:3E:CD:CE:5E:5F:9F:4D:79:97:2E:E7:EF:F9:60:62:C5:D0:4E:AD:28:B4:60
root-ca-key,2019/04/18, PrivateKeyEntry,
Certificate finger print(SHA-256): C0:06:6C:5C:2C:F5:3D:8B:DB:D4:EC:19:C5:30:A0:90:71:00:CC:17:40:E8:EB:38:64:5B:8F:40:3A:D4:3F:7C
Two keystores were prepared for verification.
The first is Alice's keystore, which currently has only one entry for a public key pair of self-signed certificates (ʻalice-key`).
The other is the CA (Certificate Authority) keystore, which has a public key pair for the intermediate certificate authority (middle-ca-key
) and a public key pair for the root certificate authority (root-ca-key
). There are two entries.
(By the way, middle-ca-key
is signed with root-ca-key
)
From here, generate a certificate signing request from Alice's public key pair, sign it with a certificate authority key, and import it into Alice's keystore.
#Generate a certificate signing request from Alice's key
$ keytool -keystore alice.keystore -certreq -alias alice-key -file alice.csr
#Signed with an intermediate certificate authority key
$ keytool -keystore ca.keystore -gencert -alias middle-ca-key -infile alice.csr -outfile alice.cer
#Check the contents of the generated certificate
$ keytool -printcert -file alice.cer
Certificate[1]:
owner: CN=alice
Issuer: CN=middle-ca
Serial number: 25fc2e01
...
Certificate[2]:
owner: CN=middle-ca
Issuer: CN=root-ca
Serial number: 248f2111
...
Generated a certificate signing request from Alice's key pair and generated a public key certificate signed with an intermediate certificate authority key.
Looking at the contents, you can see that the certificates are chained in the order of Alice → Intermediate Certificate Authority (the certificate of the root certificate authority is not included).
Import this into Alice's original key entry (that is, import it as a certificate response).
$ keytool -keystore alice.keystore -importcert -file alice.cer -alias alice-key
Top-level certificate that responded:
owner: CN=middle-ca
Issuer: CN=root-ca
Serial number: 248f2111
...
...Is not trusted. Do you want to install the response?[No]:
The top-level certificate was determined to be untrusted and asked to confirm that it could be imported.
The root-ca
certificate does not exist in Alice's keystore, so the certificate response you are trying to import is considered untrustworthy.
Here, the import is temporarily suspended, and the certificate of the root certificate authority is first imported into Alice's keystore.
#Export the root certificate authority's public key certificate
$ keytool -keystore ca.keystore -exportcert -alias root-ca-key -file root-ca.cer
Certificate is a file<root-ca.cer>Saved in
#Import the root certificate authority certificate into Alice's keystore
$ keytool -keystore alice.keystore -importcert -file root-ca.cer -alias root-ca-cert
owner: CN=root-ca
Issuer: CN=root-ca
Serial number: 13b64cb6
...
Certificate finger print:
SHA1: E9:D2:EF:D2:1D:86:8F:04:50:0C:ED:DD:5B:C1:C5:DD:FE:64:77:3D
SHA256: C0:06:6C:5C:2C:F5:3D:8B:DB:D4:EC:19:C5:30:A0:90:71:00:CC:17:40:E8:EB:38:64:5B:8F:40:3A:D4:3F:7C
...
Do you trust this certificate?[No]: y
Certificate added to keystore
The entry specified for -alias
does not exist, so the -importcert
command behaves as "import a trusted certificate".
Since this certificate is a self-signed certificate, the importer (keystore administrator) has no choice but to trust it.
I trust it here, so I completed the import with y
.
If you are actually importing a trusted certificate, you must carefully look at the information such as the fingerprints displayed on the screen to make sure you are importing the intended certificate.
Try importing Alice's certificate response again.
#Import Alice's certificate response
$ keytool -keystore alice.keystore -importcert -file alice.cer -alias alice-key
Certificate response installed in keystore
#Check Alice's key entry after import
$ keytool -keystore alice.keystore -storepass password -list -v -alias alice-key
alias: alice-key
Created date: 2019/04/18
Entry type: PrivateKeyEntry
Certificate chain length: 3
Certificate[1]:
owner: CN=alice
Issuer: CN=middle-ca
Serial number: 25fc2e01
...
Certificate[2]:
owner: CN=middle-ca
Issuer: CN=root-ca
Serial number: 248f2111
...
Certificate[3]:
owner: CN=root-ca
Issuer: CN=root-ca
Serial number: 13b64cb6
...
This time, the certificate of the root certificate authority was already installed, so the verification of the certificate chain succeeded smoothly, and the installation was completed as it was.
You can also see that the certificate chain is stored in the public key certificate in Alice's key entry after import.
KeyStore The keystore file generated and managed by the keytool command has an API so that it can be accessed from Java programs.
jshell
jshell> var keystore = KeyStore.getInstance("PKCS12")
keystore ==> java.security.KeyStore@3fd7a715
To access the keystore, use the KeyStore class.
getInstance ()
specifies the keystore type.
In Java 9 and above, the keystore type is PKCS12 by default, so you are instantiating with " PKCS12 "
.
(If it is an old keystore, it may be JKS, so specify " JKS "
in that case)
jshell
jshell> char[] password = { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' }
password ==> char[8] { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' }
jshell> keystore.load(new FileInputStream("alice.keystore"), password)
To load the keystore file, [load ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/KeyStore.html#load (java.) io.InputStream, char% 5B% 5D))) Use the method.
Specify ʻInputStreamof the keystore file in the first argument, and specify the password of the keystore file in the
char` array in the second argument.
jshell
jshell> var keystore = KeyStore.getInstance(new File("alice.keystore"), password)
keystore ==> java.security.KeyStore@525b461a
[getInstance (File, char [])](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/KeyStore.html#getInstance(java.io.File) , char% 5B% 5D)), you can do everything from creating a KeyStore
instance to loading a keystore file in one go.
The keystore type is automatically determined.
jshell
jshell> keystore.aliases().asIterator().forEachRemaining(System.out::println)
alice-key
aes-key
root-ca-cert
aliases () In the method, alias names of all entries Can be obtained.
jshell
jshell> keystore.isKeyEntry("alice-key")
$12 ==> true
jshell> keystore.isCertificateEntry("alice-key")
$13 ==> false
jshell> keystore.isKeyEntry("root-ca-cert")
$14 ==> false
jshell> keystore.isCertificateEntry("root-ca-cert")
$15 ==> true
Whether the entry pointed to by the alias is a key entry is isKeyEntry ().
Whether it is a trusted certificate entry is [isCertificateEntry ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/KeyStore.html#isCertificateEntry (java) You can check it with .lang.String)).
jshell
jshell> var seckey = keystore.getKey("seckey", password)
seckey ==> javax.crypto.spec.SecretKeySpec@fffe8f5b
jshell> seckey.getAlgorithm()
$29 ==> "AES"
[getKey ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/KeyStore.html#getKey(java.lang.String, char% 5B%) In 5D)), you can get the key of the specified alias as the private key.
The first argument is the alias name and the second argument is the password that protects the key (usually the same as the keystore password).
jshell
jshell> var alicePrivateKey = keystore.getKey("alice-key", password)
alicePrivateKey ==> SunRsaSign RSA private CRT key, 2048 bits
param ... 96220784207006016968977089
jshell> alicePrivateKey.getClass()
$38 ==> class sun.security.rsa.RSAPrivateCrtKeyImpl
You can get the private key by using getKey ()
on the key entry of the public key pair.
jshell
//Get a certificate
jshell> Certificate aliceCert = keystore.getCertificate("alice-key")
aliceCert ==> [
[
Version: V3
Subject: CN=alice
Signature ... 3 D5 .........%$.m...
]
//Get the public key from the certificate
jshell> PublicKey alicePublicKey = aliceCert.getPublicKey()
alicePublicKey ==> Sun RSA public key, 2048 bits
params: null
mo ... 9
public exponent: 65537
For the key entry of the public key pair [getCertificate ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/KeyStore.html#getCertificate (java) With .lang.String)), you can use the public key certificate (java.security.cert.Certificate /security/cert/Certificate.html))) can be obtained.
jshell
jshell> Certificate[] chain = keystore.getCertificateChain("alice-key")
chain ==> Certificate[3] { [
[
Version: V3
Subject: CN= ... C0 .;;.O...&.....N.
] }
If you use getCertificateChain () , You can get the certificate chain that exists in the specified entry as an array of Certificate
.
jshell
//Generate private key
jshell> var secKey = KeyGenerator.getInstance("AES").generateKey()
secKey ==> javax.crypto.spec.SecretKeySpec@fffe806b
//Generate private key entry
jshell> var secKeyEntry = new KeyStore.SecretKeyEntry(secKey)
secKeyEntry ==> Secret key entry with algorithm AES
//Register private key entry in keystore
jshell> var protection = new KeyStore.PasswordProtection(password)
protection ==> java.security.KeyStore$PasswordProtection@27d415d9
jshell> keystore.setEntry("sec-key", secKeyEntry, protection)
//Extract and confirm the registered private key entry
jshell> keystore.getKey("sec-key", password)
$10 ==> javax.crypto.spec.SecretKeySpec@fffe806b
To add an entry to the keystore, setEntry ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/KeyStore.html#setEntry ( Use java.lang.String, java.security.KeyStore.Entry, java.security.KeyStore.ProtectionParameter)).
The first argument is the entry alias. The second argument is the entry to register. The third argument passes parameters related to entry protection.
Create a private key entry in KeyStore.SecretKeyEntry.
The parameter will pass the password to use when encrypting the entry. It is possible to specify a password different from the keystore, but is it safe to keep it the same? (Keytool uses the same password as the keystore if the keystore type is PKCS12)
jshell
jshell> keystore.store(new FileOutputStream("alice.keystore"), password)
[store ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/KeyStore.html#store (java.io.OutputStream, char% 5B%) With 5D)), you can output the contents of KeyStore
to the specified output stream.
[Java.security.cert.Certificate] as a class representing a certificate (https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/cert/Certificate.html) There is a class called java.security.cert.CertificateFactory as a class for building this instance. There is a class called /java/security/cert/CertificateFactory.html).
However, CertificateFactory
reads an existing certificate and builds Certificate
, not a certificate from scratch.
Roughly java.security.cert Even if you look at the package , I can't find a class that generates a certificate.
I looked at the implementation of the -gencert
command of Keytool, but the class of the sun.security.x509
package (internal API) was used.
There is no way to generate a certificate using only the standard API. It seems that there is no choice but to generate it using keytool.
Use CertificateFactory to read the encoded certificate To do.
The type of certificate that can be specified by getInstance ()
is only X.509
as a standard function, and Javadoc etc. are basically explained mainly by the explanation of X.509.
CertificateFactory type | Java security standard algorithm name
Therefore, the explanation here is also based on X.509.
jshell
//Generate CertificateFactory
jshell> var factory = CertificateFactory.getInstance("X.509")
factory ==> java.security.cert.CertificateFactory@3abbfa04
//Certificate file to read (alice.Generate an input stream for cer)
jshell> var in = new FileInputStream("alice.cer")
in ==> java.io.FileInputStream@31ef45e3
//Read certificate information from input stream
jshell> factory.generateCertificate(in)
$5 ==> [
[
Version: V3
Subject: CN=alice
Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11
...
]
jshell> factory.generateCertificate(in)
$6 ==> [
[
Version: V3
Subject: CN=middle-ca
Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11
...
]
jshell> factory.generateCertificate(in)
|Exception java.security.cert.CertificateException: Could not parse certificate: java.io.IOException: Empty input
| at X509Factory.engineGenerateCertificate (X509Factory.java:110)
| at CertificateFactory.generateCertificate (CertificateFactory.java:355)
| at (#7:1)
|Cause: java.io.IOException: Empty input
| at X509Factory.engineGenerateCertificate (X509Factory.java:106)
| ...
To read an encoded X.509 certificate, generateCertificate ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/cert/CertificateFactory Use .html # generateCertificate (java.io.InputStream)).
The target data must be DER-encoded binary format data or PEM format data.
If the target data contains data from multiple certificates, you can read one certificate from the beginning each time you call generateCertificate ()
.
(If the certificate no longer exists and you try to read it further, an exception will be thrown)
jshell
jshell> Collection<? extends Certificate> certs = factory.generateCertificates(new FileInputStream("alice.cer"))
certs ==> [[
[
Version: V3
Subject: CN=alice
Signatur ... 70 ....$..&...2.n.p
]]
jshell> certs.size()
$12 ==> 2
If generateCertificates () , All certificates in the target data can be read at once.
jshell
//Extract certificates one by one from the collection of certificates read in ↑
jshell> var iterator = certs.iterator()
iterator ==> java.util.ArrayList$Itr@27808f31
//Get alice's certificate
jshell> var aliceCert = iterator.next()
aliceCert ==> [
[
Version: V3
Subject: CN=alice
Signature ... 3 D5 .........%$.m...
]
//Obtain the certificate of the intermediate certificate authority
jshell> var middleCaCert = iterator.next()
middleCaCert ==> [
[
Version: V3
Subject: CN=middle-ca
Signa ... 0 70 ....$..&...2.n.p
]
//Verify alice's certificate with the public key of the intermediate certificate authority (verification OK)
jshell> aliceCert.verify(middleCaCert.getPublicKey())
//Verify alice's certificate with alice's own public key (verification NG)
jshell> aliceCert.verify(aliceCert.getPublicKey())
|Exception java.security.SignatureException: Signature does not match.
| at X509CertImpl.verify (X509CertImpl.java:459)
| at X509CertImpl.verify (X509CertImpl.java:391)
| at (#18:1)
To verify the signature of the certificate verify () is used.
Pass the public key used for verification as an argument.
If the validation is OK then nothing happens and if it is NG an exception is thrown.
jshell
//Create a SecureRandom instance
jshell> var random = new SecureRandom()
random ==> Hash_DRBG,SHA-256,128,reseed_only
//Get random numbers less than 100
jshell> random.nextInt(100)
$3 ==> 91
//Generate a 512-bit random byte array
jshell> byte[] bytes = new byte[64]
bytes ==> byte[64] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... , 0, 0, 0, 0, 0, 0, 0, 0 }
jshell> random.nextBytes(bytes)
jshell> bytes
bytes ==> byte[64] { 97, 86, -17, 9, 81, 39, -18, -43, 40, -43, -21, 93, 112, -78, -1, 59, -5, 110, 115, 13, -86, 34, -81, -90, -107, -82, -102, -72, 19, 87, 9, -38, -91, -7, 47, -119, 103, -24, 52, -40, -3, 36, -9, -62, 10, -32, 36, 27, -74, -75, 58, -54, -124, -49, -6, 77, 78, 32, 96, 66, 67, -86, -39, -98 }
Use SecureRandom to generate random numbers for encryption.
Java also has a Random class for generating random numbers. To do. However, this is not unpredictable and should not be used in cryptography.
SecureRandom
is different from other engine classes in [getInstace ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/SecureRandom.html#" In addition to getInstance (java.lang.String)), [Constructor](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/SecureRandom.html#%3Cinit You can create an instance with% 3E ()).
When an instance is created by the constructor, the algorithm is selected from the provider with the highest priority.
Which algorithm is selected for each environment is [SecureRandom implementation | 4 JDK provider documentation](https://docs.oracle.com/javase/jp/11/security/oracle-providers.html#GUID-9DC4ADD5 It can be confirmed by -6D01-4B2E-9E85-B88E3BEE7453).
An instance of SecureRandom
can be obtained withgetInstance ()
like other engine classes, or with a constructor.
In the former, it is necessary to specify the algorithm, but in the latter, the algorithm is determined by a predetermined priority.
Which should be used to instantiate SecureRandom
?
Looking at the official Javadoc and JCA documentation, I couldn't find anything that clearly says "use this".
The personal conclusion I made after a lot of research was "use the constructor unless you have a specific reason" (it may be wrong because it is a personal conclusion).
The reason is that using a constructor allows you to maintain portability while selecting the optimal algorithm for each environment.
In Unix-like environments, the default algorithm is NativePRNG
.
However, this algorithm cannot be used in a Windows environment.
In other words, if the algorithm is specified as NativePRNG
, the program cannot be ported to Windows.
(Conversely, Windows-PRNG
cannot be used in Unix-like environments)
Also, in the Windows environment, the default algorithm up to Java 8 was SHA1PRNG
.
However, with the addition of DRBG
in Java 9, the top priority has been replaced by DRBG
(JEP 273: DRBG-Based SecureRandom Implementations). ).
DRBG
is a pseudo-random number generation method described in NIST Special Publication 800-90A Revision 1. It seems to be stronger than SHA1PRNG
.
I think it's better to use a constructor because it's easy to handle cases where the algorithm is replaced with a new one. (It's just a personal conclusion)
getInstanceStrong() Java 8 added a method called getInstanceStrong () ing.
An algorithm that generates strong random numbers is selected, as the name says Strong
.
It seems to be used when generating important values such as key pair generation for public key cryptography.
For example, when used in a Unix-like environment, the specific algorithm is NativePRNGBlocking
.
What is different from the default NativePRNG
is [nextBytes ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/ security / SecureRandom.html # nextBytes (byte% 5B% 5D)) and [generateSeed ()](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/security/ SecureRandom.html # generateSeed (int)) is different in whether it uses / dev / random
or / dev / urandom
respectively.
algorithm | nextBytes() |
generateSeed() |
---|---|---|
NativePRNG |
/dev/urandom |
/dev/random |
NativePRNGBlocking |
/dev/random |
/dev/random |
NativePRNGNonBlocking |
/dev/urandom |
/dev/urandom |
SecureRandom | Table 4-4 Algorithms in SUN Provider
These / dev / random
and / dev / urandom
are pseudo-devices for generating random numbers and can be used on Unix-like operating systems.
/ dev / random
collects environmental noise and generates random numbers, so you can get true random numbers.
However, if you try to obtain a random number when the information is not sufficiently collected, the process will be blocked (waiting).
On the other hand, / dev / urandom
can generate random numbers without blocking processing by reusing the collected information.
However, since the information is reused, the security as a random number is inferior to / dev / random
.
reference
/ dev / random
is highly secure, but at the cost of degrading program performance.
/ dev / urandom
is less secure, but at the cost of not compromising program performance.
The default choice, NativePRNG
, uses / dev / random
to generate true random numbers, while normal random number generation uses / dev / urandom
.
In other words, NativePRNG
is able to ensure performance while slightly reducing safety.
On the other hand, NativePRNGBlocking
always gets a random number from / dev / random
.
In other words, the performance is abandoned and the safety is fully devoted.
I think you should use getInstanceStrong ()
when you need to prioritize safety over program performance, and use the default implementation in the constructor when you have to do both (individual). opinion).
KeyAgreement is a class for realizing Diffie-Hellman key exchange. It is prepared.
The flow of key exchange using KeyAgreement
is as follows.
Below, launch jshell for Alice and jshell for Bob, and try exchanging keys (both current directories are in the same location).
Alice
//Generate a key pair
jshell> var aliceKeyPairGen = KeyPairGenerator.getInstance("DH")
aliceKeyPairGen ==> java.security.KeyPairGenerator$Delegate@6d7b4f4c
//Generate key pair
jshell> var aliceKeyPair = aliceKeyPairGen.generateKeyPair()
aliceKeyPair ==> java.security.KeyPair@1786dec2
//Generate Alice's KeyAgreement
jshell> var aliceKeyAgree = KeyAgreement.getInstance("DH")
aliceKeyAgree ==> javax.crypto.KeyAgreement@2c039ac6
//Initialized with Alice's private key
jshell> aliceKeyAgree.init(aliceKeyPair.getPrivate())
//Output Alice's public key to a file
jshell> writeFile("alice-dh-public-key", aliceKeyPair.getPublic().getEncoded())
//Public key format is X.509
jshell> aliceKeyPair.getPublic().getFormat()
$32 ==> "X.509"
To exchange keys, first generate a key pair using KeyPairGenerator
.
Specify DiffieHellman
or the abbreviation DH
for the algorithm name.
The public key for this key pair contains the prime number $ p $, the primitive element $ g $, and the value $ y = g ^ {a} \ bmod p $ calculated from the appropriately selected random number $ a $. There is.
For KeyAgreement
, get an instance with DiffieHellman
(or DH
) and initialize it with the private key.
Bob
//Read Alice's public key
jshell> var encodedAlicePublicKey = readFile("alice-dh-public-key")
encodedAlicePublicKey ==> byte[556] { 48, -126, 2, 40, 48, -126, 1, 27, 6, ... 70, 34, 8, -3, -119, 14 }
//Restore Alice's encoded public key
jshell> var bobKeyFactory = KeyFactory.getInstance("DH")
bobKeyFactory ==> java.security.KeyFactory@7a9273a8
jshell> var alicePublicKeySpec = new X509EncodedKeySpec(encodedAlicePublicKey)
alicePublicKeySpec ==> java.security.spec.X509EncodedKeySpec@e874448
jshell> var alicePublicKey = bobKeyFactory.generatePublic(alicePublicKeySpec)
alicePublicKey ==> SunJCE Diffie-Hellman Public Key:
y:
17cc14 ...
g:
02
l:
1024
The public key received from Alice is in X.509 format, so X509EncodedKeySpec Can be used to restore.
Since it is the role of KeyFactory
to generate Key
from KeySpec
, restore using KeyFactory
.
For the algorithm name, specify DiffieHellman
or DH
.
Bob
//Generate Bob's key pair
jshell> var bobKeyPairGen = KeyPairGenerator.getInstance("DH")
bobKeyPairGen ==> java.security.KeyPairGenerator$Delegate@42d8062c
jshell> var dhParams = ((DHPublicKey)alicePublicKey).getParams()
dhParams ==> javax.crypto.spec.DHParameterSpec@cb51256
jshell> bobKeyPairGen.initialize(dhParams)
jshell> var bobKeyPair = bobKeyPairGen.generateKeyPair()
bobKeyPair ==> java.security.KeyPair@5bcea91b
The restored Alice's public key is of type DHPublicKey It has become. Using this getPrams () method, Get the specific parameters of the key exchange generated on the Alice side.
Once the key exchange parameters have been obtained, a key pair on the Bob side is generated from the values.
Bob
//Generate Bob's KeyAgreement and initialize it with Bob's private key
jshell> var bobKeyAgree = KeyAgreement.getInstance("DH")
bobKeyAgree ==> javax.crypto.KeyAgreement@27f723
jshell> bobKeyAgree.init(bobKeyPair.getPrivate())
//Output Bob's public key to a file
jshell> writeFile("bob-dh-public-key", bobKeyPair.getPublic().getEncoded())
Initialize KeyAgreement
on Bob's side with Bob's private key.
Then send Bob's public key to Alice.
Alice
//Read Bob's public key
jshell> var encodedBobPublicKey = readFile("bob-dh-public-key")
encodedBobPublicKey ==> byte[557] { 48, -126, 2, 41, 48, -126, 1, 27, 6, ... 5, -20, 52, -85, -95, 51 }
//Restore Bob's public key on Alice's side
jshell> var aliceKeyFactory = KeyFactory.getInstance("DH")
aliceKeyFactory ==> java.security.KeyFactory@7b69c6ba
jshell> var bobPublicKeySpec = new X509EncodedKeySpec(encodedBobPublicKey)
bobPublicKeySpec ==> java.security.spec.X509EncodedKeySpec@12f41634
jshell> var bobPublicKey = aliceKeyFactory.generatePublic(bobPublicKeySpec)
bobPublicKey ==> SunJCE Diffie-Hellman Public Key:
y:
f82361 ...
g:
02
l:
1024
Alice recovers the public key received from Bob. The procedure is the same as when Bob restored Alice's public key.
Alice
//Generate a private key based on Bob's public key information
jshell> aliceKeyAgree.doPhase(bobPublicKey, true)
$20 ==> null
jshell> var secret = aliceKeyAgree.generateSecret()
secret ==> byte[256] { 102, 50, -83, 94, 119, -90, -46, -27, ... 66, -94, -121, -19, -119 }
Bob
//Generate a private key based on Alice's public key information
jshell> bobKeyAgree.doPhase(alicePublicKey, true)
$25 ==> null
jshell> var secret = bobKeyAgree.generateSecret()
secret ==> byte[256] { 102, 50, -83, 94, 119, -90, -46, -27, ... 66, -94, -121, -19, -119 }
Finally, each of the KeyAgreement
doPhase () method, the preparation for private key generation is completed.
Use generateSecret () to generate the private key. ..
Below, we have confirmed that encryption and decryption can be performed using the generated private key.
Alice
//Generate AES private key using the first 128 bits of the generated key
jshell> var secretKey = new SecretKeySpec(secret, 0, 16, "AES")
secretKey ==> javax.crypto.spec.SecretKeySpec@fffe86a4
//Encrypted with AES
jshell> var cipher = Cipher.getInstance("AES/CBC/PKCS5PAdding")
cipher ==> javax.crypto.Cipher@12cdcf4
jshell> cipher.init(Cipher.ENCRYPT_MODE, secretKey)
//Export ciphertext
jshell> writeFile("cryptograph", cipher.doFinal("Hello Diffie-Hellman!!".getBytes()))
//Export initialization vector
jshell> writeFile("iv", cipher.getParameters().getEncoded())
Bob
//Generate private key for AES as well as Alice
jshell> var secretKey = new SecretKeySpec(secret, 0, 16, "AES")
secretKey ==> javax.crypto.spec.SecretKeySpec@fffe86a4
//Restore initialization vector from file
jshell> var params = AlgorithmParameters.getInstance("AES")
params ==>
jshell> params.init(readFile("iv"))
//Prepare Cipher in decryption mode
jshell> var cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher ==> javax.crypto.Cipher@cb51256
jshell> cipher.init(Cipher.DECRYPT_MODE, secretKey, params)
//Decrypt the ciphertext
jshell> var message = cipher.doFinal(readFile("cryptograph"))
message ==> byte[22] { 72, 101, 108, 108, 111, 32, 68, 105, 1 ... 08, 109, 97, 110, 33, 33 }
jshell> new String(message)
$31 ==> "Hello Diffie-Hellman!!"
-Security Developer's Guide | JDK 11 Documentation
Recommended Posts