• 22-Jan-2023
Lightrun Team
Author Lightrun Team
Share
This article is about fixing java.io.IOException PKIX path building failed sun.security.provider.certpath.SunCertPathBuilderException unable to find valid certification path to requested target in Pengrad Java Telegram Bot API

java.io.IOException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target in Pengrad Java Telegram Bot API

Lightrun Team
Lightrun Team
22-Jan-2023

Explanation of the problem

The issue being described is the same as the one found in this Github link on 25 April 2019 (https://github.com/pengrad/java-telegram-bot-api/issues/160#issue-437322657). However, the difference is that the server has been changed and a new installation has been made. The solution for this issue, as previously understood, was to implement a custom OkHttpClient. The following is the implementation of the custom class:

public class BuilderCustomTelegram {

private final String token;

public BuilderCustomTelegram(String token) { this.token = token; }

public TelegramBot getTelegramBot() { X509TrustManager trustManager; SSLSocketFactory sslSocketFactory;

try {
    trustManager = trustManagerForCertificates(trustedCertificatesInputStream());
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, new TrustManager[]{trustManager}, null);
    sslSocketFactory = sslContext.getSocketFactory();
} catch (GeneralSecurityException e) {
    throw new RuntimeException(e);
}

OkHttpClient client = new OkHttpClient.Builder()
        .sslSocketFactory(sslSocketFactory, trustManager)
        .build();

TelegramBot bot = new TelegramBot.Builder(token)
        .okHttpClient(client)
        .build();

return bot;
}

private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException { try { KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); InputStream in = null; // By convention, ‘null’ creates an empty key store. keyStore.load(in, password); return keyStore; } catch (IOException e) { throw new AssertionError(e); } }

private X509TrustManager trustManagerForCertificates(InputStream in) throws GeneralSecurityException { CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(in); if (certificates.isEmpty()) { throw new IllegalArgumentException("expected non-empty set of trusted certificates"); }

// Put the certificates a key store.
char[] password = "password".toCharArray(); // Any password will work.
KeyStore keyStore = newEmptyKeyStore(password);
int index = 0;
for (Certificate certificate : certificates) {
    String certificateAlias = Integer.toString(index++);
    keyStore.setCertificateEntry(certificateAlias, certificate);
}

// Use it to build an X509 trust manager.
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, password);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);

TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
    throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
}
return (X509TrustManager) trustManagers[0];
}

private InputStream trustedCertificatesInputStream() {
    // PEM files for root certificates of Comodo and Entrust. These two CAs are sufficient to view
    // https://publicobject.com (Comodo) and https://squareup.com (Entrust). But they aren’t
    // sufficient to connect to most HTTPS sites including https://godaddy.com and https://visa.com.
    // Typically developers will need to get a PEM file from their organization’s TLS administrator.
    String comodoRsaCertificationAuthority = ""
            + "-----BEGIN CERTIFICATE-----\n"
            + "MIIEiDCCA3CgAwIBAgIQIZasKdlbfwsnz6AiPh7PVDANBgkqhkiG9w0BAQsFADBI\n"
            + "MRswGQYDVQQDExJFU0VUIFNTTCBGaWx0ZXIgQ0ExHDAaBgNVBAoTE0...
    return new ByteArrayInputStream(comodoRsaCertificationAuthority.getBytes());
}

Troubleshooting with the Lightrun Developer Observability Platform

Getting a sense of what’s actually happening inside a live application is a frustrating experience, one that relies mostly on querying and observing whatever logs were written during development.
Lightrun is a Developer Observability Platform, allowing developers to add telemetry to live applications in real-time, on-demand, and right from the IDE.

  • Instantly add logs to, set metrics in, and take snapshots of live applications
  • Insights delivered straight to your IDE or CLI
  • Works where you do: dev, QA, staging, CI/CD, and production

Start for free today

Problem solution for java.io.IOException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target in Pengrad Java Telegram Bot API

The issue of broken CA certificates authority when using Oracle JDK11 distribution under CentOS is related to the way the JDK distribution deals with SSL/TLS certificates. When connecting to a server over HTTPS, the client (in this case, the Java application) needs to verify the server’s identity by checking its SSL/TLS certificate against a trusted certificate authority (CA) store. If the certificate cannot be verified, the connection will be terminated.

In the case of Oracle JDK11, the CA store is located in the file cacerts in the Java installation directory. This file contains a list of root certificates from various trusted CAs. However, if the server’s certificate is not signed by one of the CAs in this list, the connection will be terminated, even if the certificate is valid. This is why the issue of broken CA certificates authority is occurring.

One solution to this issue is to use the OpenJDK-devel CentOS JDK package instead of Oracle JDK11. OpenJDK uses a different CA store that is updated more frequently and may include the server’s CA, thus resolving the issue.

sudo yum install java-11-openjdk-devel

Another solution is to use a custom OkHttpClient, as previously described in the initial problem description. This involves creating a custom X509TrustManager and SSLSocketFactory that trusts the server’s certificate, regardless of whether it is in the default CA store. This is less secure than using a trusted CA, as it allows any certificate to be trusted. Therefore, it is important to be very careful when implementing this solution and ensure that the certificate you’re trusting is from a trusted source.

public TelegramBot getTelegramBot() { 
  X509TrustManager trustManager; 
  SSLSocketFactory sslSocketFactory;
  trustManager = trustManagerForCertificates(trustedCertificatesInputStream());
  SSLContext sslContext = SSLContext.getInstance("TLS");
  sslContext.init(null, new TrustManager[]{trustManager}, null);
  sslSocketFactory = sslContext.getSocketFactory();
  OkHttpClient client = new OkHttpClient.Builder()
        .sslSocketFactory(sslSocketFactory, trustManager)
        .build();
  TelegramBot bot = new TelegramBot.Builder(token)
        .okHttpClient(client)
        .build();
  return bot;
}

It is important to keep in mind that these workarounds may not be the best long-term solution for the issue and should be used with caution, especially in production environments. It is recommended to contact the certificate authority to ensure that the certificate being used is up-to-date and secure. It’s also important to consider the security implications of using a custom OkHttpClient and ensure that it is properly configured and maintained.

Other popular problems with Java Telegram Bot API

Problem: SSL/TLS certificate verification failed

One of the most common problems when using the Java Telegram Bot API is encountering an SSL/TLS certificate verification error when connecting to the Telegram server. This occurs when the client (Java application) is unable to verify the server’s SSL/TLS certificate against the trusted certificate authority (CA) store. This can happen if the server’s certificate is not signed by a trusted CA or if the CA store on the client is out-of-date.

Solution:

One solution to this problem is to use a custom X509TrustManager and SSLSocketFactory that trusts the server’s certificate, regardless of whether it is in the default CA store. This involves creating a class that implements the X509TrustManager interface and overriding the checkServerTrusted method to always return true. Then, create an instance of SSLSocketFactory using the custom trust manager.

public class TrustAllX509TrustManager implements X509TrustManager {
    @Override
    public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
    }
    @Override
    public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
    }
    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
    }
}
TrustAllX509TrustManager trustAllX509TrustManager = new TrustAllX509TrustManager();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{trustAllX509TrustManager}, new SecureRandom());
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

OkHttpClient client = new OkHttpClient.Builder()
            .sslSocketFactory(sslSocketFactory, trustAllX509TrustManager)
            .build();
TelegramBot bot = new TelegramBot.Builder(token)
            .okHttpClient(client)
            .build();

Please note that this solution is less secure than using a trusted CA, as it allows any certificate to be trusted. Therefore, it is important to be very careful when implementing this solution and ensure that the certificate you’re trusting is from a trusted source.

Problem: Bot token invalid

Another common problem when using the Java Telegram Bot API is encountering an error that the bot token is invalid. This can happen if the token is incorrect, has expired, or the bot has been blocked by Telegram.

Solution:

To resolve this problem, first check that the token being used is correct and has not expired. If the token is correct and not expired, check the bot’s account on Telegram to see if it has been blocked or if there are any issues with the account. If the bot has been blocked, contact Telegram support to request unblocking. Also, make sure that the bot has been created on telegram and the token you have is associated with the bot.

Problem: Too many requests

A third common problem when using the Java Telegram Bot API is encountering an error that too many requests have been made to the Telegram server. This can happen if the bot is sending too many messages or requests in a short period of time, exceeding Telegram’s API rate limits.

Solution:

To resolve this problem, implement rate limiting in the bot’s code. This can be done by adding a delay between requests or by keeping track of the number of requests made in a certain time frame and limiting the number of requests to stay within Telegram’s API rate limits.

long lastRequestTime = 0;
long delayBetweenRequests = 1000; // 1 second

// ...

long currentTime = System.currentTimeMillis();
if (currentTime - lastRequestTime < delayBetweenRequests) {
    Thread.sleep(delayBetweenRequests - (currentTime - lastRequestTime));
}
lastRequestTime = currentTime;

bot.execute(new SendMessage(chatId, "message"));

Another solution is to use a library that already implements rate limiting, such as telegram-bot-ratelimiter. This is an easy and efficient way to handle rate limiting, as it takes care of the implementation details for you.

TelegramBot bot = TelegramBotAdapter.build(new TelegramBotRateLimiter(token));

It is important to note that Telegram’s API rate limits may change over time, so it is a good practice to periodically check the limits and adjust the rate limiting accordingly.

A brief introduction to Java Telegram Bot API

The Java Telegram Bot API is a Java library that allows developers to interact with the Telegram Bot API, a service provided by Telegram for creating and managing bots. The API provides a way for Java applications to send and receive messages, as well as perform other actions such as sending files, making payments, and more. The API is based on the Telegram Bot API, which is a HTTP-based API that uses JSON for data serialization.

The Java Telegram Bot API provides a convenient and easy-to-use Java wrapper around the Telegram Bot API. It allows developers to send and receive messages, as well as perform other actions, with just a few lines of code. The API also includes a number of useful features such as support for file uploads, support for inline keyboards, and support for handling updates. Additionally, it is built on top of OkHttp3, a popular HTTP library for Java, which allows for easy customization of the underlying HTTP client and connection settings.

Most popular use cases for Java Telegram Bot API

  1. Sending and receiving messages: The Java Telegram Bot API can be used to send and receive messages between a Telegram bot and its users. The API provides methods for sending text, media, and other types of messages, as well as receiving incoming messages and updates. For example, the following code block shows how to send a message to a Telegram user using the execute method of the TelegramBot class:
TelegramBot bot = TelegramBotAdapter.build(token);
SendMessage sendMessage = new SendMessage()
            .setChatId(chatId)
            .setText("Hello, this is a message from my bot!");
bot.execute(sendMessage);

2. Handling inline keyboards: The Java Telegram Bot API can be used to handle inline keyboards, which are a type of keyboard that can be used to provide options to the user within the Telegram chat interface. The API provides methods for creating, updating, and handling inline keyboards and inline queries. For example, the following code block shows how to create an inline keyboard and send it as part of a message:

InlineKeyboardButton button1 = new InlineKeyboardButton("Option 1").callbackData("option1");
InlineKeyboardButton button2 = new InlineKeyboardButton("Option 2").callbackData("option2");
InlineKeyboardMarkup inlineKeyboard = new InlineKeyboardMarkup(
    new InlineKeyboardButton[]{button1},
    new InlineKeyboardButton[]{button2});
SendMessage sendMessage = new SendMessage()
            .setChatId(chatId)
            .setText("Please select an option:")
            .setReplyMarkup(inlineKeyboard);
bot.execute(sendMessage);

3. Handling payments: The Java Telegram Bot API can be used to handle payments made through Telegram bots. The API provides methods for creating, managing, and processing payments, as well as handling shipping and pre-checkout queries. This feature requires a bot to be set as a Telegram payment bot and obtain the necessary credentials from Telegram.

Share

It’s Really not that Complicated.

You can actually understand what’s going on inside your live applications. It’s a registration form away.

Get Lightrun

Lets Talk!

Looking for more information about Lightrun and debugging?
We’d love to hear from you!
Drop us a line and we’ll get back to you shortly.

By submitting this form, I agree to Lightrun’s Privacy Policy and Terms of Use.