Introduction
Most encrypted file sharing solutions today use digital certificates, which are designed for applications where there is a large user base not known in advance. However, if someone wants to avoid the cost and the hassle of obtaining and maintaining certificates, there is another possible solution around.
In this article, a simple client/server solution is presented for secure private file sharing. The security is provided through a symmetric encryption algorithm (AES 128) with shared secret keys. It could be more efficient and practical in many situations, when small groups are established.
Before we start
Download and install the free edition of the file sharing SDK, which is free for non-commercial use (two connections).
The client/server components employ 128-bit AES encryption, without the need of SSL certificates. Instead, it uses a shared secret key, which creates an application-based virtual private network. In addition to standard FTP operations, such as upload, download, rename, delete, etc., the library also offers a number of advanced features, like opening of seekable streams on the server, remote searching for files and text, remote zip compression and decompression.
The server
Load the sample project FileServer.sln. The server component can be accessed via its instance named FileSrv
. Let's review its main properties and methods.
Starting the server
In order to start accepting connections, we have to go through the following steps:
- Setting the property
ListenningPort
to the number of the listening port. We take the value from the Settings form.
- If we want to establish encrypted connections, we have to set the property
SecurityMode
to 2, and SecretKey
to the value of the crypto key, specified on the settings form.
If we don't need encryption, we must set SecurityMode
to 0.
- If we want to use packet compression, we must set
UseCompression
to True
.
- Invoke the method
Start()
.
Private Sub SetProperties()
FileSrv.ListeningPort = Val(fSettings.txtPort.Text)
If fSettings.txtKey.Text > "" Then
FileSrv.SecurityMode = 2
FileSrv.SecretKey = fSettings.txtKey.Text
Else
FileSrv.SecurityMode = 0
End If
FileSrv.UseCompression = fSettings.chkCompress.Checked
End Sub
Private Sub btnStart_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnStart.Click
If FileSrv.Start Then
LogMsg("Server started")
Else
Call MsgBox("Cannot start the server!", , "Error")
End If
UpdateStatus()
End Sub
Stopping the server
In order to stop accepting connections, we have to call the method Stop()
. If we have some client currently connected, we should remove them with the method RemoveClient()
.
Private Sub btnStop_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnStop.Click
Dim LI As ListViewItem
Dim i As Integer
FileSrv.Stop()
For i = lvClients.Items.Count - 1 To 0 Step -1
FileSrv.RemoveClient(lvClients.Items(i).Tag)
lvClients.Items.Remove(lvClients.Items(i))
Next i
UpdateStatus()
LogMsg("Server stopped")
End Sub
Adding a new connection
When a new client is connected, the event OnNewClient
is fired. At this point we should get the client information (address/port) and store its handle somewhere for later use.
Private Sub FileSrv_OnNewClient(ByVal sender As Object, _
ByVal e As AxbsFileServerSDK.IBSFileSrvXEvents_OnNewClientEvent) _
Handles FileSrv.OnNewClient
Dim LI As ListViewItem
LI = lvClients.Items.Add("Not signed in")
LI.Tag = e.aHandle
LI.SubItems.Add(FileSrv.GetClientAddress(e.aHandle))
LI.SubItems.Add(Str(FileSrv.GetClientPort(e.aHandle)))
LI.SubItems.Add(VB6.Format(Now, "hh:mm:ss"))
LI.SubItems.Add("Connected")
UpdateStatus()
LogMsg("New connection from " + LI.SubItems.Item(1).Text + _
":" + LI.SubItems.Item(2).Text)
End Sub
Removing disconnected clients
When a connected client is disconnected, the event OnClientDisconnected
is fired. At this point we should release all data associated with this client.
Private Sub FileSrv_OnClientDisconnected(ByVal sender As Object, ByVal e As _
AxbsFileServerSDK.IBSFileSrvXEvents_OnClientDisconnectedEvent) _
Handles FileSrv.OnClientDisconnected
Dim LI As ListViewItem
LI = ItemFromHandle(e.aHandle)
If LI Is Nothing Then Exit Sub
LogMsg("Disconnected " + LI.Text + " " + _
FileSrv.GetClientAddress(e.aHandle) + _
":" + Str(FileSrv.GetClientPort(e.aHandle)))
FileSrv.RemoveClient(LI.Tag)
lvClients.Items.Remove(LI)
End Sub
Providing the user password
When a connection is established, the client immediately sends a sign-in request. In our simple implementation, we accept only the default users (Guest). That's why in the handler of the event OnNeedPassword
, the asked username should always be empty, and we always pass an empty string for the password value.
Private Sub FileSrv_OnNeedPassword1(ByVal sender As Object, ByVal e As _
AxbsFileServerSDK.IBSFileSrvXEvents_OnNeedPasswordEvent) _
Handles FileSrv.OnNeedPassword
If e.aUsername = "" Then
e.aOkay = True
Else
e.aOkay = False
End If
End Sub
Signing in
After checking the password, the server component fires the event OnSignin
. Since that moment, the client can perform various file operations.
Private Sub FileSrv_OnSignin(ByVal sender As Object, ByVal e As _
AxbsFileServerSDK.IBSFileSrvXEvents_OnSigninEvent) _
Handles FileSrv.OnSignin
Dim S As String
Dim LI As ListViewItem
S = GetUsername(e.aHandle)
LI = ItemFromHandle(e.aHandle)
If Not (LI Is Nothing) Then
LI.Text = S
End If
S = S + " " + FileSrv.GetClientAddress(e.aHandle) + _
":" + FileSrv.GetClientPort(e.aHandle)
If e.aCode = 0 Then
S = S + " signed in successfully"
Else
S = S + " failed sign-in"
End If
LogMsg(S)
End Sub
Listing the folder contents
The first thing which the client does after the signing in, is to get the (main) folder content. The handler of the event OnNeedListFolder
is responsible to grant or refuse a permission for the operation. There are two input and two output parameters:
aHandle
- Handle of the client object firing the event.
aPath
- Relative pathname of the folder of interest.
aOkay
- Set this variable to True
in order to permit the operation.
aRoot
- If the operation is permitted, write here the root folder assigned to this user.
Private Sub FileSrv_OnNeedListFolder(ByVal sender As Object, ByVal e As _
AxbsFileServerSDK.IBSFileSrvXEvents_OnNeedListFolderEvent) _
Handles FileSrv.OnNeedListFolder
e.aOkay = True
e.aRoot = App_Path()
LogMsg(GetUsername(e.aHandle) + ": list folder " + PreSlash(e.aPath))
End Sub
The end of the listing operation is signaled with the event OnListFolderDone
.
Handling of "create folder" requests
When a "create folder" request is received from the client, the event OnNeedCreateFolder
is fired. The handler must grant or refuse a permission for the operation. The parameters are:
aHandle
- Handle of the client object firing the event.
aPath
- Relative pathname of the file to be zipped.
aOkay
- Set this variable to True
in order to permit the operation.
aRoot
- If the operation is permitted, write here the root folder assigned to this user.
Private Sub FileSrv_OnNeedCreateFolder(ByVal sender As Object, ByVal e As _
AxbsFileServerSDK.IBSFileSrvXEvents_OnNeedCreateFolderEvent) _
Handles FileSrv.OnNeedCreateFolder
e.aOkay = True
e.aRoot = App_Path()
LogMsg(GetUsername(e.aHandle) + ": create folder " + PreSlash(e.aPath))
End Sub
Handling of "rename file" requests
When a "rename file" request is received from the client, the event OnNeedRenameFile
is fired. The handler must grant or refuse a permission for the operation. The parameters are:
aHandle
- Handle of the client object firing the event.
aPath
- Relative pathname of the file to be renamed.
aOkay
- Set this variable to True
in order to permit the operation.
aRoot
- If the operation is permitted, write here the root folder assigned to this user.
Private Sub FileSrv_OnNeedRenameFile(ByVal sender As Object, ByVal e As _
AxbsFileServerSDK.IBSFileSrvXEvents_OnNeedRenameFileEvent) _
Handles FileSrv.OnNeedRenameFile
e.aOkay = True
e.aRoot = App_Path()
LogMsg(GetUsername(e.aHandle) + ": rename file " + PreSlash(e.aPath))
End Sub
Handling of "delete file" requests
When a "delete file" request is received from the client, the event OnNeedDeleteFile
is fired. The handler must grant or refuse a permission for the operation. The parameters are:
aHandle
- Handle of the client object firing the event.
aPath
- Relative pathname of the file to be deleted.
aOkay
- Set this variable to True
in order to permit the operation.
aRoot
- If the operation is permitted, write here the root folder assigned to this user.
Private Sub FileSrv_OnNeedDeleteFile(ByVal sender As Object, ByVal e As _
AxbsFileServerSDK.IBSFileSrvXEvents_OnNeedDeleteFileEvent) _
Handles FileSrv.OnNeedDeleteFile
e.aOkay = True
e.aRoot = App_Path()
LogMsg(GetUsername(e.aHandle) + ": delete file " + PreSlash(e.aPath))
End Sub
Handling of download requests
When a download request is received from the client, the event OnNeedDownload
is fired. The handler must grant or refuse a permission for the operation. The parameters are:
aHandle
- Handle of the client object firing the event.
aPath
- Relative pathname of the file to be downloaded.
aOkay
- Set this variable to True
in order to permit the operation.
aRoot
- If the operation is permitted, write here the root folder assigned to this user.
Private Sub FileSrv_OnNeedDownload(ByVal sender As Object, ByVal e As _
AxbsFileServerSDK.IBSFileSrvXEvents_OnNeedDownloadEvent) _
Handles FileSrv.OnNeedDownload
e.aOkay = True
e.aRoot = App_Path()
LogMsg(GetUsername(e.aHandle) + ": start downloading " + PreSlash(e.aPath))
End Sub
The event OnDownloadDone
informs about the end of the operation.
Private Sub FileSrv_OnDownloadDone(ByVal sender As Object, ByVal e As _
AxbsFileServerSDK.IBSFileSrvXEvents_OnDownloadDoneEvent) _
Handles FileSrv.OnDownloadDone
LogMsg(GetUsername(e.aHandle) + ": finish downloading ")
End Sub
Handling of upload requests
When an upload request is received from the client, the event OnNeedUpload
is fired. The handler must grant or refuse a permission for the operation. The parameters are:
aHandle
- Handle of the client object firing the event.
aPath
- Relative pathname of the file of interest.
aOkay
- Set this variable to True
in order to permit the operation.
aRoot
- If the operation is permitted, write here the root folder assigned to this user.
Private Sub FileSrv_OnNeedUpload(ByVal sender As Object, ByVal e As _
AxbsFileServerSDK.IBSFileSrvXEvents_OnNeedUploadEvent) _
Handles FileSrv.OnNeedUpload
e.aOkay = True
e.aRoot = App_Path()
LogMsg(GetUsername(e.aHandle) + ": start uploading " + PreSlash(e.aPath))
End Sub
The event OnUploadDone
informs about the end of the operation.
Private Sub FileSrv_OnUploadDone(ByVal sender As Object, ByVal e As _
AxbsFileServerSDK.IBSFileSrvXEvents_OnUploadDoneEvent) _
Handles FileSrv.OnUploadDone
LogMsg(GetUsername(e.aHandle) + ": finish uploading ")
End Sub
The client
Load the sample project FileClient.sln. The client component can be accessed via its instance FileCln
. Let's review its main properties and methods.
Connecting to the server
In order to establish a new connection to the server, we have to go through the following steps:
- If we want encrypted connections, we have to set the property
SecurityMode
to 2, and SecretKey
to the value of the crypto key, specified on the "Connect" form.
If we don't need encryption, we just set SecurityMode
to 0.
- If we want to use packet compression, we must set the property
UseCompression
to True
.
- Invoke the method
Connect(Addr, Port)
, passing as parameters the IP address/Domain name and the listening port of the server.
Private Sub btnConnect_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnConnect.Click
If fConnect.ShowDialog <> DialogResult.OK Then Exit Sub
If fConnect.txtKey.Text() > "" Then
FileCln.SecurityMode = 2
FileCln.SecretKey = fConnect.txtKey.Text
Else
FileCln.SecurityMode = 0
End If
If Not FileCln.Connect(fConnect.txtHost.Text, _
Val(fConnect.txtPort.Text)) Then
MsgBox("Error Code: " + Str(FileCln.LastError))
End If
UpdateStatus()
End Sub
Disconnecting from the server
In order to end a connection to the server, we have to call the method Disconnect()
.
Private Sub btnDisconnect_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnDisconnect.Click
If Not FileCln.Connected Then Exit Sub
FileCln.Disconnect()
Call DoDisconnected()
End Sub
Listing folder contents
The method ListFolder()
sends a request for the folder contents.
Private Sub ListFolder()
If NowList Then Exit Sub
If FileCln.ListFolder(txtFolder.Text) Then
NowList = True
lvFiles.Items.Clear()
btnList.Enabled = False
If txtFolder.Text > "\" Then
Call DoHaveListItem("..", True, 0, 0, 0, 0)
End If
Else
CheckError(FileCln.LastError)
End If
End Sub
The event OnHaveListItem
is fired for each available folder item.
Private Sub DoHaveListItem(ByVal aName As String, _
ByVal aFolder As Boolean, ByVal aLoSize As Long, _
ByVal aHiSize As Long, _
ByVal aLoTime As Long, ByVal aHiTime As Long)
Dim LI As ListViewItem
Dim ft As FILETIME
Dim Dt As Date
LI = lvFiles.Items.Add(aName)
If aFolder Then
LI.ImageIndex = FolderImgIdx
LI.SubItems.Add(" ")
Else
LI.ImageIndex = FileImgIdx
LI.SubItems.Add(Str(aLoSize))
End If
ft.dwHighDateTime = aHiTime
ft.dwLowDateTime = aLoTime
Dt = FileTimeToDate(ft)
LI.SubItems.Add(Dt)
End Sub
The end of the list is signaled with the event OnListFolderDone()
.
Private Sub FileCln_OnListFolderDone(ByVal sender As Object, ByVal e As _
AxbsFileClientSDK.IBSFileClnXEvents_OnListFolderDoneEvent) _
Handles FileCln.OnListFolderDone
NowList = False
UpdateButtons()
End Sub
Creating a new folder
The method CreateFolder()
sends a request to create a new folder.
Private Sub btnNew_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnNew.Click
Dim Nm As String
Nm = InputBox("Folder name:", "Create folder")
If Nm = "" Then Exit Sub
If FileCln.CreateFolder(AddSlash(txtFolder.Text) + Nm) Then
NowCreate = True
Else
CheckError(FileCln.LastError)
End If
UpdateButtons()
End Sub
The event OnCreateFolderDone()
is fired when the folder is created.
Private Sub FileCln_OnCreateFolderDone(ByVal sender As Object, ByVal e As _
AxbsFileClientSDK.IBSFileClnXEvents_OnCreateFolderDoneEvent) _
Handles FileCln.OnCreateFolderDone
NowCreate = False
CheckError(e.aCode)
UpdateButtons()
If e.aCode > 0 Then Exit Sub
Call ListFolder()
End Sub
Renaming a file
The method RenameFile()
sends a request to rename a file on the server.
Private Sub btnRename_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnRename.Click
Dim Nm As String
If lvFiles.SelectedItems.Count = 0 Then Exit Sub
If lvFiles.SelectedItems(0).ImageIndex = FolderImgIdx Then
Nm = InputBox("New name:", "Rename folder")
If Nm = "" Then Exit Sub
If FileCln.RenameFolder(AddSlash(txtFolder.Text) + _
lvFiles.SelectedItems(0).Text, _
AddSlash(txtFolder.Text) + Nm) Then
NowRenameFolder = True
Else
CheckError(FileCln.LastError)
End If
Else
Nm = InputBox("New name:", "Rename file")
If Nm = "" Then Exit Sub
If FileCln.RenameFile(AddSlash(txtFolder.Text) + _
lvFiles.SelectedItems(0).Text, _
AddSlash(txtFolder.Text) + Nm) Then
NowRenameFile = True
Else
CheckError(FileCln.LastError)
End If
End If
Call UpdateButtons()
End Sub
The event OnRenameFileDone()
is fired when the confirmation is received.
Private Sub FileCln_OnRenameFileDone(ByVal sender As Object, ByVal e As _
AxbsFileClientSDK.IBSFileClnXEvents_OnRenameFileDoneEvent) _
Handles FileCln.OnRenameFileDone
NowRenameFile = False
CheckError(e.aCode)
Call UpdateButtons()
If e.aCode > 0 Then Exit Sub
Call ListFolder()
End Sub
Deleting a file
The method DeleteFile()
sends a requests to delete a file.
Private Sub btnDelete_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnDelete.Click
If lvFiles.SelectedItems.Count = 0 Then Exit Sub
If lvFiles.SelectedItems(0).ImageIndex = FolderImgIdx Then
If FileCln.DeleteFolder(AddSlash(txtFolder.Text) + _
lvFiles.SelectedItems(0).Text) Then
NowDeleteFolder = True
Else
CheckError(FileCln.LastError)
End If
Else
If FileCln.DeleteFile(AddSlash(txtFolder.Text) + _
lvFiles.SelectedItems(0).Text) Then
NowDeleteFile = True
Else
CheckError(FileCln.LastError)
End If
End If
Call UpdateButtons()
End Sub
The event OnDeleteFileDone()
is fired when the confirmation is received.
Private Sub FileCln_OnDeleteFileDone(ByVal sender As Object, ByVal e As _
AxbsFileClientSDK.IBSFileClnXEvents_OnDeleteFileDoneEvent) _
Handles FileCln.OnDeleteFileDone
NowDeleteFile = False
CheckError(e.aCode)
Call UpdateButtons()
If e.aCode > 0 Then Exit Sub
Call ListFolder()
End Sub
Downloading a file
The method Download()
sends a request to download a file.
Private Sub GoDownload()
If lvFiles.SelectedItems.Count = 0 Then Exit Sub
If lvFiles.SelectedItems(0).ImageIndex = FolderImgIdx Then
Exit Sub
SaveDlg.FileName = lvFiles.SelectedItems(0).Text
If SaveDlg.ShowDialog() <> DialogResult.OK Then Exit Sub
DnldFile = lvFiles.SelectedItems(0).Text
If FileCln.Download(AddSlash(txtFolder.Text) + _
lvFiles.SelectedItems(0).Text, _
ExtractFilePath(SaveDlg.FileName)) Then
NowDownload = True
txtDownload.Text = DnldFile + ": handshaking"
Else
CheckError(FileCln.LastError)
End If
UpdateStatus()
End Sub
The event OnDownloadProgress()
informs about the download progress.
Private Sub FileCln_OnDownloadProgress(ByVal sender As Object, ByVal e As _
AxbsFileClientSDK.IBSFileClnXEvents_OnDownloadProgressEvent) _
Handles FileCln.OnDownloadProgress
txtDownload.Text = DnldFile + " - " + _
Str(e.aCountLo) + "/" + Str(e.aSizeLo)
End Sub
The event OnDownloadDone()
is fired when the download is completed.
Private Sub FileCln_OnDownloadDone(ByVal sender As Object, _
ByVal e As AxbsFileClientSDK.IBSFileClnXEvents_OnDownloadDoneEvent) _
Handles FileCln.OnDownloadDone
If e.aCode = 0 Then
txtDownload.Text = DnldFile + " - Done."
Else
txtDownload.Text = DnldFile + " - Aborted: " + ErrorText(e.aCode)
End If
NowDownload = False
Call UpdateButtons()
End Sub
Uploading a file
The method Upload()
sends a request to upload a file.
Private Sub btnUpload_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnUpload.Click
On Error GoTo IsCanceled
OpenDlg.FileName = ""
If OpenDlg.ShowDialog() <> DialogResult.OK Then Exit Sub
UpldFile = OpenDlg.FileName
If FileCln.Upload(UpldFile, txtFolder.Text) Then
NowUpload = True
UpldFol = txtFolder.Text
txtUpload.Text = ExtractFileName(UpldFile) + ": handshaking"
Else
CheckError(FileCln.LastError)
End If
UpdateStatus()
IsCanceled:
End Sub
The event OnUploadProgress()
provides information about the upload progress.
Private Sub FileCln_OnUploadProgress(ByVal sender As Object, _
ByVal e As AxbsFileClientSDK.IBSFileClnXEvents_OnUploadProgressEvent) _
Handles FileCln.OnUploadProgress
txtUpload.Text = ExtractFileName(UpldFile) + " - " + _
Str(e.aCountLo) + "/" + Str(e.aSizeLo)
End Sub
The event OnUploadDone()
is fired when the upload operation is completed.
Private Sub FileCln_OnUploadDone(ByVal sender As Object, _
ByVal e As AxbsFileClientSDK.IBSFileClnXEvents_OnUploadDoneEvent) _
Handles FileCln.OnUploadDone
If e.aCode = 0 Then
txtUpload.Text = ExtractFileName(UpldFile) + " - Done."
Else
txtUpload.Text = ExtractFileName(UpldFile) + _
" - Aborted: " + ErrorText(e.aCode)
End If
NowUpload = False
Call UpdateButtons()
If UpldFol = txtFolder.Text Then
Call ListFolder()
End If
End Sub
The bottom line
Nowadays, the Internet is insecure for a variety of reasons. Exchanging sensitive information could be a problem in many situations. In this article, we have presented one simple and affordable solution for secure private file sharing without the need of certificates.