Page 1 of 1

How to download SSL certificate from a server

Posted: Sun Nov 04, 2018 10:33 am
by firace
I'm not sure how to download (to file or memory) the SSL certificate from an HTTPS server in PB.
Any ideas?

In Python for instance, that's very simple:

Code: Select all

import ssl
ssl.get_server_certificate(('server.test.com', 443))

Re: How to download SSL certificate from a server

Posted: Sun Nov 04, 2018 11:09 am
by firace
Apparently this could be a way for Windows: https://blogs.msdn.microsoft.com/jpsand ... in_context

But it looks complicated - to me, at least.

Re: How to download SSL certificate from a server

Posted: Sun Nov 04, 2018 11:23 am
by infratec
In PB it's nearly the same ...

You have to write a wrapper for OpenSSL, then you can use the same function :wink:

Or you can use libcurl, but I don't know if you can get all informations:

https://curl.haxx.se/libcurl/c/CURLINFO_CERTINFO.html

You can nearly direct convert this example to PB with our libcurl.pbi

Re: How to download SSL certificate from a server

Posted: Sun Nov 04, 2018 12:22 pm
by infratec
Ups..

I had to add some sructures and definitions to libcurl.pbi
But on windows it results in 0 certs maybe that openssl is not used by the PB libcurl.
I have to check the code on linux.

Re: How to download SSL certificate from a server

Posted: Thu Nov 08, 2018 7:56 pm
by firace
Yes it does seem that the embedded PB libcurl does not support that feature, unfortunately.

So if someone knows how to translate the following MSDN snippet to PB, it would be really great:

Code: Select all

PCCERT_CHAIN_CONTEXT CertCtx=NULL;
DWORD cbCertSize = sizeof(&CertCtx);


//GetCertificate information
if (InternetQueryOption(hReq,INTERNET_OPTION_SERVER_CERT_CHAIN_CONTEXT ,(LPVOID)&CertCtx,&cbCertSize))
{
//---------------------------------------------------------------
// Display some of the contents of the chain.
PCCERT_CHAIN_CONTEXT pChainContext=CertCtx;
printf("The size of the chain context is %d. \n",pChainContext->cbSize);
printf("%d simple chains found.\n",pChainContext->cChain);
printf("\nError status for the chain:\n");


CERT_SIMPLE_CHAIN *simpleCertificateChainWithinContext = NULL;
for (int i=0; i<pChainContext->cChain; i++)
{
    simpleCertificateChainWithinContext=pChainContext->rgpChain[i];
    // for each certificate chain in this context...
    for (int simpleCertChainIndex = 0; 
    simpleCertChainIndex < simpleCertificateChainWithinContext->cElement;
    simpleCertChainIndex++)
    {



        // get the CertContext from the array
        PCCERT_CONTEXT pCertContext = 
        simpleCertificateChainWithinContext->rgpElement[simpleCertChainIndex]->pCertContext;


        //-------------------------------------------------------------------
        // Find and print the name of the subject of the certificate 
        // just retrieved.
        TCHAR pszNameString[256]; 
        if(CertGetNameString( 
        pCertContext, 
        CERT_NAME_SIMPLE_DISPLAY_TYPE, 
        0,
        NULL, 
        pszNameString, 
        128))
        {
            wprintf(L"Certificate for %s has been retrieved.\n", pszNameString);
        }
        else
        {
            wprintf(L"CertGetName failed. \n");
        }


        // Get the issuer now...
        if(CertGetNameString( pCertContext, 
        CERT_NAME_SIMPLE_DISPLAY_TYPE, 
        CERT_NAME_ISSUER_FLAG,
        NULL, 
        pszNameString, 
        128))
        {
            wprintf(L"Certificate issuer is %s.\n\n", pszNameString);
        }
        else
        {
            wprintf(L"CertGetName failed. \n");
        }
    }
}


//important! Free the CertCtx
CertFreeCertificateChain(CertCtx);
}
else
{
  ErrorOut (GetLastError(), TEXT("HttpQueryInfo"));
}

Re: How to download SSL certificate from a server

Posted: Thu Nov 08, 2018 9:29 pm
by infratec
Hi,

maybe this works a bit (or not)

Code: Select all

#INTERNET_OPTION_SERVER_CERT_CHAIN_CONTEXT = 105

#CERT_NAME_SIMPLE_DISPLAY_TYPE = 4

#CERT_NAME_ISSUER_FLAG = $00000001

...
removed. See working code below.

Re: How to download SSL certificate from a server

Posted: Fri Nov 09, 2018 10:48 am
by firace
Thanks infratec! I'm impressed at how fast you could convert it, even if it doesn't work yet.

This is my simplified test code. Not sure where it goes wrong...

Code: Select all

#INTERNET_OPTION_SERVER_CERT_CHAIN_CONTEXT = 105

#CERT_NAME_SIMPLE_DISPLAY_TYPE = 4

#CERT_NAME_ISSUER_FLAG = $00000001

Structure CERT_CONTEXT
  dwCertEncodingType.l
  *pbCertEncoded.Byte
  cbCertEncoded.l
  *CertInfo.CERT_INFO
  HCERTSTORE.l
EndStructure


Structure CERT_TRUST_STATUS
  dwErrorStatus.l
  dwInfoStatus.l
EndStructure

Structure CERT_CHAIN_ELEMENT
  cbSize.l
  *CertContext.CERT_CONTEXT
  TrustStatus.CERT_TRUST_STATUS
  *RevocationInfo.CERT_REVOCATION_INFO
  *IssuanceUsage.CERT_ENHKEY_USAGE
  *ApplicationUsage.CERT_ENHKEY_USAGE
  *ExtendedErrorInfo
EndStructure

Structure CERT_SIMPLE_CHAIN
  cbSize.l
  TrustStatus.CERT_TRUST_STATUS
  cElement.l
  *rgpElement.CERT_CHAIN_ELEMENT[0]
  *TrustListInfo.CERT_TRUST_LIST_INFO
  fHasRevocationFreshnessTime.i
  dwRevocationFreshnessTime.l
EndStructure

Structure CERT_CHAIN_CONTEXT
  cbSize.l
  TrustStatus.CERT_TRUST_STATUS
  cChain.l
  *rgpChain.CERT_SIMPLE_CHAIN[0]
  cLowerQualityChainContext.l
  *rgpLowerQualityChainContext.CERT_CHAIN_CONTEXT
  fHasRevocationFreshnessTime.i
  dwRevocationFreshnessTime.l
  dwCreateFlags.l
  ChainId.GUID
EndStructure



Define cbCertSize.l = SizeOf(CERT_CHAIN_CONTEXT);
Define *CertCtx.CERT_CHAIN_CONTEXT

Define *ChainContext.CERT_CHAIN_CONTEXT
Define *simpleCertificateChainWithinContext.CERT_SIMPLE_CHAIN
Define *CertContext.CERT_CONTEXT

Define i.i, simpleCertChainIndex.i



file$ = "https://ipinfo.io/8.8.8.8/org"

#INTERNET_FLAG_RELOAD = $80000000 
#INTERNET_OPTION_SECURITY_FLAGS = 31
#SECURITY_FLAG_IGNORE_UNKNOWN_CA = $100
Bytes.l = 0 
Html.s  = Space(50000)
hInet.l = InternetOpen_("", 0, #Null, #Null, 0) 


  dwFlags.l = 0
  dwBuffLen = SizeOf(dwFlags)
  
  dwFlags = $2000|$1000|$200|$100  ; $800000| |$80
  InternetSetOption_(hInet, #INTERNET_OPTION_SECURITY_FLAGS, @dwFlags, SizeOf(dwFlags))
    
  hURL.l  = InternetOpenUrl_(hInet, file$, #Null, 0, #INTERNET_FLAG_RELOAD, 0) 
  
  InternetReadFile_(hURL, @Html, Len(Html), @Bytes) 
  

html = PeekS(@HTML, Len(HTML), #PB_Ascii)

Debug "HTML: " + html
; 

Debug hInet

*CertCtx = AllocateMemory(cbCertSize)

InternetQueryOption_(hInet, #INTERNET_OPTION_SERVER_CERT_CHAIN_CONTEXT , @*CertCtx, @cbCertSize)

Debug "The size of the chain context is " + Str(*CertCtx\cbSize)


Re: How to download SSL certificate from a server

Posted: Fri Nov 09, 2018 6:55 pm
by infratec
One step further:

Code: Select all

EnableExplicit

#INTERNET_OPTION_SERVER_CERT_CHAIN_CONTEXT = 105

#CERT_NAME_SIMPLE_DISPLAY_TYPE = 4

#CERT_NAME_ISSUER_FLAG = $00000001

...

removed. See working code below.

Re: How to download SSL certificate from a server

Posted: Sat Nov 10, 2018 11:33 am
by infratec
I did it:

Code: Select all

EnableExplicit

#INTERNET_FLAG_RELOAD = $80000000

#INTERNET_OPTION_SECURITY_FLAGS = 31
#INTERNET_OPTION_SERVER_CERT_CHAIN_CONTEXT = 105

#SECURITY_FLAG_IGNORE_UNKNOWN_CA = $100

#CERT_NAME_SIMPLE_DISPLAY_TYPE = 4

#CERT_NAME_ISSUER_FLAG = $00000001


Structure CERT_CONTEXT Align #PB_Structure_AlignC
  dwCertEncodingType.l
  *pbCertEncoded.Byte
  cbCertEncoded.l
  *CertInfo.CERT_INFO
  HCERTSTORE.l
EndStructure

Structure CERT_TRUST_STATUS Align #PB_Structure_AlignC
  dwErrorStatus.l
  dwInfoStatus.l
EndStructure

Structure CERT_CHAIN_ELEMENT Align #PB_Structure_AlignC
  cbSize.l
  *CertContext.CERT_CONTEXT
  TrustStatus.CERT_TRUST_STATUS
  *RevocationInfo.CERT_REVOCATION_INFO
  *IssuanceUsage.CERT_ENHKEY_USAGE
  *ApplicationUsage.CERT_ENHKEY_USAGE
  *ExtendedErrorInfo
EndStructure

Structure CERT_SIMPLE_CHAIN Align #PB_Structure_AlignC
  cbSize.l
  TrustStatus.CERT_TRUST_STATUS
  cElement.l
  *rgpElement.CERT_CHAIN_ELEMENT[0]
  *TrustListInfo.CERT_TRUST_LIST_INFO
  fHasRevocationFreshnessTime.l
  dwRevocationFreshnessTime.l
EndStructure

Structure CERT_CHAIN_CONTEXT Align #PB_Structure_AlignC
  cbSize.l
  TrustStatus.CERT_TRUST_STATUS
  cChain.l
  *rgpChain.CERT_SIMPLE_CHAIN[0]
  cLowerQualityChainContext.l
  *rgpLowerQualityChainContext.CERT_CHAIN_CONTEXT
  fHasRevocationFreshnessTime.l
  dwRevocationFreshnessTime.l
  dwCreateFlags.l
  ChainId.GUID
EndStructure




Define *CertCtx.CERT_CHAIN_CONTEXT
Define *ChainContext.CERT_CHAIN_CONTEXT
Define *simpleCertificateChainWithinContext.CERT_SIMPLE_CHAIN
Define *CertContext.CERT_CONTEXT
Define.i i, simpleCertChainIndex
Define.l Bytes, hInet, dwFlags, hConnect, hRequest, dwContext, cbCertSize
Define *SimpleChain.CERT_CHAIN_ELEMENT
Define *NameString




Prototype.l Proto_CertGetNameString(*CertContext.CERT_CONTEXT, dwType.l, dwFlags.l, *pvTypePara, *NameString, cchNameString.l)
Prototype Proto_CertFreeCertificateChain(*ChainContext.CERT_CHAIN_CONTEXT)

Global CertGetNameString.Proto_CertGetNameString
Global CertFreeCertificateChain.Proto_CertFreeCertificateChain

If OpenLibrary(0, "crypt32.dll")
  CertGetNameString = GetFunction(0, "CertGetNameStringW")
  CertFreeCertificateChain = GetFunction(0, "CertFreeCertificateChain")
Else
  End
EndIf




hInet = InternetOpen_("", 0, #Null, #Null, 0)
If hInet
  
  hConnect = InternetConnect_(hInet, "ipinfo.io", #INTERNET_DEFAULT_HTTPS_PORT, #Null, #Null, #INTERNET_SERVICE_HTTP, 0,  @dwContext)
  If hConnect
    
    hRequest = HttpOpenRequest_(hConnect, "GET", "/8.8.8.8/org", #Null, #Null, #Null, #INTERNET_FLAG_SECURE, @dwContext)
    If hRequest
      
      If HttpSendRequest_(hRequest, #Null, 0, #Null, 0)
        
        cbCertSize = SizeOf(CERT_CHAIN_CONTEXT)
        
        If InternetQueryOption_(hRequest, #INTERNET_OPTION_SERVER_CERT_CHAIN_CONTEXT , @*CertCtx, @cbCertSize)
          
          *ChainContext = *CertCtx
          
          Debug "The size of the chain context is " + Str(*ChainContext\cbSize)
          Debug Str(*ChainContext\cChain) + " simple chains found."
          Debug ""
          Debug "Status For the chain(s):"
          Debug ""
          
          For i = 0 To *ChainContext\cChain - 1
            
            Debug "Chain " + Str(i + 1)
            Debug ""
            
            *simpleCertificateChainWithinContext = PeekL(*ChainContext\rgpChain + i * 4)
            
            Debug "ChainElements: " + Str(*simpleCertificateChainWithinContext\cElement)
            
            ; For each certificate chain in this context...
            For simpleCertChainIndex = 0 To *simpleCertificateChainWithinContext\cElement - 1
              Debug ""
              Debug "ChainElement " + Str(simpleCertChainIndex + 1)
              
              ; get the CertContext from the Array
              *SimpleChain = PeekL(*simpleCertificateChainWithinContext\rgpElement + simpleCertChainIndex * 4)
              *CertContext = *SimpleChain\CertContext
              
              ;-------------------------------------------------------------------
              ; Find And print the name of the subject of the certificate
              ; just retrieved.
              *NameString = AllocateMemory(128)
              If *NameString
                If CertGetNameString(*CertContext, #CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, #Null, *NameString, 128)
                  Debug "Certificate for " + PeekS(*NameString) + " has been retrieved"
                Else
                  Debug "CertGetName failed."
                EndIf
                FreeMemory(*NameString)
              EndIf
              
              ; Get the issuer now...
              *NameString = AllocateMemory(128)
              If *NameString
                If CertGetNameString(*CertContext, #CERT_NAME_SIMPLE_DISPLAY_TYPE, #CERT_NAME_ISSUER_FLAG, #Null, *NameString, 128)
                  Debug "Certificate issuer is " + PeekS(*NameString)
                Else
                  Debug "CertGetName failed."
                EndIf
                FreeMemory(*NameString)
              EndIf
              
            Next simpleCertChainIndex
            
          Next i
          
          CertFreeCertificateChain(*CertCtx)
          
        Else
          Debug "InternetQueryOption Error: " + Str(GetLastError_())
        EndIf
      Else
        Debug "HTTPSendRequest Error: " + Str(GetLastError_())
      EndIf
    Else
      Debug "HttpOpenRequest Error: " + Str(GetLastError_())
    EndIf
  Else
    Debug "InternetConnect Error: " + Str(GetLastError_())
  EndIf
Else
  Debug "InternetOpen Error: " + Str(GetLastError_())
EndIf
The Problem was: pointer to pointer array
In PB we have to receive the correct entries 'by hand'

When you use 64bit version of PB you have to adjust some things...

Bernd

Re: How to download SSL certificate from a server

Posted: Sat Nov 10, 2018 5:09 pm
by firace
Wow, that's an amazing job.
You are the KING of WINAPI. :!: :!: :!:
Now I will study the code...

Thanks again!

Re: How to download SSL certificate from a server

Posted: Sat Nov 10, 2018 5:29 pm
by infratec
firace wrote:You are the KING of WINAPI. :!: :!: :!:
No, that's definately Rashad :!:

Re: How to download SSL certificate from a server

Posted: Tue Feb 18, 2020 3:55 pm
by tatanas
Sorry to dig up this topic but I'm stucked with a certificate problem with httprequest(). (viewtopic.php?f=13&t=74640)
I would like to download the certificate of a switch. I found your code infratec but as you said, it needs some adjustments to work with 64bits PB and I don't know what variable or Peek I should modify.
Could you tell me what I should change ?

Thanks.

Re: How to download SSL certificate from a server

Posted: Tue Feb 18, 2020 4:17 pm
by firace
tatanas wrote:Sorry to dig up this topic but I'm stucked with a certificate problem with httprequest(). (viewtopic.php?f=13&t=74640)
I would like to download the certificate of a switch. I found your code infratec but as you said, it needs some adjustments to work with 64bits PB and I don't know what variable or Peek I should modify.
Could you tell me what I should change ?

Thanks.
Infratec kindly provided a 64-bit solution here:

viewtopic.php?f=12&t=71727