Skip to content

Set Up a Corporate SSL Certificate

This guide shows how to configure SSL certificates in environments with an SSL Inspection (MITM) proxy, covering Java applications (Wizard, BotRunner, BotStudio) and Python (requests).

Context

Corporate environments with SSL Inspection (MITM proxy) intercept HTTPS connections and replace the server's original certificate with one issued by an internal company CA. This causes Java and Python applications to reject the connection, since they don't recognize that CA.

This behavior usually results in SSL errors when using tools like the BotCity Runner, typically something like PKIX path building failed or unable to find valid certification path to requested target.

One possible solution is to register the internal CA's root certificate in each technology's respective truststore.

Prerequisites

  • The company's internal CA root certificate (.crt, .cer, or .pem file), provided by the infrastructure/security team.

    If you need to extract it, you can use the browser: access the target server from the corporate machine, click the lock icon, export the certificate chain, and identify the root certificate (the one at the top of the chain, which is self-signed).

  • The Java (JDK/JRE) used to run the applications.

    Here you need to know which Java is being used to run the tools (Runner): the Java bundled with the SDK installation, or the Java from the system installation.

  • Python with the certifi package installed (pip install --upgrade certifi).

Configuration for Java

1. Locate the JVM's cacerts

cacerts is Java's default truststore. Its location depends on the version and the Java being used:

# cacerts on Java 9+
# $JAVA_HOME\lib\security\cacerts

# cacerts on Java 8
# $JAVA_HOME\jre\lib\security\cacerts

# Command that can help identify the installed Java
# java -version

If the Java used to run the tools is the one bundled with the BotCity Studio SDK, the path to cacerts should look like:

"<BOTCITY_SDK_PATH>\win32\java\lib\security\cacerts"

Confirm the file exists:

Test-Path "<PATH_JAVA>\lib\security\cacerts"

2. List the existing certificates (optional)

keytool is a utility that already ships with the JDK, located at <PATH_JAVA>\bin\keytool.exe. This is the tool used for all certificate operations.

# To make the calls easier, open a terminal in the "<PATH_JAVA>\bin" directory
.\keytool.exe -list -keystore "<PATH_JAVA>\lib\security\cacerts" -storepass changeit

This command should return the list of existing default certificates.

3. Import the certificate

With the company's certificate file in hand, use keytool to import it into the cacerts file. You can also set an alias to make it easier to identify the imported certificate later.

.\keytool.exe -importcert -alias company-internal-ca -file "C:\certs\company-root-ca.crt" -keystore "<PATH_JAVA>\lib\security\cacerts" -storepass changeit -noprompt

Note

The default cacerts password is changeit. Use a descriptive alias to make future identification easier. If there are intermediate certificates, import each one with a different alias.

4. Verify the import

.\keytool.exe -list -keystore "<PATH_JAVA>\lib\security\cacerts" -storepass changeit -alias company-internal-ca

The output should show the alias, the creation date, and the certificate type.

5. Restart the Java application

The JVM loads the truststore at startup, so the process needs to be restarted for the new certificate to be recognized.

Configuration for Python (requests / certifi)

The requests library doesn't use the operating system's truststore or Java's cacerts. It relies on the certifi package, which ships its own bundle of trusted CAs (a cacert.pem file).

1. Locate the certifi bundle

python -c "import certifi; print(certifi.where())"

Note the path returned (for example, C:\Users\user\AppData\...\certifi\cacert.pem).

2. Create the custom bundle

Copy the original bundle and add the internal CA's certificate to the end:

# Creates the destination directory (if it doesn't exist)
New-Item -ItemType Directory -Path "C:\Users\<user>\Documents\Certs" -Force

# Copies the original certifi bundle
Copy-Item -Path (python -c "import certifi; print(certifi.where())") -Destination "C:\Users\<user>\Documents\Certs\company-ca-bundle.pem" -Force

# Adds the internal CA's root certificate to the end of the bundle
Add-Content -Path "C:\Users\<user>\Documents\Certs\company-ca-bundle.pem" -Value ""
Get-Content -Path "C:\certs\company-root-ca.crt" | Add-Content -Path "C:\Users\<user>\Documents\Certs\company-ca-bundle.pem"

3. Set the environment variable

Option A: for the current session only (quick test)

$env:REQUESTS_CA_BUNDLE = "C:\Users\<user>\Documents\Certs\company-ca-bundle.pem"

Option B: persistent for the current user

[System.Environment]::SetEnvironmentVariable("REQUESTS_CA_BUNDLE", "C:\Users\<user>\Documents\Certs\company-ca-bundle.pem", "User")

Option C: persistent for the whole machine (requires PowerShell as Admin)

[System.Environment]::SetEnvironmentVariable("REQUESTS_CA_BUNDLE", "C:\Users\<user>\Documents\Certs\company-ca-bundle.pem", "Machine")

Tip

If other scripts or libraries besides requests also need to trust the internal CA (for example, httpx, urllib3), also set the SSL_CERT_FILE variable to the same path, following the same procedure above.

4. Verify the configuration

Open a new terminal (so the persistent variable gets loaded) and test:

# Confirms the variable is set
echo $env:REQUESTS_CA_BUNDLE

# Tests the connection
python -c "import requests; r = requests.get('https://developers.botcity.dev/api/v2/maestro/version'); print(r.status_code)"

5. Virtual environments

The REQUESTS_CA_BUNDLE variable overrides requests' default behavior of consulting the certifi package installed in the active venv. With the variable set, requests ignores certifi and uses the pointed-to file directly, so it works for any venv with no extra per-environment configuration needed.

Maintenance

Updating certifi

When the certifi package is updated via pip, the custom bundle is not updated automatically (it's a static copy). To rebuild it, just repeat steps 2 and 3 of the previous section.

Removing the Java certificate (if needed)

keytool -delete -keystore "<JAVA_PATH>\lib\security\cacerts" -storepass changeit -alias company-internal-ca

Removing the Python environment variables (if needed)

# Removes it from the user scope
[System.Environment]::SetEnvironmentVariable("REQUESTS_CA_BUNDLE", $null, "User")

# Or from the machine scope (requires Admin)
[System.Environment]::SetEnvironmentVariable("REQUESTS_CA_BUNDLE", $null, "Machine")

Troubleshooting

Java: PKIX path building failed / unable to find valid certification path

  • Check that JAVA_HOME points to the correct JVM the application uses.
  • Confirm the imported certificate is the root one (not an intermediate).
  • Check whether the application uses a custom truststore via -Djavax.net.ssl.trustStore. If so, import the certificate into that truststore instead of the global cacerts.

Python: SSLCertVerifyError / CERTIFICATE_VERIFY_FAILED

  • Confirm the REQUESTS_CA_BUNDLE variable is set in the terminal where the script runs (echo $env:REQUESTS_CA_BUNDLE).
  • Check that the .pem file contains both certifi's CAs and the internal CA's certificate.
  • If the error persists, test with an explicit verify argument to isolate the problem:
import requests
r = requests.get("https://your-server.com", verify=r"C:\certs\company-ca-bundle.pem")
print(r.status_code)

Summary

Step Java Python
Truststore cacerts (JKS) certifi (PEM)
Tool keytool Copy + PEM concatenation
Import command keytool -importcert ... Copy-Item + Add-Content
Environment variable javax.net.ssl.trustStore (if using a custom truststore) REQUESTS_CA_BUNDLE or SSL_CERT_FILE
Restart required Yes (JVM) New terminal

Note

Replace the example paths (such as C:\certs\...) and the certificate alias with the real values from the customer's environment.