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 :)
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 ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With