How to download SSL certificate from a server

Just starting out? Need help? Post your questions and find answers here.
firace
Addict
Addict
Posts: 899
Joined: Wed Nov 09, 2011 8:58 am

How to download SSL certificate from a server

Post 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))
firace
Addict
Addict
Posts: 899
Joined: Wed Nov 09, 2011 8:58 am

Re: How to download SSL certificate from a server

Post 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.
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: How to download SSL certificate from a server

Post 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
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: How to download SSL certificate from a server

Post 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.
firace
Addict
Addict
Posts: 899
Joined: Wed Nov 09, 2011 8:58 am

Re: How to download SSL certificate from a server

Post 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"));
}
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: How to download SSL certificate from a server

Post 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.
Last edited by infratec on Sat Nov 10, 2018 12:28 pm, edited 1 time in total.
firace
Addict
Addict
Posts: 899
Joined: Wed Nov 09, 2011 8:58 am

Re: How to download SSL certificate from a server

Post 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)

infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: How to download SSL certificate from a server

Post 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.
Last edited by infratec on Sat Nov 10, 2018 12:29 pm, edited 1 time in total.
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: How to download SSL certificate from a server

Post 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
firace
Addict
Addict
Posts: 899
Joined: Wed Nov 09, 2011 8:58 am

Re: How to download SSL certificate from a server

Post by firace »

Wow, that's an amazing job.
You are the KING of WINAPI. :!: :!: :!:
Now I will study the code...

Thanks again!
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: How to download SSL certificate from a server

Post by infratec »

firace wrote:You are the KING of WINAPI. :!: :!: :!:
No, that's definately Rashad :!:
tatanas
Enthusiast
Enthusiast
Posts: 199
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: How to download SSL certificate from a server

Post 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.
Windows 10 Pro x64
PureBasic 6.04 x64
firace
Addict
Addict
Posts: 899
Joined: Wed Nov 09, 2011 8:58 am

Re: How to download SSL certificate from a server

Post 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
Post Reply