Partner – Microsoft – NPI EA (cat = Baeldung)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, visit the documentation page.

You can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Microsoft – NPI EA (cat= Spring Boot)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, you can get started over on the documentation page.

And, you can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Orkes – NPI EA (cat=Spring)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag=Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – MongoDB – NPI EA (tag=MongoDB)
announcement - icon

Traditional keyword-based search methods rely on exact word matches, often leading to irrelevant results depending on the user's phrasing.

By comparison, using a vector store allows us to represent the data as vector embeddings, based on meaningful relationships. We can then compare the meaning of the user’s query to the stored content, and retrieve more relevant, context-aware results.

Explore how to build an intelligent chatbot using MongoDB Atlas, Langchain4j and Spring Boot:

>> Building an AI Chatbot in Java With Langchain4j and MongoDB Atlas

Partner – LambdaTest – NPI EA (cat=Testing)
announcement - icon

Accessibility testing is a crucial aspect to ensure that your application is usable for everyone and meets accessibility standards that are required in many countries.

By automating these tests, teams can quickly detect issues related to screen reader compatibility, keyboard navigation, color contrast, and other aspects that could pose a barrier to using the software effectively for people with disabilities.

Learn how to automate accessibility testing with Selenium and the LambdaTest cloud-based testing platform that lets developers and testers perform accessibility automation on over 3000+ real environments:

Automated Accessibility Testing With Selenium

1. Overview

In this tutorial, we’re going to learn about the Digital Signature mechanism and how we can implement it using the Java Cryptography Architecture (JCA). We’ll explore the KeyPair, MessageDigest, Cipher, KeyStore, Certificate, and Signature JCA APIs.

We’ll start by understanding what is Digital Signature, how to generate a key pair, and how to certify the public key from a certificate authority (CA). After that, we’ll see how to implement Digital Signature using the low-level and high-level JCA APIs.

2. What Is Digital Signature?

In this section, we’ll describe the Digital Signature concept and the way it is used in sending a message.

2.1. Digital Signature Definition

Digital Signature is a technique for ensuring:

  • Integrity: the message hasn’t been altered in transit
  • Authenticity: the author of the message is really who they claim to be
  • Non-repudiation: the author of the message can’t later deny that they were the source

2.2. Sending a Message with a Digital Signature

Technically speaking, a digital signature is an encrypted hash (digest, checksum) of a message. That means we generate a hash from a message and encrypt it with a private key according to a chosen algorithm.

The message, the encrypted hash, the corresponding public key, and the algorithm are all then sent. This is classified as a message with a digital signature.

2.3. Receiving and Checking a Digital Signature

To check the digital signature, the message receiver generates a new hash from the received message, decrypts the received encrypted hash using the public key, and compares them. If they match, the Digital Signature is said to be verified.

We should note that we only encrypt the message hash, and not the message itself. In other words, Digital Signature doesn’t try to keep the message secret. Our digital signature only proves that the message was not altered in transit.

When the signature is verified, we’re sure that only the owner of the private key could be the author of the message.

3. Digital Certificate and Public Key Identity

A certificate is a document that associates an identity to a given public key.  Certificates are signed by a third-party entity called a Certificate Authority (CA).

We know that if the hash we decrypt with the published public key matches the actual hash, then the message is signed. However, how do we know that the public key really came from the right entity? This is solved by the use of digital certificates.

A Digital Certificate contains a public key and is itself signed by another entity. The signature of that entity can itself be verified by another entity and so on. We end up having what we call a certificate chain. Each top entity certifies the public key of the next entity. The most top-level entity is self-signed, which means that his public key is signed by his own private key.

The X.509 is the most used certificate format, and it is shipped either as binary format (DER) or text format (PEM). JCA already provides an implementation for this via the X509Certificate class.

4. KeyPair Management

Since Digital Signature uses a private and public key, we’ll use the JCA classes PrivateKey and PublicKey for signing and checking a message, respectively.

4.1. Getting a KeyPair

To create a key pair of a private and public key, we’ll use the Java keytool.

Let’s generate a key pair for the sender using the genkeypair command:

keytool -genkeypair -alias senderKeyPair -keyalg RSA -keysize 2048 \
  -dname "CN=Baeldung" -validity 365 -storetype JKS \
  -keystore sender_keystore.jks -storepass changeit

This creates a private key and its corresponding public key for us. The public key is wrapped into an X.509 self-signed certificate which is wrapped in turn into a single-element certificate chain. We store the certificate chain and the private key in the Keystore file sender_keystore.jks, which we can process using the KeyStore API.

Here, we’ve used the JKS key store format. Also, we should remember the password and alias, as we’ll use them in the next subsection when loading the Keystore file.

4.2. Loading the Private Key for Signing

In order to sign a message, we need an instance of PrivateKey.

Using the KeyStore API, and the previous Keystore file, sender_keystore.jks, we can get a PrivateKey object:

KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream("sender_keystore.jks"), "changeit");
PrivateKey privateKey = 
  (PrivateKey) keyStore.getKey("senderKeyPair", "changeit");

4.3. Publishing the Public Key

Before we can publish the public key, we must first decide whether we’re going to use a self-signed certificate or a CA-signed certificate.

When using a self-signed certificate, we need only to export it from the Keystore file. We can do this with the exportcert command:

keytool -exportcert -alias senderKeyPair -storetype JKS \
  -keystore sender_keystore.jks -file \
  sender_certificate.cer -rfc -storepass changeit

Otherwise, if we’re going to work with a CA-signed certificate, then we need to create a certificate signing request (CSR). We do this with the certreq command:

keytool -certreq -alias senderKeyPair -storetype JKS \
  -keystore sender_keystore.jks -file -rfc \
  -storepass changeit > sender_certificate.csr

The CSR file, sender_certificate.csr, is then sent to a Certificate Authority for the purpose of signing. When this is done, we’ll receive a signed public key wrapped in an X.509 certificate, either in binary (DER) or text (PEM) format. Here, we’ve used the rfc option for a PEM format.

The public key we received from the CA, sender_certificate.cer, has now been signed by a CA and can be made available for clients.

4.4. Loading a Public Key for Verification

On the receiver side, we need a Keystore file for storing the public key. Let’s create an empty Keystore:

keytool -genkeypair -alias receiverKeyPair -keyalg RSA -keysize 2048 \
  -dname "CN=Baeldung" -validity 365 -storetype JKS \
  -keystore receiver_keystore.jks -storepass changeit 
 
keytool -delete -alias receiverKeyPair -storepass changeit -keystore receiver_keystore.jks 

Having access to the public key, a receiver can load it into their Keystore using the importcert command:

keytool -importcert -alias receiverKeyPair -storetype JKS \
  -keystore receiver_keystore.jks -file \
  sender_certificate.cer -rfc -storepass changeit

And using the KeyStore API as before, we can get a PublicKey instance:

KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream("receiver_keytore.jks"), "changeit");
Certificate certificate = keyStore.getCertificate("receiverKeyPair");
PublicKey publicKey = certificate.getPublicKey();

Now that we have a PrivateKey instance on the sender side, and an instance of the PublicKey on the receiver side, we can start the process of signing and verification.

5. Digital Signature With MessageDigest and Cipher Classes

As we have seen, the digital signature is based on hashing and encryption.

Usually, we use the MessageDigest class with SHA or MD5 for hashing and the Cipher class for encryption.

Now, let’s start implementing digital signature mechanisms.

5.1. Generating a Message Hash

A message can be a string, a file, or any other data. So let’s take the content of a simple file:

byte[] messageBytes = Files.readAllBytes(Paths.get("message.txt"));

Now, using MessageDigest, let’s use the digest method to generate a hash:

MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] messageHash = md.digest(messageBytes);

Here, we’ve used the SHA-256 algorithm, which is the one most commonly used. Other alternatives are MD5, SHA-384, and SHA-512.

It is necessary to encode the hash value of the message to be signed. We can do this with the DigestInfo class:

DigestAlgorithmIdentifierFinder hashAlgorithmFinder = new DefaultDigestAlgorithmIdentifierFinder();
AlgorithmIdentifier hashingAlgorithmIdentifier = hashAlgorithmFinder.find("SHA-256");
DigestInfo digestInfo = new DigestInfo(hashingAlgorithmIdentifier, messageHash);
byte[] hashToEncrypt = digestInfo.getEncoded();

The DigestInfo class uses the identifier of the hash function and the message hash. Here, the hash value is encoded using getEncoded() method.

5.2. Encrypting the Generated Hash

To encrypt a message, we need an algorithm and a private key. Here we’ll use the RSA algorithm. The DSA algorithm is another option.

Let’s create a Cipher instance and initialize it for encryption. Then we’ll call the doFinal() method to encrypt the previously hashed message:

Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] encryptedMessageHash = cipher.doFinal(hashToEncrypt);

At this point, the message, the digital signature, the public key, and the algorithm are all sent, and the receiver can use these pieces of information to verify the integrity of the message.

5.3. Verifying Signature

When we receive a message, we must verify its signature. To do so, we decrypt the received encrypted hash and compare it with a hash we make of the received message.

For decryption, we create a Cipher instance. Then we call the doFinal method:

Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] decryptedMessageHash = cipher.doFinal(encryptedMessageHash);

Next, we generate a new message hash from the received message:

byte[] messageBytes = Files.readAllBytes(Paths.get("message.txt"));

MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] newMessageHash = md.digest(messageBytes);

Then, we compute the encoded hash value:

DigestAlgorithmIdentifierFinder hashAlgorithmFinder = new DefaultDigestAlgorithmIdentifierFinder();
AlgorithmIdentifier hashingAlgorithmIdentifier = hashAlgorithmFinder.find("SHA-256");
DigestInfo digestInfo = new DigestInfo(hashingAlgorithmIdentifier, newMessageHash);
byte[] hashToEncrypt = digestInfo.getEncoded();

And finally, we check if the newly generated message hash matches the decrypted one:

boolean isCorrect = Arrays.equals(decryptedMessageHash, hashToEncrypt);

In this example, we’ve used the text file message.txt to simulate a message we want to send or the location of the body of a message we’ve received. Normally, we’d expect to receive our message alongside the signature.

6. Digital Signature Using the Signature Class

So far, we’ve used the low-level APIs to build our own digital signature verification process. This helps us understand how it works and allows us to customize it.

However, JCA already offers a dedicated API in the form of the Signature class.

6.1. Signing a Message

To start the process of signing, we first create an instance of the Signature class. To do that, we need a signing algorithm. We then initialize the Signature with our private key:

Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);

The signing algorithm we chose, SHA256withRSA in this example, is a combination of a hashing algorithm and an encryption algorithm. Other alternatives include SHA1withRSA, SHA1withDSA, and MD5withRSA, among others.

Next, we proceed to sign the byte array of the message:

byte[] messageBytes = Files.readAllBytes(Paths.get("message.txt"));

signature.update(messageBytes);
byte[] digitalSignature = signature.sign();

6.2. Verifying the Signature

To verify the received signature, we again create a Signature instance:

Signature signature = Signature.getInstance("SHA256withRSA");

Next, we initialize the Signature object for verification by calling the initVerify method, which takes a public key:

signature.initVerify(publicKey);

Then, we need to add the received message bytes to the signature object by invoking the update method:

byte[] messageBytes = Files.readAllBytes(Paths.get("message.txt"));

signature.update(messageBytes);

And finally, we can check the signature by calling the verify method:

boolean isCorrect = signature.verify(digitalSignature);

7. Conclusion

In this article, we first looked at how digital signature works and how to establish trust for a digital certificate. Then we implemented a digital signature using the MessageDigest, Cipher, and Signature classes from the Java Cryptography Architecture.

We saw in detail how to sign data using the private key and how to verify the signature using a public key.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

Partner – Microsoft – NPI EA (cat = Baeldung)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, visit the documentation page.

You can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Microsoft – NPI EA (cat = Spring Boot)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, visit the documentation page.

You can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Orkes – NPI EA (cat = Spring)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag = Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Partner – MongoDB – NPI EA (tag=MongoDB)
announcement - icon

Traditional keyword-based search methods rely on exact word matches, often leading to irrelevant results depending on the user's phrasing.

By comparison, using a vector store allows us to represent the data as vector embeddings, based on meaningful relationships. We can then compare the meaning of the user’s query to the stored content, and retrieve more relevant, context-aware results.

Explore how to build an intelligent chatbot using MongoDB Atlas, Langchain4j and Spring Boot:

>> Building an AI Chatbot in Java With Langchain4j and MongoDB Atlas

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Course – LSS – NPI (cat=Security/Spring Security)
announcement - icon

I just announced the new Learn Spring Security course, including the full material focused on the new OAuth2 stack in Spring Security:

>> CHECK OUT THE COURSE

eBook Jackson – NPI EA – 3 (cat = Jackson)