SMTPS - sending emails via gmail using SSL/TLS

Just starting out? Need help? Post your questions and find answers here.
Fred
Administrator
Administrator
Posts: 18152
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: SMTPS - sending emails via gmail using SSL/TLS

Post by Fred »

There is a #PB_Compiler_Version for this: http://www.purebasic.com/documentation/ ... tives.html . Also note than there is no need to have separate header, as the 4.40 way for CallFunctionFast() still work on 4.30. Again, these functions should not be used anymore (prefer prototypes, which are type-proof and smaller/faster).
Mike Trader
User
User
Posts: 43
Joined: Tue Jul 10, 2007 8:09 pm

Re: SMTPS - sending emails via gmail using SSL/TLS

Post by Mike Trader »

>Again, these functions should not be used - prefer prototypes
http://www.purebasic.com/documentation/ ... types.html

The prototype still needs a type for the arguments no?


DeviceOpen.l = cryptDeviceOpen( pDevice, cryptUser, deviceType, @zName )
or
DeviceOpen.l = cryptDeviceOpen( pDevice, cryptUser, deviceType, zName )


OpenLibrary(0, "cl32.dll")
pFunc.DWORD = GetFunction(0, "cryptDeviceOpen") ; not sure what you use for an unsigned 32bit integer

Then Im not sure of the syntax but something like:

CALL BY pFunc cryptDeviceOpen( ....
User avatar
idle
Always Here
Always Here
Posts: 5834
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: SMTPS - sending emails via gmail using SSL/TLS

Post by idle »

Code: Select all

CompilerIf #PB_Compiler_Version = 440 
  Prototype.i protocryptDeviceOpen( pDevice, cryptUser, deviceType, @zName )
CompilerElse
  Prototype.i protocryptDeviceOpen( pDevice, cryptUser, deviceType, zName )
CompilerEndIf

If OpenLibrary(0, "cl32.dll")

  cryptDeviceOpen.protocryptDeviceOpen = GetFunction(0, "cryptDeviceOpen") 

EndIf 

CryptDeviceOpen(1,2,3,"BoB") 
Uncle B
User
User
Posts: 82
Joined: Mon Jan 12, 2004 11:28 am
Location: the Netherlands

Re: SMTPS - sending emails via gmail using SSL/TLS

Post by Uncle B »

Hi again..

I think the last part of the header should be changed to this:

Code: Select all

;  /****************************************************************************
;  *                                                                                                                 *
;  *                                        General Functions                                         *
;  *                                                                                                                 *
;  ****************************************************************************/ 

#Cryptlib = 1

;Global #Cryptlib.l
OpenLibrary(#Cryptlib, "cl32.dll")


Prototype.l Proto_cryptInit()
Global cryptInit.Proto_cryptInit = GetFunction(#Cryptlib, "cryptInit")

Prototype.l Proto_cryptEnd()
Global cryptEnd.Proto_cryptEnd = GetFunction(#Cryptlib, "cryptEnd")

Prototype.l Proto_cryptQueryCapability(device.l, cryptAlgo.l, CQI) ;CQI.CRYPT_QUERY_INFO
Global cryptQueryCapability.Proto_cryptQueryCapability = GetFunction(#Cryptlib, "cryptQueryCapability")

Prototype.l Proto_cryptCreateContext(hContext.l, cryptUser.l, cryptAlgo.l) 
Global cryptCreateContext.Proto_cryptCreateContext = GetFunction(#Cryptlib, "cryptCreateContext")

Prototype.l Proto_cryptDestroyContext(hContext.l) 
Global cryptDestroyContext.Proto_cryptDestroyContext = GetFunction(#Cryptlib, "cryptDestroyContext")

Prototype.l Proto_cryptDestroyObject(hCrypt.l) 
Global cryptDestroyObject.Proto_cryptDestroyObject = GetFunction(#Cryptlib, "cryptDestroyObject")

Prototype.l Proto_cryptGenerateKey(hContext.l) 
Global cryptGenerateKey.Proto_cryptGenerateKey = GetFunction(#Cryptlib, "cryptGenerateKey")

Prototype.l Proto_cryptGenerateKeyAsync(hContext.l) 
Global cryptGenerateKeyAsync.Proto_cryptGenerateKeyAsync = GetFunction(#Cryptlib, "cryptGenerateKeyAsync")

Prototype.l Proto_cryptAsyncQuery(hCrypt.l) 
Global cryptAsyncQuery.Proto_cryptAsyncQuery = GetFunction(#Cryptlib, "cryptAsyncQuery")

Prototype.l Proto_cryptAsyncCancel(hCrypt.l) 
Global cryptAsyncCancel.Proto_cryptAsyncCancel = GetFunction(#Cryptlib, "cryptAsyncCancel")

Prototype.l Proto_cryptEncrypt(hContext.l, pBuffer.i, length.l) 
Global cryptEncrypt.Proto_cryptEncrypt = GetFunction(#Cryptlib, "cryptEncrypt")

Prototype.l Proto_cryptDecrypt(hContext.l, pBuffer.i, length.l) 
Global cryptDecrypt.Proto_cryptDecrypt = GetFunction(#Cryptlib, "cryptDecrypt")

Prototype.l Proto_cryptSetAttribute(hCrypt.l,CryptAttType.l, value.l) 
Global cryptSetAttribute.Proto_cryptSetAttribute = GetFunction(#Cryptlib, "cryptSetAttribute")

Prototype.l Proto_cryptSetAttributeString(hCrypt.l, CryptAttType.l, pBuff.i, StrLen.l) 
Global cryptSetAttributeString.Proto_cryptSetAttributeString = GetFunction(#Cryptlib, "cryptSetAttributeString")

Prototype.l Proto_cryptGetAttribute(hCrypt.l, CryptAttType.l, pRetVal.l) 
Global cryptGetAttribute.Proto_cryptGetAttribute = GetFunction(#Cryptlib, "cryptGetAttribute")

Prototype.l Proto_cryptGetAttributeString(hCrypt.l, CryptAttType.l, pBuff.i, pStrLen.l) 
Global cryptGetAttributeString.Proto_cryptGetAttributeString = GetFunction(#Cryptlib, "cryptGetAttributeString")

Prototype.l Proto_cryptDeleteAttribute(hCrypt.l, CryptAttType.l) 
Global cryptDeleteAttribute.Proto_cryptDeleteAttribute = GetFunction(#Cryptlib, "cryptDeleteAttribute")

Prototype.l Proto_cryptAddRandom(pData.i, RandDataLen.l) 
Global cryptAddRandom.Proto_cryptAddRandom = GetFunction(#Cryptlib, "cryptAddRandom")

Prototype.l Proto_cryptQueryObject(pData.i, pCOI) ;pCOI.CRYPT_OBJECT_INFO)
Global cryptQueryObject.Proto_cryptQueryObject = GetFunction(#Cryptlib, "cryptQueryObject")


;  /****************************************************************************
;  *                                                                                                                 *
;  *                                        Mid-level Encryption Functions                             *
;  *                                                                                                                 *
;  ****************************************************************************/

Prototype.l Proto_cryptExportKey(pKey.i, pEncryptedKeyLength.l, exportKey.l, sessionKeyContext.l) 
Global cryptExportKey.Proto_cryptExportKey = GetFunction(#Cryptlib, "cryptExportKey")

Prototype.l Proto_cryptExportKeyEx(pKey.i, pEncryptedKeyLength.l, FormatType.l, exportKey.l, sessionKeyContext.l) 
Global cryptExportKeyEx.Proto_cryptExportKeyEx = GetFunction(#Cryptlib, "cryptExportKeyEx")

Prototype.l Proto_cryptImportKey(pKey.i, importKey.l, sessionKeyContext.l)
Global cryptImportKey.Proto_cryptImportKey = GetFunction(#Cryptlib, "cryptImportKey")

Prototype.l Proto_cryptImportKeyEx(pKey.i, importKey.l, sessionKeyContext.l, pReturnedContext.l)
Global cryptImportKeyEx.Proto_cryptImportKeyEx = GetFunction(#Cryptlib, "cryptImportKeyEx")

Prototype.l Proto_cryptCreateSignature(pSig.i, pSignatureLength.l, signContext.l, hashContext.l)
Global cryptCreateSignature.Proto_cryptCreateSignature = GetFunction(#Cryptlib, "cryptCreateSignature")

Prototype.l Proto_cryptCreateSignatureEx(pSig.i, pSignatureLength.l, FormatType.l, signContext.l, hashContext.l, extraData.l)
Global cryptCreateSignatureEx.Proto_cryptCreateSignatureEx = GetFunction(#Cryptlib, "cryptCreateSignatureEx")

Prototype.l Proto_cryptCheckSignature(pSig.i, sigCheckKey.l, hashContext.l)
Global cryptCheckSignature.Proto_cryptCheckSignature = GetFunction(#Cryptlib, "cryptCheckSignature")

Prototype.l Proto_cryptCheckSignatureEx(pSig.i, sigCheckKey.l, hashContext.l, pExtraData.l)
Global cryptCheckSignatureEx.Proto_cryptCheckSignatureEx = GetFunction(#Cryptlib, "cryptCheckSignatureEx")


;  /****************************************************************************
;  *                                                                                                                 *
;  *                                                  Keyset Functions                                       *
;  *                                                                                                                 *
;  ****************************************************************************/

Prototype.l Proto_cryptKeysetOpen(pKeyset.l, cryptUser.l, keysetType.l, zName.s, options.l)
Global cryptKeysetOpen.Proto_cryptKeysetOpen = GetFunction(#Cryptlib, "cryptKeysetOpen")

Prototype.l Proto_cryptKeysetClose(keyset.l)
Global cryptKeysetClose.Proto_cryptKeysetClose = GetFunction(#Cryptlib, "cryptKeysetClose")

Prototype.l Proto_cryptGetPublicKey(keyset.l, pContext.l, keyIDtype.l, zKeyID.s)
Global cryptGetPublicKey.Proto_cryptGetPublicKey = GetFunction(#Cryptlib, "cryptGetPublicKey")

Prototype.l Proto_cryptGetPrivateKey(keyset.l, pContext.l, keyIDtype.l, zKeyID.s, zPassword.s)
Global cryptGetPrivateKey.Proto_cryptGetPrivateKey = GetFunction(#Cryptlib, "cryptGetPrivateKey")

Prototype.l Proto_cryptGetKey(keyset.l, CryptContext.l, keyIDtype.l, zKeyID.s, zPassword.s)
Global cryptGetKey.Proto_cryptGetKey = GetFunction(#Cryptlib, "cryptGetKey")

Prototype.l Proto_cryptAddPublicKey(keyset.l, certificate.l)
Global cryptAddPublicKey.Proto_cryptAddPublicKey = GetFunction(#Cryptlib, "cryptAddPublicKey")

Prototype.l Proto_cryptAddPrivateKey(keyset.l, cryptKey.l, zPassword.s)
Global cryptAddPrivateKey.Proto_cryptAddPrivateKey = GetFunction(#Cryptlib, "cryptAddPrivateKey")

Prototype.l Proto_cryptDeleteKey(keyset.l, keyIDtype.l, zKeyID.s)
Global cryptDeleteKey.Proto_cryptDeleteKey = GetFunction(#Cryptlib, "cryptDeleteKey")


;  /****************************************************************************
;  *                                                                                                                 *
;  *                                                Certificate Functions                                   *
;  *                                                                                                                 *
;  ****************************************************************************/

Prototype.l Proto_cryptCreateCert(pCert.l, cryptUser.l, certType.l)
Global cryptCreateCert.Proto_cryptCreateCert = GetFunction(#Cryptlib, "cryptCreateCert")

Prototype.l Proto_cryptDestroyCert(hCert.l)
Global cryptDestroyCert.Proto_cryptDestroyCert = GetFunction(#Cryptlib, "cryptDestroyCert")

Prototype.l Proto_cryptGetCertExtension(hCert.l, zOid.s, pCriticalFlag.l, pExtension.i, pextensionLen.l)
Global cryptGetCertExtension.Proto_cryptGetCertExtension = GetFunction(#Cryptlib, "cryptGetCertExtension")

Prototype.l Proto_cryptAddCertExtension(hCert.l, zOid.s, criticalFlag.l, pExtension.i, extensionLen.l)
Global cryptAddCertExtension.Proto_cryptAddCertExtension = GetFunction(#Cryptlib, "cryptAddCertExtension")

Prototype.l Proto_cryptDeleteCertExtension(hCert.l, zOid.s)
Global cryptDeleteCertExtension.Proto_cryptDeleteCertExtension = GetFunction(#Cryptlib, "cryptDeleteCertExtension")

Prototype.l Proto_cryptSignCert(hCert.l, signContext.l)
Global cryptSignCert.Proto_cryptSignCert = GetFunction(#Cryptlib, "cryptSignCert")

Prototype.l Proto_cryptCheckCert(hCert.l, sigCheckKey.l)
Global cryptCheckCert.Proto_cryptCheckCert = GetFunction(#Cryptlib, "cryptCheckCert")

Prototype.l Proto_cryptImportCert(pCertObj.i, certObjectLength.l, cryptUser.l, pCert.l)
Global cryptImportCert.Proto_cryptImportCert = GetFunction(#Cryptlib, "cryptImportCert")

Prototype.l Proto_cryptExportCert(pCertObj.i, pCertObjectLength.l, certFormatType.l, hCert.l)
Global cryptExportCert.Proto_cryptExportCert = GetFunction(#Cryptlib, "cryptExportCert")

Prototype.l Proto_cryptCAAddItem(keyset.l, hCert.l)
Global cryptCAAddItem.Proto_cryptCAAddItem = GetFunction(#Cryptlib, "cryptCAAddItem")

Prototype.l Proto_cryptCAGetItem(keyset.l, pCert.l, certType.l, keyIDtype.l, zKeyID.s)
Global cryptCAGetItem.Proto_cryptCAGetItem = GetFunction(#Cryptlib, "cryptCAGetItem")

Prototype.l Proto_cryptCADeleteItem(keyset.l, keyIDtype.l, zKeyID.s)
Global cryptCADeleteItem.Proto_cryptCADeleteItem = GetFunction(#Cryptlib, "cryptCADeleteItem")

Prototype.l Proto_cryptCACertManagement(pCert.l, CertAction.l, keyset.l, caKey.l, certRequest.l)
Global cryptCACertManagement.Proto_cryptCACertManagement = GetFunction(#Cryptlib, "cryptCACertManagement")


;  /****************************************************************************
;  *                                                                                                                 *
;  *                                        Envelope & Session Functions                               *
;  *                                                                                                                 *
;  ****************************************************************************/

Prototype.l Proto_cryptCreateSession(pSession.l, cryptUser.l, SessionType.l)
Global cryptCreateSession.Proto_cryptCreateSession = GetFunction(#Cryptlib, "cryptCreateSession")

Prototype.l Proto_cryptDestroySession(session.l)       
Global cryptDestroySession.Proto_cryptDestroySession = GetFunction(#Cryptlib, "cryptDestroySession")

Prototype.l Proto_cryptCreateEnvelope(pEnvelope.l, cryptUser.l, FormatType.l)     
Global cryptCreateEnvelope.Proto_cryptCreateEnvelope = GetFunction(#Cryptlib, "cryptCreateEnvelope")

Prototype.l Proto_cryptDestroyEnvelope(envelope.l)      
Global cryptDestroyEnvelope.Proto_cryptDestroyEnvelope = GetFunction(#Cryptlib, "cryptDestroyEnvelope")

Prototype.l Proto_cryptPushData(envelope.l, pBuff.i, StrLen.l,  pBytesCopied.l)     
Global cryptPushData.Proto_cryptPushData = GetFunction(#Cryptlib, "cryptPushData")

Prototype.l Proto_cryptFlushData(envelope.l)           
Global cryptFlushData.Proto_cryptFlushData = GetFunction(#Cryptlib, "cryptFlushData")

Prototype.l Proto_cryptPopData(envelope.l, pBuff.i, StrLen.l, pBytesCopied.l)          
Global cryptPopData.Proto_cryptPopData = GetFunction(#Cryptlib, "cryptPopData")


;  /****************************************************************************
;  *                                                                                                                 *
;  *                                              Device Functions                                           *
;  *                                                                                                                 *
;  ****************************************************************************/

Prototype.l Proto_cryptDeviceOpen(pDevice.l, cryptUser.l, deviceType.l, zName.s)
Global cryptDeviceOpen.Proto_cryptDeviceOpen = GetFunction(#Cryptlib, "cryptDeviceOpen")

Prototype.l Proto_cryptDeviceClose(device.l)
Global cryptDeviceClose.Proto_cryptDeviceClose = GetFunction(#Cryptlib, "cryptDeviceClose")

Prototype.l Proto_cryptDeviceQueryCapability(device.l, cryptAlgo.l, pCryptQueryInfo) ;pCryptQueryInfo.CRYPT_QUERY_INFO
Global cryptDeviceQueryCapability.Proto_cryptDeviceQueryCapability = GetFunction(#Cryptlib, "cryptDeviceQueryCapability")

Prototype.l Proto_cryptDeviceCreateContext(device.l, pContext.l, cryptAlgo.l)
Global cryptDeviceCreateContext.Proto_cryptDeviceCreateContext = GetFunction(#Cryptlib, "cryptDeviceCreateContext")


;  /****************************************************************************
;  *                                                                                                                 *
;  *                                          User Management Functions                                   *
;  *                                                                                                                 *    
;  ****************************************************************************/

Prototype.l Proto_cryptLogin(pUser.l, zName.s, zPassword.s)
Global cryptLogin.Proto_cryptLogin = GetFunction(#Cryptlib, "cryptLogin")

Prototype.l Proto_cryptLogout(user.l)
Global cryptLogout.Proto_cryptLogout = GetFunction(#Cryptlib, "cryptLogout")


;  /****************************************************************************
;  *                                                                                                                 *
;  *                                          User Interface Functions                                   *
;  *                                                                                                                 *
;  ****************************************************************************/

Prototype.l Proto_cryptUIGenerateKey(CryptDevice.l, CryptContext.l, CryptCert.l, zPassword.s, hWnd.l)
Global cryptUIGenerateKey.Proto_cryptUIGenerateKey = GetFunction(#Cryptlib, "cryptUIGenerateKey")

Prototype.l Proto_cryptUIDisplayCert(CryptCert.l, hWnd.l)
Global cryptUIDisplayCert.Proto_cryptUIDisplayCert = GetFunction(#Cryptlib, "cryptUIDisplayCert")

As I think it would be a waste to only use cryptLib for Gmail I've been trying to create a PB-like set of functions to build a proper TLS server.
I did manage to do so, but only in a very unstable way. In the below example project the stability decreases as the number of clients increases. As I'm not very experienced I think I might be using a wrong approach. I would highly appriciate any of you to see how this can be done in a stable and fast way with a high number of clients..

Thanks in advance,

Bart

SSL Library.pb:

Code: Select all


XIncludeFile "Cryptlib_Header_V2.pb"

cryptContext.l
cryptKeyset.l
cryptCertificate.l


Structure SSLServerConnection
    ThreadID.l
    ConnectionID.l
    UserName.s
EndStructure

Structure SSLServerParams
    ServerPort.l
    KeysetFile.s
    KeysetLabel.s
    KeysetPassword.s
EndStructure

Enumeration 
  #SSLEvent_Connect = 1   
  #SSLEvent_Data  
  #SSLEvent_File
  #SSLEvent_Disconnect
EndEnumeration

Structure SSLServerEvents
  ClientID.l
  EventType.l
  pBuffer.l
  BufferLength.l
  Processed.l
EndStructure

Global NewList SSL_Server_Connections.SSLServerConnection()
Global NewList SSL_EventBuffer.SSLServerEvents()

Global SSLServerParameters.SSLServerParams
Global SSLServerMutex = CreateMutex()
Global SSLEventMutex = CreateMutex()

Procedure GenerateKeyset(newKeysetFile.s, KeysetLabel.s, CommonName.s, PassWord.s)

;Parameter specifications:
;========================
;newKeysetFile:   a path+filename where the keyset and certificate can be saved to. 
;                 existing files will be overwritten. The common used extension for this kind of file is *.p15 (see Cryptlib manual).
;KeysetLabel:     a label where to identify the generated keyset by in the keyset file.
;PassWord:        password used for future extraction of the private key.
;CommonName:      Name used for certificate. This should typically be the server name (e.g. www.yourserver.com) but every random string is accepted.
;                 Not using the server name may cause some browsers to generate warning messages (see Cryptlib manual).
;========================


;*** Generate keyset with certificate ***

Protected cryptContext.l, cryptKeyset.l, cryptCertificate.l
Protected ReturnValue.l = 1


RetVal = cryptCreateContext(@cryptContext, #CRYPT_UNUSED, #CRYPT_ALGO_RSA) ;Create encription context
If RetVal <> 0 : ReturnValue = 0 : EndIf

    RetVal = cryptSetAttributeString(cryptContext, #CRYPT_CTXINFO_LABEL, @KeysetLabel, Len(KeysetLabel));
    If RetVal <> 0 : ReturnValue = 0 : EndIf
    
    RetVal = cryptGenerateKey(cryptContext);
    If RetVal <> 0 : ReturnValue = 0 : EndIf
    
    RetVal = cryptKeysetOpen(@cryptKeyset, #CRYPT_UNUSED, #CRYPT_KEYSET_FILE, newKeysetFile, #CRYPT_KEYOPT_CREATE);
    If RetVal <> 0 : ReturnValue = 0 : EndIf
    
    ;/* Load/store keys */
        RetVal = cryptAddPrivateKey(cryptKeyset, cryptContext, PassWord);
        If RetVal <> 0 : ReturnValue = 0 : EndIf
        
        ;/* Create a simplified certificate */
        RetVal = cryptCreateCert(@cryptCertificate, #CRYPT_UNUSED, #CRYPT_CERTTYPE_CERTIFICATE);
        If RetVal <> 0 : ReturnValue = 0 : EndIf
        
        RetVal = cryptSetAttribute(cryptCertificate, #CRYPT_CERTINFO_XYZZY, 1);
        If RetVal <> 0 : ReturnValue = 0 : EndIf
        
        ;/* Add the public key And certificate owner name And sign the certificate with the private key */
        RetVal = cryptSetAttribute(cryptCertificate, #CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO, cryptContext);
        If RetVal <> 0 : ReturnValue = 0 : EndIf
        
        RetVal = cryptSetAttributeString(cryptCertificate, #CRYPT_CERTINFO_COMMONNAME, @CommonName, Len(CommonName));
        If RetVal <> 0 : ReturnValue = 0 : EndIf
        
        RetVal = cryptSignCert(cryptCertificate, cryptContext);
        If RetVal <> 0 : ReturnValue = 0 : EndIf
        
        RetVal = cryptAddPublicKey(cryptKeyset, cryptCertificate );
        If RetVal <> 0 : ReturnValue = 0 : EndIf

    RetVal = cryptKeysetClose(cryptKeyset)
    If RetVal <> 0 : ReturnValue = 0 : EndIf
    
RetVal = cryptDestroyContext(cryptContext);
If RetVal <> 0 : ReturnValue = 0 : EndIf

ProcedureReturn ReturnValue

EndProcedure   

Procedure.l WaitSSLServerEvent(ConnectionID)

Protected sBuff.l, BytesReply.l, Buff2.l, oldsize.l, Done.l

sBuff = AllocateMemory(256)

Repeat
      RetVal = CryptPopData(ConnectionID, sBuff, 256, @BytesReply )
      If RetVal <> #CRYPT_OK : Debug "CryptPopData ERROR": EndIf
      
      If BytesReply > 0
            
            Buff2 = AllocateMemory(256)
            CopyMemory(sBuff, Buff2, BytesReply)
            size = BytesReply
            
            Repeat
            
                RetVal = CryptPopData(ConnectionID, sBuff, 256, @BytesReply )
                If RetVal <> #CRYPT_OK : Debug "CryptPopData ERROR": EndIf
      
                If BytesReply > 0
                      Buff2 = ReAllocateMemory(Buff2, size + BytesReply)
                      CopyMemory(sBuff, Buff2 + size, BytesReply)
                      size + BytesReply
                Else
                   done=1
                   Break
                EndIf

            ForEver

      Else
          Delay(200)
      EndIf
      
      If done=1
         Break
      EndIf
      
ForEver

ProcedureReturn Buff2

EndProcedure

Procedure WaitEventProcessing(ConnectionID)

Protected processed.l = 0

Repeat
    LockMutex(SSLEventMutex)
        ForEach SSL_EventBuffer()
           If SSL_EventBuffer()\ClientID = ConnectionID
              If SSL_EventBuffer()\processed = 1
                    DeleteElement(SSL_EventBuffer())
                    processed =1 
              EndIf
              Break
           EndIf
        Next
    UnlockMutex(SSLEventMutex)
Delay(100)
Until processed = 1

EndProcedure

Procedure SSLServer(void)

Protected cryptContext.l, cryptKeyset.l, cryptSession.l
Protected privateKey.l, publicKey.l, Port.l, bytes.l, BytesReply.l
Protected password.s, label.s, fname.s, clientname.s
Protected eventBuffer.SSLServerEvents


With SSLServerParameters
    Port = \ServerPort
    label = \KeysetLabel
    fname = \KeysetFile ;"TestKeyset.p15"
    password = \KeysetPassword
EndWith 


;/* Create cryptContext */
     cryptCreateContext(@cryptContext, #CRYPT_UNUSED, #CRYPT_ALGO_RSA)

;/* Open Keyset file */
     cryptKeysetOpen(@cryptKeyset, #CRYPT_UNUSED, #CRYPT_KEYSET_FILE, fname, #CRYPT_KEYOPT_READONLY);

;/* Load private key */
         cryptGetPrivateKey(cryptKeyset, @privateKey, #CRYPT_KEYID_NAME, label, password)

;/* Create the session */
         cryptCreateSession(@cryptSession, #CRYPT_UNUSED, #CRYPT_SESSION_SSL_SERVER)

;/* Add the server key/certificate, add the port and activate the session */
       cryptSetAttribute(cryptSession, #CRYPT_SESSINFO_SERVER_PORT, Port)
       cryptSetAttribute(cryptSession, #CRYPT_SESSINFO_PRIVATEKEY, privateKey);
       cryptSetAttribute(cryptSession, #CRYPT_SESSINFO_ACTIVE, 1 );

    
    ;***************************************************************************************************
    ;************************************ Create new server thread *************************************
    ;***************************************************************************************************
    ;After a client contacts this server a new server thread must be created to stand by for the next client to log on.
    ;Also register the current cryptSession as ConnectionID in the SSL_Server_Connections() list.
    ;The stand by server thread is always the last one in the list.
    
      thread = CreateThread(@SSLServer(), 0)
        If thread
            LockMutex(SSLServerMutex)
            
                LastElement(SSL_Server_Connections())
                SSL_Server_Connections()\ConnectionID = cryptSession
      
                AddElement(SSL_Server_Connections())
                SSL_Server_Connections()\ThreadID = thread
                
            UnlockMutex(SSLServerMutex)
        EndIf
        
    ;***************************************************************************************************
    ;***************************************************************************************************
    
    sReply.s = ""
    sBuff = AllocateMemory(256)
      
      Repeat

        ;eventBuffer\ClientID = cryptSession
      
      cryptGetAttribute(cryptSession, #CRYPT_SESSINFO_CONNECTIONACTIVE, @connectionActive);
      If connectionActive
     
          Buff = WaitSSLServerEvent(cryptSession)
      
          If Buff
           LockMutex(SSLEventMutex)
              AddElement(SSL_EventBuffer())
              With SSL_EventBuffer()
                   \ClientID = cryptSession
                   \EventType = #SSLEvent_Data
                   \pBuffer = Buff
                   \Bufferlength = MemorySize(Buff)
                   \Processed = 0
              EndWith 
            UnlockMutex(SSLEventMutex)

            WaitEventProcessing(cryptSession)
        
            FreeMemory(Buff)
          EndIf
        
        Else
            Break
        EndIf
      ForEver

;/* Clean up */
    cryptDestroySession(cryptSession)
    cryptKeysetClose(cryptKeyset)
    cryptDestroyContext(cryptContext)

    LockMutex(SSLServerMutex)
      ForEach SSL_Server_Connections()
        If SSL_Server_Connections()\ConnectionID = cryptSession
            DeleteElement(SSL_Server_Connections())
            Break
        EndIf
      Next
    UnlockMutex(SSLServerMutex)

EndProcedure

Procedure SSLEvent()

result = 0
LockMutex(SSLEventMutex)
    If ListSize(SSL_EventBuffer()) > 0
          FirstElement(SSL_EventBuffer())
          result = SSL_EventBuffer()\EventType
          
    EndIf
UnlockMutex(SSLEventMutex)
ProcedureReturn result 

EndProcedure

Procedure SSLEventClient()
Protected result.l = 0
LockMutex(SSLEventMutex)
    If ListSize(SSL_EventBuffer()) > 0
          FirstElement(SSL_EventBuffer())
          result = SSL_EventBuffer()\ClientID
    EndIf
UnlockMutex(SSLEventMutex)
ProcedureReturn result 

EndProcedure

Procedure RecieveSSLData(ConnectionID)

Protected result.l = 0
LockMutex(SSLEventMutex)
    If ListSize(SSL_EventBuffer()) > 0
          ForEach SSL_EventBuffer()
          With SSL_EventBuffer()
            If \ClientID = ConnectionID
                *DataBuffer = AllocateMemory(\BufferLength)
                CopyMemory(\pBuffer, *DataBuffer, \BufferLength)
                result = *DataBuffer
                \Processed = 1
                Break
            EndIf
          EndWith
          Next
    EndIf
UnlockMutex(SSLEventMutex)

ProcedureReturn result 

EndProcedure

Procedure OpenSSLConnection(ServerName.s, Port.l)

Protected cryptSession;

;/* Create the session */
    cryptCreateSession(@cryptSession, #CRYPT_UNUSED, #CRYPT_SESSION_SSL);

;/* Add the server name and activate the session */
    cryptSetAttributeString(cryptSession, #CRYPT_SESSINFO_SERVER_NAME, @ServerName, Len(ServerName));
    cryptSetAttribute(cryptSession, #CRYPT_SESSINFO_SERVER_PORT, Port)
    cryptSetAttribute(cryptSession, #CRYPT_SESSINFO_ACTIVE, 1 );

    ProcedureReturn cryptSession

EndProcedure

Procedure SendSSLString(ConnectionID.l, string$) 

Protected copied.l

pBuff.s = Space(255)

If ConnectionID
      ;RetVal = cryptPopData(ConnectionID, @pBuff, 256, @copied)
      ;If RetVal <> 0 : Debug "popData Error" : ProcedureReturn 0 : EndIf

      RetVal = cryptPushData(ConnectionID, @string$, Len(string$), @copied)
      If RetVal <> 0 : Debug "pushData Error" : ProcedureReturn 0 : EndIf
      
      RetVal = cryptFlushData(ConnectionID)
      If RetVal <> 0
          ProcedureReturn 0
      EndIf
      
Else
    ProcedureReturn 0
EndIf

ProcedureReturn 1

EndProcedure

Procedure CreateSSLServer(Port.l, KeySetFile.s, KeySetLabel.s, KeySetPassword.s)

With SSLServerParameters
    \ServerPort = Port
    \KeysetFile = KeySetFile
    \KeysetLabel = KeySetLabel
    \KeysetPassword = KeySetPassWord
EndWith

ClearList(SSL_Server_Connections())
thread = CreateThread(@SSLServer(), 0)

If thread
    LastElement(SSL_Server_Connections())
    AddElement(SSL_Server_Connections())
    SSL_Server_Connections()\ThreadID = thread
    
    ProcedureReturn 1
Else
    ProcedureReturn 0
EndIf

EndProcedure

Procedure SSLServerShutDown()

  LockMutex(SSLServerMutex)
    ForEach SSL_Server_Connections()
      If IsThread(SSL_Server_Connections()\ThreadID)
        KillThread(SSL_Server_Connections()\ThreadID)
      EndIf
      DeleteElement(SSL_Server_Connections())
    Next
  UnlockMutex(SSLServerMutex)

EndProcedure
Server.pb:

Code: Select all

XIncludeFile "SSL Library.pb"

cryptInit()

If GenerateKeyset("TestKeyset.p15", "key", "commonname", "password")
    Debug "Keyset generated succesfully..."
Else
    Debug "Unable to generate keyset..."
EndIf

CreateSSLServer(6000, "TestKeyset.p15", "key", "password")

Repeat

    Event = SSLEvent()
    
        If Event
        
            ClientID = SSLEventClient()
            
            Select Event
                
                Case #SSLEvent_Data
                                  
                   DataBuffer = RecieveSSLData(ClientID)
                   
                   string$ = PeekS(DataBuffer)
                   Debug string$
                   sendSSLString(ClientID, "String received OK"); + Chr(10) + Chr(13))
                   
                   FreeMemory(DataBuffer)
                   
            
            EndSelect
        EndIf
        
Delay(200) ; Increase delay to increase stability...

ForEver

SSLServerShutDown()
Client.pb:

Code: Select all

XIncludeFile "SSL Library.pb"

cryptInit()

Procedure startclient(number)

  ConnectionID = OpenSSLConnection("localhost", 6000)

  cnt = 0

  If ConnectionID
    Repeat
        res = SendSSLString(ConnectionID, "This is test string #"+Str(cnt)+" from client #"+ Str(number))
        If res 
            cnt +1
            *Buffer = WaitSSLServerEvent(ConnectionID)
            
            If *Buffer
                  sReply.s = PeekS(*Buffer)
                  Debug "sReply= " + sReply
                  FreeMemory(*Buffer)
            EndIf
            Delay(1000)

        Else
            Break
        EndIf

    ForEver
  EndIf
  
EndProcedure
  
 
 
 For i = 1 To 3
    CreateThread(@startclient(),i)
    Delay(1050)
 Next
 
 Repeat
 
    Delay(1000)
 
 ForEver
 
Post Reply