Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R - adding P12 certificate to get call

Tags:

r

rcurl

httr

I have a successful call in Postman where I access an API with a .p12 certificate. In Postman, the certificate is stored in the global settings.

Now I am trying to translate this Postman-call to R-code, where I spent hours playing around using the httr and rCURL packages. Unfortunately, without results where my call results in a 403 (no valid certificate).

My steps:

First, I defined my headers for my call and the url

headers = c(
  'Content-Type' = 'application/pdf',
  'Authorization' = 'Basic ...'
) 

#Set URL
url = "https://myurlwithcertificate.eu"

Next I linked to my .p12 certificate

#Cert info
pkcs12_name <- "certi.p12"
pkcs12_pwd <- "certificatePassword"

I tried to perform the call:

res <- httr::GET(url = url, 
             add_headers(headers),
             config = config(sslcert=pkcs12_name,
                             sslkey=pkcs12_pwd))

Leading into: could not load PEM client certificate

Next I also tried to first use the read_p12 function to open the certificate.

#Open certificate
cert <- read_p12(pkcs12_name,pkcs12_pwd)

I managed to successfully open the .p12 certificate. However, I didn't manage to add the certificate and the key in my call to the API.

res <- httr::GET(url = url, 
             add_headers(headers),
             config = config(sslcert=cert$cert,
                             sslkey=cert$key))

As a test, I have this call successfully created in Python. However, I also want to achieve the result in R.

Python script:

import requests
from requests_pkcs12 import get

url = "https://myurlwithcertificate.eu"

payload={}
headers = {
  'Content-Type': 'application/pdf',
  'Authorization': 'Basic ...'
}

response = get(url, headers = headers, data=payload, pkcs12_filename='certi.p12', pkcs12_password='certificatePassword')



response = requests.request("GET", url, headers=headers, data=payload)

print(response.text)

I have the feeling that somehow in my R-script I didn't successfully managed to add the certificate in the call, url and headers are fine. Please if you have any ideas how I can manage to add the .p12 certificate in my call, let me know :)

like image 978
Laurens Avatar asked Oct 15 '25 15:10

Laurens


1 Answers

The problem is the password, and more specifically that sslkey (CURLOPT_SSLKEY) is not the place to provide that. Rather, this option sets the private key file which isn't applicable in your case.

For this to work you'll need a recent version of curl (>=8.11?) and set sslcerttype (CURLOPT_SSLCERTTYPE) to P12.

curl will accept a password for the certificate via --cert file:password (see also e.g. here, here and its man page). Unfortunately, this is the part that I can't get to work in httr: if you give it config(sslcert='file:"pass"') with various quoting approaches that invariably results in

Error in curl::curl_fetch_memory(url, handle = handle) :
  Problem with the local SSL certificate [...]:
schannel: Failed to get certificate location or file for file:"pass"

If you can somehow fall back to a non-password-protected certificate then the following should work:

## Create example certificate
system2("openssl", "req -newkey 2048 -keyout cert.key -nodes -x509 -out cert.pem -batch")

openssl::write_p12(
  key="cert.key",
  cert="cert.pem",
  path="cert.p12"
)

## Send it along
httr::GET(
  "https://example.com",
  httr::config(sslcert="cert.p12", sslcerttype="P12"),
  httr::verbose(info=TRUE)
)

## Showing only a small selection of output
#> *  schannel: disabled automatic use of client certificate
#> *  Connected to example.com (23.215.0.136) port 443
#> *  Request completely sent off
#> *  schannel: SSL/TLS connection renegotiated
#> <- HTTP/2 200 
#> Response [https://example.com/]
#>   Status: 200

There may indeed be other approaches using openssl::read_p12 or similar. Since that handles the password perhaps it's even an option to temporarily create a non-passworded local copy of the existing certificate?

Otherwise I keep getting stuck with curl expecting its certificate/key arguments to point towards files and, as above, that not playing along with a password in R's curl (so changes will likely have to happen there). Setting these options as blobs (pointers, e.g. CURLOPT_SSLCERT_BLOB) might be an alternative, but that's again something I haven't managed to get working.


I missed your mention of RCurl, and it turns out that supports an sslcertpasswd option which httr/curl don't. This might be what you need?

## Passworded copy of example certificate
openssl::write_p12(
  key="cert.key",
  cert="cert.pem",
  path="cert.pwd.p12",
  password="pass123"
)

## Send it
RCurl::getURL(
  "https://example.com",
  sslcert="cert.pwd.p12",
  sslcertpasswd="pass123",
  sslcerttype="P12",
  verbose=TRUE
)

#>    Trying 23.215.0.138:443...
#> * SSL connection using TLSv1.3
#> * SSL certificate verify ok.
#> * Connected to example.com (23.215.0.138) port 443
#> * Request completely sent off
#> < HTTP/1.1 200 OK
#> "<!doctype html>\n<html>\n<head>\n ...
like image 74
PBulls Avatar answered Oct 17 '25 05:10

PBulls