JCA (Java Cryptography Architecture) Usage Memo

What is JCA

Abbreviation for Java Cryptography Architecture. An API and framework for using cryptography in Java.

environment

Java openjdk 11.0.2

OS Windows 10 (64bit)

Prerequisite knowledge

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.

Predefined functions

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));
}

Notes

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.

Basic mechanism of JCA

Design concept

JCA is designed with the basic policy of achieving the following.

  1. Implementation independence and interoperability
  2. Algorithm independence and extensibility

Implementation independence

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

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.

Algorithm independence

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

Algorithm extensibility refers to the ease with which new cryptographic algorithms can be added and used when they are invented and implemented.

Cryptographic service provider

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

Search for a provider

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.

Check the providers installed in the execution environment

A class called java.security.Provider is prepared as a class representing the provider. ing.

All Providers 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.

Engine class

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

Get an instance

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 **.

Algorithms supported by the specification

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 )

Check the algorithms supported by the execution environment provider

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.

Algorithm-independent API

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.

Why the package is divided into two

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

Message digest (hash)

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.

Split input

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 bytes 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.

Convert to a hexadecimal string

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"

Calculate the hash value of the file

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 ()ofMessageDigest` 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.

key

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.

  1. getAlgorithm() --Get the algorithm name associated with the key, such as AES or RSA
  2. getEncoded() --Get the value of the key encoded in a specific format (such as X.509 or PKCS8)
  3. getFormat() --Get the name of the encoding

Key specifications

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.png

Generator and Factory

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.

Encryption / decryption

Both the common key cryptography and public key cryptography encryption / decryption processing are javax.crypto.Cipher. /crypto/Cipher.html) Use the class.

Cipher algorithm specification

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.

Initialization

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 andDECRYPT_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.

Encryption / decryption

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.

jca.jpg

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.

Shouldn't transferTo () be used?

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.

Key generation

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.

Key length specification

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).

encryption

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 ().

Decryption

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.

Output the key to a file

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.

Restore key from file

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.

Password-based encryption (PBE)

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 ofPBEParameterSpec` is slightly different.)

RSA Try using RSA for public key cryptography.

Key generation

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.

Key length specification

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.

encryption

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 U5wgL 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.

Decryption

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.

Message authentication code (MAC)

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.

Divide and calculate

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.

Digital signature

RSA signature

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 abyte` 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.

DSA signature

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.

Command format

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

Keystore

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).

entry

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.

Entry type

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.

Check the contents of the keystore

Display list

$ 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.

Display by specifying an entry

$ 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                                        @.;.
]
]

Change the keystore path

$ keytool -storepasswd
New keystore password:
Please re-enter your new keystore password:

You can change the keystore password with the -storepasswd command.

Delete entry

$ keytool -delete -alias foo

You can delete any entry with the -delete command.

The entry to be deleted is specified by the -alias option.

Change entry alias

$ 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.

Check the keystore built into the JRE

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.

Generate a private key

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.

Can't generate a password-based key?

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?

Generate a public key cryptographic key pair

$ 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.

X.500 Distinguished Name

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, USSuch)

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

Certificate validity period

The validity period of the certificate can be controlled by the following two options.

-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.

Output certificate

$ 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-----

View the contents of the certificate file

$ 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.

View certificate for any SSL server

$ 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).

Create a certificate signing request

$ 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

Generate a certificate from a certificate signing request

#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 entryca-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-----

Get the 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.

  1. Import certificate response
  2. Import trusted certificate

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

Import certificate response

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.

Verification of certificate response

The certificate response is validated before the actual import.

The verification is roughly carried out in the following two ways.

  1. Is the certificate response indeed corresponding to the key entry specified by -alias?
  2. Whether the certificate response is reliable

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.

jca.jpg

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.

When multiple certificates are included 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.

jca.jpg

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.

jca.jpg

If the certificate response is a single certificate

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.

jca.jpg

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.

Import trusted certificate

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.

Also use cacerts certificates for validation

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)

Actually try

#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.

Get a KeyStore instance

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)

Read the keystore file

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 thechar` array in the second argument.

Load at the same time as instance acquisition

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.

Get alias names for all entries

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.

Check the entry type from the alias name

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)).

Get the private key

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).

Get a private key

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.

Get public key (certificate)

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.

Get a certificate chain

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.

Add a private key entry

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)

Output to a file

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.

Certificate

Is there a new API to create?

[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.

Read the certificate

Specifiable certificate type

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.

Read the certificates one by one

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)

Read multiple certificates at once

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.

Verify certificate signature

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.

random number

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).

Should I use getInstance () or the constructor?

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).

Diffie-Hellman Key Exchange

KeyAgreement is a class for realizing Diffie-Hellman key exchange. It is prepared.

The flow of key exchange using KeyAgreement is as follows.

jca.png

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!!"

reference

-Security Developer's Guide | JDK 11 Documentation

Recommended Posts

JCA (Java Cryptography Architecture) Usage Memo
Java memo
java anything memo
Java Silver memo
java, maven memo
JavaParser usage memo
Java SE 7 memo
WatchService usage memo
PlantUML usage memo
java anything memo 2
Java specification memo
JUnit5 usage memo
Java pattern memo
Java development environment memo
Spring Shell usage memo
java basic knowledge memo
Java learning memo (method)
Java Kuche Day memo
[Java ~ Method ~] Study memo (5)
java se 8 programmer Ⅰ memo
Java paid private memo
Regarding Java variable usage
[Java ~ Array ~] Study memo 4
Java learning memo (basic)
java lambda expression memo
(Memo) Java for statement
Java lambda expression [memo]
Java learning memo (interface)
[Java] Implicit inheritance memo
Java learning memo (inheritance)
java competitive programming memo
[Memo] Java Linked List
Java (WebSphere Application Server) memo [1]
[Java] Variable name naming memo
Java memo (standard class) substring
[Java] Mirage-Basic usage of SQL
Java learning memo (data type)
Spring Security usage memo CSRF
Java memo (standard class) length
Spring Security usage memo Run-As
Java Silver Study Method Memo
[Java ~ Boolean value ~] Study memo (2)
Create a java method [Memo] [java11]
Java Silver exam preparation memo
Java exception handling usage rules
Spring Security Usage memo Method security
Spring Security usage memo Remember-Me
Java learning memo (logical operator)
Java learning memo (abstract class)
[Java] Date Related Term Memo
Dependency Management Plugin Usage memo
Java study memo 2 with Progate
Spring Security usage memo CORS
[Java11] Stream Usage Summary -Basics-
Spring Security usage memo test
What are Java metrics? _Memo_20200818
Java HashMap, entrySet [Personal memo]