JavaRush /Java Blog /Random EN /Java Cryptography Architecture: Getting Started
Viacheslav
Level 3

Java Cryptography Architecture: Getting Started

Published in the Random EN group
Communication security is one of the most important properties of modern applications. Since ancient times, people have come up with cunning ways that, with the development of mankind, have become a whole science of cryptography. Naturally, Java did not stand aside and offered Java Cryptography Architecture (JCA) to developers. This overview should give you a first idea of ​​how it works.
Java Cryptography Architecture: Getting Started - 1
Content:

Foreword

I suggest you go back in time. Before us is Ancient Rome. And before us is Gaius Julius Caesar, who sends a message to his commanders. Let's see what's in this post:
Java Cryptography Architecture: Getting Started - 2
"ЕСКЕУГЬГМХИФЯ Е УЛП"What could this mean: Let's open the Java Online Compiler, for example: repl.it
class Main {
  public static void main(String[] args) {
    String code = "ЕСКЕУГЬГМХИФЯ Е УЛП";
    for (char symbol : code.toCharArray()) {
      if (symbol != ' ') {
        symbol = (char) (symbol - 3);
      }
      System.out.print(symbol);
    }
  }
}
Before us is the simplest implementation of the Caesar Cipher. According to the work of the ancient Roman historian Suetonius, entitled "The Life of the Twelve Caesars," this is how Caesar encoded messages to his generals. And this is one of the oldest references to the use of such a thing as Cryptography . The word "cryptography" comes from the ancient Greek words "hidden" and "I write", i.e. it is the science of privacy practices. Java has its own support for cryptography and it's called the Java Cryptography Architecture (JCA). The description can be found in the official documentation from Oracle - " Java Cryptography Architecture (JCA) ". I propose to see what opportunities we get thanks to the JCA.
Java Cryptography Architecture: Getting Started - 3

JCA

As we have previously learned, in Java, Java Cryptography Architecture (JCA) offers to work with cryptography. This architecture contains an API (i.e. a certain set of interfaces) and providers (which implement them):
Java Cryptography Architecture: Getting Started - 4
As the documentation says, " The Java platform includes a number of built-in providers ". That is, the Java platform provides a set of built-in providers that can be supplemented if necessary. You can see it for yourself:
import java.security.Provider;
import java.security.Security;
class Main {
  public static void main(String[] args) {
    Provider[] providers = Security.getProviders();
    for (Provider p : providers) {
      System.out.println(p.getName());
    }
  }
}
Registering a third party provider is very easy. For example: Security.addProvider(new BouncyCastleProvider()); This example connects one of the most famous providers - BouncyCastle . But in this review, we will use only basic tools, without third-party libraries. Our main document: " Java Cryptography Architecture (JCA) ". Understanding how JCA works will make it easier to understand the technologies within which this same JCA is actively used. For example: HTTPS (see " From HTTP to HTTPS ").
Java Cryptography Architecture: Getting Started - 5

MessageDigest

The first thing mentioned in the JCA documentation is MessageDigest. In general, Digest in Russian will be the same - a digest and corresponds in meaning to "a summary". But in cryptography, a digest is a hash sum. And you can also easily remember that in English Digest you can also translate how to digest. More details can be read in the JCA documentation under MessageDigest". As stated in the documentation, MessageDigest generates a fixed size result, called digest or hash. Hashing is a one-way function, i.e. if we hashed something, then from the result (i.e. from the hash) we cannot get source. But if the same objects are hashed (for example, strings of the same characters), then their hash must match. As stated in the documentation, such a hash is sometimes also called "checksums" (checksums) or "digital fingerprints" "digital fingerprints" data.Hashing can be done using different algorithms.Available algorithms can be found in " Java Cryptography Architecture Standard Algorithm Name Documentation for JDK 8 ".Let's do the hashing and print the hash to the console:
import javax.xml.bind.DatatypeConverter;
import java.security.*;
public class Main {
  public static void main(String[] args) {
    try {
      MessageDigest digester = MessageDigest.getInstance("SHA-512");
      byte[] input = "Secret string".getBytes();
      byte[] digest = digester.digest(input);
      System.out.println(DatatypeConverter.printHexBinary(digest));
    } catch (NoSuchAlgorithmException e) {
      throw new IllegalStateException(e);
    }
  }
}
Hashing can be useful, for example, when storing passwords. Since the hash of the entered password can be checked against a previously saved hash. If the hashes matched, then the password matched too. For even more secure hashing, a concept such as "salt" (salt) is used. The salt can be implemented using the SecureRandom class . Before executing the digest method, let's describe the addition of "salt":
byte[] salt = new byte[16];
SecureRandom.getInstanceStrong().nextBytes(salt);
digester.update(salt);
But hash is a one-way function. But what if you want to be able to encrypt and decrypt?
Java Cryptography Architecture: Getting Started - 6

Symmetric key cryptography

Symmetric encryption is encryption in which the same key is used for both encryption and decryption. In order to use symmetric encryption, we need a key. To get it we use KeyGenerator . In addition, we need a class that represents a cipher ( Cipher ). As stated in the JCA documentation in the section " Creating a Cipher Object ", to create a Cipher you need to specify in the line not just an algorithm, but a "transformation". The transformation description is as follows: "algorithm/mode/padding":
  • Algorithm : here we look at the standard names for " Cipher (Encryption) Algorithms ". AES is recommended.
  • Mode : encryption mode. For example: ECB or CBC (we'll talk about this a little later)
  • Indent/Split : Each block of data is encrypted separately. This parameter determines how much data to consider for 1 block.
For example, let's take the following transformation: "AES/ECB/PKCS5Padding". That is, the encryption algorithm is AES, the encryption mode is ECB (abbreviation for Electronic Codebook), the block size is PKCS5Padding. PKCS5Padding says that the size of one block is 2 bytes (16 bits). The Electronic Codebook encryption mode involves sequential encryption of each of the blocks:
Java Cryptography Architecture: Getting Started - 7
It might look like this in code:
import javax.xml.bind.DatatypeConverter;
import javax.crypto.*;
import java.security.Key;
public class Main {
  public static void main(String[] args) throws Exception {
    String text = "secret!!secret!!secret!!secret!!";
    // Generate new key
    KeyGenerator keygen = KeyGenerator.getInstance("AES");
    keygen.init(256);
    Key key = keygen.generateKey();
    // Encrypt with key
    String transformation = "AES/ECB/PKCS5Padding";
    Cipher cipher = Cipher.getInstance(transformation);
    cipher.init(Cipher.ENCRYPT_MODE, key);
    byte[] encrypted = cipher.doFinal(text.getBytes());
    System.out.println(DatatypeConverter.printHexBinary(encrypted));
    // Decrypt with key
    cipher.init(Cipher.DECRYPT_MODE, key);
    String result = new String(cipher.doFinal(encrypted));
    System.out.println(result);
  }
}
If we execute, then we will see a replay as expected, because we specified 32 characters. These characters make up 2 blocks of 16 bits:
Java Cryptography Architecture: Getting Started - 8
To avoid repetition in this case, another mode should be used - Cipher Block Chaining (CBC). This mode introduces the concept of an Initialization Vector (represented by the IvParameterSpec class). And also thanks to this mode, the result of the generation of the previous block will be used to generate the next one:
Java Cryptography Architecture: Getting Started - 9
Let's write it in code now:
import javax.xml.bind.DatatypeConverter;
import javax.crypto.*;
import java.security.*;
import javax.crypto.spec.IvParameterSpec;
public class Main {
  public static void main(String[] args) throws Exception {
    // Initialization Vector
    SecureRandom random = SecureRandom.getInstanceStrong();
    byte[] rnd = new byte[16];
    random.nextBytes(rnd);
    IvParameterSpec ivSpec = new IvParameterSpec(rnd);
    // Prepare key
    KeyGenerator keygen = KeyGenerator.getInstance("AES");
    keygen.init(256);
    Key key = keygen.generateKey();
    // CBC
    String text = "secret!!secret!!secret!!secret!!";
    String transformation = "AES/CBC/PKCS5Padding";
    Cipher cipher = Cipher.getInstance(transformation);
    cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
    byte[] enc = cipher.doFinal(text.getBytes());
    System.out.println(DatatypeConverter.printHexBinary(enc));
    // Decrypt
    cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
    String result = new String(cipher.doFinal(enc));
    System.out.println(result);
  }
}
As we can see, as a result, we do not see repeating cipher blocks. For this reason, ECB mode is not recommended as makes it possible to see repetitions and use this knowledge to decipher. For more information about ECB and CBC, I advise you to read the material: " Electronic Codebook Mode ". But symmetric encryption has an obvious problem - you need to somehow transfer the key from the one who encrypts to the one who encrypts. And on this path, this key can be intercepted and then it will be possible to intercept data. And this problem is designed to solve asymmetric encryption.
Java Cryptography Architecture: Getting Started - 10

Asymmetric cryptography

Asymmetric encryption or Public-key cryptography is an encryption method that uses a pair of keys: a private key (kept secret from everyone) and a public key (publicly available). This separation is necessary in order to securely exchange the public key between the parties to the exchange of information, while keeping the secret key safe. In creating a key pair, the KeyGenerator is no longer enough for us, we need the KeyPairGenerator . Let's look at an example:
import javax.crypto.*;
import java.security.*;
public class Main {
  public static void main(String[] args) throws Exception {
    KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
    generator.initialize(1024);
    KeyPair keyPair = generator.generateKeyPair();
    // Encrypt with PRIVATE KEY
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
    byte[] data = cipher.doFinal("Hello!".getBytes());
    // Decrypt with PUBLIC KEY
    cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
    byte[] result = cipher.doFinal(data);
    System.out.println(new String(result));
  }
}
It is important to understand here that using asymmetric encryption, we always use one key from KeyPair for encryption and the other for decryption. But since the meaning of encryption is that only the recipient could decrypt, then it is encrypted with a public key, and decrypted only with a private one.
Java Cryptography Architecture: Getting Started - 11

Digital signature

As we saw above, knowing the public key, you can send data so that only the owner of the private key can decrypt it. That is, the essence of asymmetric encryption is that anyone encrypts, but only we read. There is also a reverse procedure - a digital signature, represented by the Signature class . The digital signature can use the following algorithms: " Signature Algorithms ". The JCA documentation suggests taking a closer look at these two: DSAwithMD5 and RSAwithMD5 Which is better than DSA or RSA and how they differ can be read here: " Which Works Best for Encrypted File Transfers - RSA or DSA? ". Or read the discussions here: " RSA vs. DSA for SSH authentication keys". So, a digital signature. We will need, as before, a KeyPair key pair and a new Signature class. If you have tested in online compilers so far, then the following example may be somewhat difficult for them. My example was executed only here: rextester .com Import the classes we need:
import javax.crypto.*;
import java.security.*;
And also rewrite the main method:
public static void main(String[] args) throws Exception {
    // Generate keys
    KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
    SecureRandom random = SecureRandom.getInstanceStrong();
    generator.initialize(2048, random);
    KeyPair keyPair = generator.generateKeyPair();
    // Digital Signature
    Signature dsa = Signature.getInstance("SHA256withRSA");
    dsa.initSign(keyPair.getPrivate());
    // Update and sign the data
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
    byte[] data = cipher.doFinal("Hello!".getBytes());
    dsa.update(data);
    byte[] signature = dsa.sign();
    // Verify signature
    dsa.initVerify(keyPair.getPublic());
    dsa.update(data);
    boolean verifies = dsa.verify(signature);
    System.out.println("Signature is ok: " + verifies);
    // Decrypt if signature is correct
    if (verifies) {
      cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
      byte[] result = cipher.doFinal(data);
      System.out.println(new String(result));
    }
}
This is how a digital signature works. Digital signature is an interesting topic. I advise you to look at the report on this topic:
Java Cryptography Architecture: Getting Started - 12
Above, we saw how the parties exchange data. Isn't there some standard interface for this interaction provided in the JCA? It turns out - there is. Let's take a look at it.
Java Cryptography Architecture: Getting Started - 13

key agreement

Java Cryptography Architecture introduces an important tool - Key agreement is a protocol. It is represented by the KeyAgreement class . As stated in the JCA documentation, this protocol allows you to set the same cryptographic key for several parties, while no secret information is transferred between the parties. Sounds weird? Then let's look at an example:
// 1. Одна из сторон (Алиса) генерирует пару ключей. Encoded публичный ключ отдаёт.
KeyPairGenerator generator = KeyPairGenerator.getInstance("DH");
KeyPair aliceKeyPair = generator.generateKeyPair();
byte[] alicePubKeyEncoded = aliceKeyPair.getPublic().getEncoded();

// 2. Другая сторона (например, Боб) получает открытый ключ Алисы
KeyFactory bobKeyFactory = KeyFactory.getInstance("DH");
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(alicePubKeyEncoded);
PublicKey alicePubKey = bobKeyFactory.generatePublic(x509KeySpec);
// Параметры, которые использовала Алиса при генерации ключей
DHParameterSpec dhParamFromAlicePubKey = ((DHPublicKey)alicePubKey).getParams();
// Создаёт свою пару ключей. Отдаёт свой Encoded открытый ключ
KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH");
bobKpairGen.initialize(dhParamFromAlicePubKey);
KeyPair bobKeyPair = bobKpairGen.generateKeyPair();
byte[] bobPubKeyEncoded = bobKeyPair.getPublic().getEncoded();

Теперь, у Алисы есть открытый ключ Боба, а у Боба есть открытый ключ Алисы. What дальше?
Как сказано в documentации JCA, у нас есть инструмент KeyAgreement, https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#KeyAgreement который позволяет установить одинаковые ключи шифрования без необходимости обмениваться секретной информацией (т.е. без обмена private key). Соглашение выглядит следующим образом:
// 3. Соглашение по протоколу Диффи-Хеллмана (Diffie–Hellman, DH)
KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");
aliceKeyAgree.init(aliceKeyPair.getPrivate());
// Алиса на основе ключа боба и своего private key создаёт общий shared ключ
KeyFactory aliceKeyFactory = KeyFactory.getInstance("DH");
x509KeySpec = new X509EncodedKeySpec(bobPubKeyEncoded);
PublicKey bobPubKey = aliceKeyFactory.generatePublic(x509KeySpec);
aliceKeyAgree.doPhase(bobPubKey, true);
byte[] aliceSharedSecret = aliceKeyAgree.generateSecret();
SecretKeySpec aliceAesKey = new SecretKeySpec(aliceSharedSecret, 0, 16, "AES");
// Боб на основе ключа Алисы и своего private key создаёт общий shared ключ
KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");
bobKeyAgree.init(bobKeyPair.getPrivate());
bobKeyAgree.doPhase(alicePubKey, true);
byte[] bobSharedSecret = bobKeyAgree.generateSecret();
SecretKeySpec bobAesKey = new SecretKeySpec(bobSharedSecret, 0, 16, "AES");
// Общий ключ у Алисы и Боба одинаков
System.out.println("Shared keys are equals: " + Arrays.equals(aliceSharedSecret, bobSharedSecret));

Далее Боб и Алиса, используя общий ключ, про который больше никто не знает, обмениваются зашифрованными данными:
// 4. Боб шифрует сообщение для Алисы
Cipher bobCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
bobCipher.init(Cipher.ENCRYPT_MODE, bobAesKey);
byte[] ciphertext = bobCipher.doFinal("Hello, Alice!".getBytes());
// Передаёт Алисе параметры, с которыми выполнялась шифровка
byte[] encodedParamsFromBob = bobCipher.getParameters().getEncoded();

// 5. Алиса принимает сообщение и расшифровывает его
AlgorithmParameters aesParams = AlgorithmParameters.getInstance("AES");
aesParams.init(encodedParamsFromBob);
Cipher aliceCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
aliceCipher.init(Cipher.DECRYPT_MODE, aliceAesKey, aesParams);
byte[] recovered = aliceCipher.doFinal(ciphertext);
System.out.println(new String(recovered));
This example was taken from the example JCA documentation: " Diffie-Hellman Key Exchange between 2 Parties ". This is how the work of asymmetric encryption performed by Java Cryptography Architecture using the Key agreement protocol looks like. More about asymmetric encryption recommended videos:
Java Cryptography Architecture: Getting Started - 14

Certificates

Well, for dessert, we have left no less important - certificates. Typically, certificates are generated using the keytool utility included with the jdk. More details can be read, for example, here: " Generating a self-signed SSL certificate using the Java keytool command ". You can also read the manuals from Oracle. For example, here: " To Use keytool to Create a Server Certificate ". Let's use the Tutorialspoint Java Online Compiler as an example :
import sun.security.tools.keytool.CertAndKeyGen;
import sun.security.x509.*;
import java.security.cert.*;
import java.security.*;
// Compiler args: -XDignore.symbol.file
public class Main {
  public static void main(String[] args) throws Exception {
    CertAndKeyGen certGen = new CertAndKeyGen("RSA", "SHA256WithRSA", null);
    // generate it with 2048 bits
    certGen.generate(2048);
    PrivateKey privateKey = certGen.getPrivateKey();
    X509Key publicKey = certGen.getPublicKey();
    // prepare the validity of the certificate
    long validSecs = (long) 365 * 24 * 60 * 60; // valid for one year
    // enter your details according to your application
    X500Name principal = new X500Name("CN=My Application,O=My Organisation,L=My City,C=DE");
    // add the certificate information, currently only valid for one year.
    X509Certificate cert = certGen.getSelfCertificate(principal, validSecs);
    // Public Key from Cert equals Public Key from generator
    PublicKey publicKeyFromCert = cert.getPublicKey();
    System.out.println(publicKeyFromCert.equals(publicKey));
  }
}
As we can see, the certificate provides the ability to provide a public key. This method has a drawback - we use sun.security, which is considered risky, because. this package is not part of the public Java API. That is why when compiling, you must specify the parameter - XDignore.symbol.file. There is another way - to create a certificate manually. The downside is that it uses an internal API that is not documented. However, it is useful to know about it. At least, because it clearly shows how the specification RFC-2459: " Internet X.509 Public Key Infrastructure " is used. Here is an example:
// 1. Генерируем пару ключей
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(4096);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 2. Определяем данные сертификата
// Определяем срок действия сертификата
Date from = new Date();
Date to = new Date(from.getTime() + 365 * 1000L * 24L * 60L * 60L);
CertificateValidity interval = new CertificateValidity(from, to);
// Определяем subject name, т.е. Name того, с чем ассоциирован публичный ключ
// CN = Common Name. Через точку с запятой могут быть указаны также другие атрибуты
// См. https://docs.oracle.com/cd/E24191_01/common/tutorials/authz_cert_attributes.html
X500Name owner = new X500Name("cn=Unknown");
// Уникальный в пределах CA, т.е. Certificate Authority (тот, кто выдаёт сертификат) номер
BigInteger number = new BigInteger(64, new SecureRandom());
CertificateSerialNumber serialNumber = new CertificateSerialNumber(number);
// Определяем алгоритм подписи сертификата
AlgorithmId algorithmId = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
CertificateAlgorithmId certificateAlgorithmId = new CertificateAlgorithmId(algorithmId);
// 3. По подготовленной информации создаём сертификат
X509CertInfo info = new X509CertInfo();
info.set(X509CertInfo.VALIDITY, interval);
info.set(X509CertInfo.SERIAL_NUMBER, serialNumber);
info.set(X509CertInfo.SUBJECT, owner);
info.set(X509CertInfo.ISSUER, owner);
info.set(X509CertInfo.KEY, new CertificateX509Key(keyPair.getPublic()));
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
info.set(X509CertInfo.ALGORITHM_ID, certificateAlgorithmId);
// 4. Подписываем сертификат
X509CertImpl certificate = new X509CertImpl(info);
certificate.sign(keyPair.getPrivate(), "SHA256withRSA");
// 5. Проверка сертификата
try {
	// В случае ошибки здесь будет брошено исключение. Например: java.security.SignatureException
	certificate.verify(keyPair.getPublic());
} catch (Exception e) {
	throw new IllegalStateException(e);
}
Java Cryptography Architecture: Getting Started - 15

Key store (KeyStore)

The last thing I would like to talk about is the key and certificate store, which is called KeyStore. It is clear that constantly generating certificates and keys is expensive and pointless. So they need to be stored securely somehow. There is a tool for this - KeyStore. The keystore is described in the JCA documentation in the KeyManagement chapter . The API for working with it is very clear. Here is a small example:
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
String alias = "EntityAlias";
java.security.cert.Certificate[] chain = {certificate};
keyStore.setKeyEntry(alias, keyPair.getPrivate(), "keyPassword".toCharArray(), chain);
// Загрузка содержимого (Private Key + Certificate)
Key key = keyStore.getKey(alias, "keyPassword".toCharArray());
Certificate[] certificateChain = keyStore.getCertificateChain(alias);
// Сохранение KeyStore на диск
File file = File.createTempFile("security_", ".ks");
System.out.println(file.getAbsolutePath());
try (FileOutputStream fos = new FileOutputStream(file)) {
	keyStore.store(fos, "keyStorePassword".toCharArray());
}
As you can see from the example, it is first executed loadfor the KeyStore. But in our case, we specified the first null attribute, i.e. there is no source for KeyStore. This means that the KeyStore is created empty in order to save it further. The second parameter is also null, because we create a new KeyStore. If we were loading the KeyStore from a file, then we would need to specify a password here (similar to the KeyStore method called store).
Java Cryptography Architecture: Getting Started - 16

Outcome

So we have considered with you the most basic and elementary actions within the Java Cryptography Architecture (aka JCA). We saw what symmetric and asymmetric encryption is and how it is implemented in JCA. We have seen how certificates and digital signatures are created and how they are used. These are just the basics of the basics, behind which there are many more complex and interesting things. I hope that this review material will be useful and will interest you for further study of this area. Well, in conclusion, I would like to point out a super video on this topic:
Java Cryptography Architecture: Getting Started - 17
Additional materials:
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION