Self signed certificate in chain on v11.7.20

On our 11.7 server we try to connect to an api that runs on another server, but we encounter this error:

Secure Socket Layer (SSL) failure. error code -54: self signed certificate
in certificate chain: for aff19c05.0 in c:\dlc11.7\certs (9318)


To resolve this, we opened a browser, navigated to the URI in question and exported the certificates (Lets Encrypt). Note: in chrome/edge the URI can be accessed without any issues. We then imported the root and the intermediate certificate to the local certificate store in c:\dlc11.7\certs. However, the problem remains.

We consulted our networking guy and he claims there is an error in OpenEdge that results in the wrong hash being generated, quote:
So OE’s tooling registers the certificate under alias 4042bcee (the modern OpenSSL-style hash, SHA-1). But the runtime SSL-C resolver looks it up using the old MD5-style hash → aff19c05.0. Those two parts of Progress use a different hash algorithm. This is a real product bug in 11.7, not a configuration error on your side.

I have serious doubts regarding his conclusion, I cannot find any information on such a bug. We are on 11.7.20 witch is the latest v11
 
I'm assuming that you are using the OE HTTP Client to make the connection to the remote API.

Can you confirm the root certificate is correctly stored in the c:\dlc11.7\certs after the import.
Try this command
Code:
certutil -list | findstr /C:"Certificate store entry"
Or this:
Code:
certutil -list | findstr /C:"aff19c05"

I'm not convinced that aff19c05 belongs to Let's Encrypt. It might be a self-signed certificate.

I've just imported the Let's Encrypt Root Certificates:
Code:
proenv>certutil -import isrgrootx1.pem
Importing trusted certificate to alias name: 4042bcee

proenv>certutil -import isrg-root-x2.pem
Importing trusted certificate to alias name: 0b9bc432

proenv>certutil -import root-ye.pem
Importing trusted certificate to alias name: 8ed85ee6

proenv>certutil -import root-yr.pem
Importing trusted certificate to alias name: 05e55783

Imported Let encrypt root certificates:
Code:
Certificate store entry: 0b9bc432
Certificate:
subject=C = US, O = Internet Security Research Group, CN = ISRG Root X2
issuer=C = US, O = Internet Security Research Group, CN = ISRG Root X2
notBefore=Sep  4 00:00:00 2020 GMT
notAfter=Sep 17 16:00:00 2040 GMT

Certificate store entry: 4042bcee
Certificate:
subject=C = US, O = Internet Security Research Group, CN = ISRG Root X1
issuer=C = US, O = Internet Security Research Group, CN = ISRG Root X1
notBefore=Jun  4 11:04:38 2015 GMT
notAfter=Jun  4 11:04:38 2035 GMT

Certificate store entry: 8ed85ee6
Certificate:
subject=C = US, O = ISRG, CN = Root YE
issuer=C = US, O = ISRG, CN = Root YE
notBefore=Sep  3 00:00:00 2025 GMT
notAfter=Sep  2 23:59:59 2045 GMT

Certificate store entry: 05e55783
Certificate:
subject=C = US, O = ISRG, CN = Root YR
issuer=C = US, O = ISRG, CN = Root YR
notBefore=Sep  3 00:00:00 2025 GMT
notAfter=Sep  2 23:59:59 2045 GMT

sslc command to get the hash of a certificate file. Neither hashing options equates to "aff19c05"
Code:
proenv>sslc x509 -hash -noout -in isrgrootx1.pem
4042bcee

proenv>sslc x509 -subject_hash -noout -in isrgrootx1.pem
4042bcee

proenv>sslc x509 -subject_hash_old -noout -in isrgrootx1.pem
6187b673

Using the -hash and -subject_hash_old parameter uses the MD5 hash algorithm.
-subject_hash uses the SHA-1 hash algorithm,
 
Last edited:
In the meantime I got an answer from Sil Boydens on discord to use the native .net libraries for http connections since that one uses the windows cert store instead of he OE certs folder. This works as a charm, see below for some code

Code:
  METHOD PUBLIC DataDumper sendData():
    /* Stuur via POST
    */   
    DEFINE VARIABLE oHttpRequest  AS CLASS    System.Net.Http.HttpRequestMessage  NO-UNDO.
    DEFINE VARIABLE oHttpResponse AS CLASS    System.Net.Http.HttpResponseMessage NO-UNDO.
    DEFINE VARIABLE oHttpClient   AS CLASS    System.Net.Http.HttpClient          NO-UNDO.
    DEFINE VARIABLE cResponse     AS LONGCHAR NO-UNDO.
    DEFINE VARIABLE cProcesData   AS LONGCHAR NO-UNDO.
    DEFINE VARIABLE i             AS INTEGER  NO-UNDO.

    IF NOT THIS-OBJECT:SendData THEN RETURN THIS-OBJECT.
    
    // Init request
    oHttpClient = NEW System.Net.Http.HttpClient().
    oHttpRequest = NEW System.Net.Http.HttpRequestMessage(NEW System.Net.Http.HttpMethod("post"), SendUrl).
    IF SendApiKey <> "" THEN oHttpRequest:Headers:Add("x-api-key", SendApiKey).

    // JSON in body (not for GET requests)
    FIX-CODEPAGE(cProcesData) = "UTF-8".
    ProcesData:WRITE(cProcesData, FALSE).
    oHttpRequest:content = NEW System.Net.Http.StringContent(cProcesData, System.Text.Encoding:UTF8, "application/json").

    oHttpResponse = oHttpClient:SendAsync(oHttpRequest):RESULT.
    IF VALID-OBJECT(oHttpResponse) THEN
    DO:
      FIX-CODEPAGE(cResponse) = "UTF-8".
      ASSIGN cResponse = oHttpResponse:Content:ReadAsStringAsync():Result WHEN VALID-OBJECT(oHttpResponse:Content).
      
      IF NOT oHttpResponse:IsSuccessStatusCode
        THEN ASSIGN SendError  = TRUE
                    SendResult = SUBSTITUTE('Error:&1 (&2)', oHttpResponse:StatusCode:VALUE__, oHttpResponse:StatusCode:ToString() ).
        ELSE ASSIGN SendResult = SUBSTRING(cResponse,1,2000).
    END.

    CATCH e AS Progress.Lang.Error:     
      SendError = TRUE.
      DO i = 1 TO e:NumMessages:
        SendResult = TRIM(SUBSTITUTE("&1~n&2~n~n&3", SendResult, e:GetMessage(i)), STRING(SUBSTRING(cResponse,1,2000))).
      END.
    END CATCH.

    FINALLY:
      IF VALID-OBJECT(oHttpClient)   THEN DELETE OBJECT oHttpClient   NO-ERROR.
      IF VALID-OBJECT(oHttpResponse) THEN DELETE OBJECT oHttpResponse NO-ERROR.
      IF VALID-OBJECT(oHttpRequest)  THEN DELETE OBJECT oHttpRequest  NO-ERROR.
    END FINALLY.
  END METHOD. // sendData
 
Back
Top