Fundamentals of Digital Signatures and Certificates in Java
Having online access to almost anything and being able to interact with one another is part of our daily routine. However, living in the online world means you have to question the legitimacy of data: is each email trustworthy? When you log in to your favorite website, how do you know it’s reliable? Is the software update you want to install authentic, or is it a virus in disguise waiting to infect your device?
In this article we will take a closer to how you can verify digital signatures and inspect certificates within your Java code.
Implementing Digital Signatures with Java
You should use digital signatures if you need a mechanism for validating:
- Authenticity: the identity of the message author is accurate.
- Integrity: the message was not altered during the transmission.
- Non-repudiation: the transferred message has been sent and received by the parties claiming to have shipped and accepted it.
To send a message using a digital signature you first need to generate an asymmetric key pair using and algorithm, for example Ed25519
:
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(Ed25519);
KeyPair keypair = keyPairGenerator.generateKeyPair();
Next, you should feed the message into the signature object and call the sign
method:
public static byte[] generateDigitalSignature(byte[] plainText, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance("Ed25519");
signature.initSign(privateKey);
signature.update(plainText);
return signature.sign();
}
In the method generateDigitalSignature
, you obtain an instance of the Signature
object by passing the signing algorithm Ed25519
, an elliptic curve signing algorithm using EdDSA
and Curve25519
.
Then, you initialize the signature with a private key, pass the message, and finish the signing operation by storing it as a byte array.
To verify a signature, you create again a Signature
instance, this time passing the text message and the public key, like in verifyDigitalSignature
method:
public static boolean verifyDigitalSignature(byte[] plainText, byte[] digitalSignature, PublicKey publicKey)
throws Exception {
Signature signature = Signature.getInstance("Ed25519");
signature.initVerify(publicKey);
signature.update(plainText);
return signature.verify(digitalSignature);
}
Finally, you can check the signature by invoking the verify
method on it.
You can try the previous methods by running the following snippet of code in jshell
:
import java.security.*;
import java.util.HexFormat;
import java.util.Scanner;
public class DigitalSignatureExample {
public static final String Ed25519 = "Ed25519";
public static byte[] generateDigitalSignature(byte[] plainText, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance(Ed25519);
signature.initSign(privateKey);
signature.update(plainText);
return signature.sign();
}
public static boolean verifyDigitalSignature(byte[] plainText, byte[] digitalSignature, PublicKey publicKey)
throws Exception {
Signature signature = Signature.getInstance(Ed25519);
signature.initVerify(publicKey);
signature.update(plainText);
return signature.verify(digitalSignature);
}
public static void main(String[] args) throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("Ed25519");
KeyPair keypair = keyPairGenerator.generateKeyPair();
Scanner message = new Scanner(System.in);
System.out.print("Enter the message you want to encrypt using Ed25519: ");
String plainText = message.nextLine();
byte[] bytes = plainText.getBytes();
message.close();
byte[] digitalSignature = generateDigitalSignature(bytes, keypair.getPrivate());
System.out.println("Signature Value:\n " + HexFormat.of().formatHex(digitalSignature));
System.out.println("Verification: " + verifyDigitalSignature(bytes, digitalSignature, keypair.getPublic()));
}
}
In the following section, let's investigate more about digital certificates and how they can help you link the public key ownership with the entity that owns it.
Digital Certificates Basics in Java
In cryptography, certificates represent electronic document binding some pieces of information together, such as a user’s identity and public key. A trusted third party - Certificate Authority (CA) - issues digital certificates to verify the identity of the certificate holder.
Figure 1: Portion of a certificate from the output of keytool -printcert -sslserver oracle.com/java
A digital certificate contains:
- Serial number used to uniquely identify a certificate, the individual or the entity determined by the certificate.
- Expiration dates (not before and not after)
- Name of certificate holder (subject).
- Copy of certificate holder's public key. You need this to decrypt messages and digital signatures.
- Digital Signature of the certificate issuing authority.
To gain a better understanding of digital signatures and certificates, please look at the table below:
Characteristic | Digital Signature | Digital Certificate |
---|---|---|
Purpose | Verify authenticity, integrity, non-repudiation. | Verify the identity of the sender and receiver. |
Process | Apply the cryptographic algorithm on the message with a private key to generate a unique digital signature. | A Certifying Authority (CA) generates it: Key Generation, Registration, Verification, Creation. |
Standard | Digital Signature Standard (DSS) | X.509 Standard Format |
Certificates not issued by a known Certificate Authority but rather by the server hosting the certificate are called self-signed.
You can generate a self-signed certificate valid for 365 days using keytool
by running the following command in a terminal window:
$ keytool -genkeypair -keyalg ed25519 -alias mykey -keystore mykeystore.jks -storepass jkspass \
-dname "CN=server.mycompany.com,OU=My Company Dev Team,O=My Company,c=NL" -validity 365
> Generating 255 bit Ed25519 key pair and self-signed certificate (Ed25519)
> with a validity of 365 days for: CN=server.mycompany.com, OU=My Company Dev Team, O=My Company, C=NL
The previous command performs several operations:
- creates the keystore named
mykeystore
and assigns it the passwordjkspass
. - generates a public/private key pair for the entity whose "distinguished name" has a common name of "server.company.com", organizational unit of "My Company", organization of "My Company" and two-letter country code of "NL".
- uses the default
Ed25519
key generation algorithm to create the keys. - creates a self-signed certificate that includes the public key and the distinguished name information. This certificate will be valid for 365 days and is associated with the private key in a keystore entry referred to by the alias
mykey
.
You can read the certificate associated with the alias from the keystore and store it in a file mycert.pem
in the printable encoding format via:
$ keytool -exportcert -alias mykey \
-keystore mykeystore.jks -storepass jkspass -rfc -file mycert.pem
> Certificate stored in file <mycert.pem>
To parse and manage this certificate, work with certification paths and certificate revocation lists (CRLs) you can use either keytool
or the classes from java.security.cert
package.
For example, you can generate a java.security.cert.Certificate
and cast it to java.security.cert.X509Certificate
using the previously created self-signed certificate:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
public class CertificateManagementExample {
public static void main(String[] args) throws Exception {
//generate a certificate object
X509Certificate cert = extractCertificate("mycert.pem");
}
private static X509Certificate extractCertificate(String filePath) throws IOException, CertificateException {
try (InputStream is = new FileInputStream(filePath)) {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
return (X509Certificate) certificateFactory.generateCertificate(is);
}
}
}
While java.security.cert.Certificate
is an abstraction for certificates that have different formats, the class java.security.cert.X509Certificate
provides a standard way to access all the attributes of an X.509
certificate.
If you need to validate X.509
certification paths you should use java.security.cert.TrustAnchor
and java.security.cert.CertPathValidator
for certificate chains.
When working with digital certificates, you have a quick way to locate a particular certificate in the credential store of your system by using a certificate thumbprint.
A certificate's fingerprint is the unique identifier of the certificate and can help you compare certificates. You can easily compute a certificate’s thumbprint by using java.security.MessageDigest
over the java.security.cert.X509Certificate
object:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.HexFormat;
public class CertificateManagementExample {
public static void main(String[] args) throws Exception {
//generate a certificate object
X509Certificate cert = extractCertificate("mycert.pem");
//compute thumbprint
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(cert.getEncoded());
System.out.println(HexFormat.ofDelimiter(":").formatHex(md.digest()));
}
private static X509Certificate extractCertificate(String filePath) throws IOException, CertificateException {
try (InputStream is = new FileInputStream(filePath)) {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
return (X509Certificate) certificateFactory.generateCertificate(is);
}
}
}
Congratulations, you’ve learned about the differences between digital signatures and digital certificates and how to interact with those in Java using the Java Security API.
Useful Links:
- Oracle JDK Cryptographic Roadmap
- Java Security Standard Algorithm Names Specification
- DSS
- T-REC-X.509
- RFC-8032
More Learning
Last update: Invalid DateTime