I'll start without a hitch, once I had a revelation (well, not very powerful, to be honest) and the idea arose to print a program that transfers an image from a client to a server. Simple enough right? Well, for an experienced programmer, it will be so. The conditions are simple - do not use third-party libraries. In principle, itβs a little more complicated, but given that you have to figure it out and look for examples, well, such an occupation. I decided that this task was up to me. Plus, it is desirable that there be enough code so that it can be posted on the forum in case you need help. First of all, my eyes fell on FTP, by the way, the OS in which Windows is being developed. The advantage of FTP is that you can transfer not only an image, but any file through it. After downloading Filezilla Server, sharing one directory for reading / writing and creating a user with a password, I tried to connect Filezilla Client, everything worked. I created a simple code example in C/C++:
#include <iostream>
void main()
{
FILE* fs;
fopen_s(&fs, "1.txt", "w");
if (fs)
{
fwrite("userrnpasswordrnsend D:\share.txtrnbye", 1, sizeof("userrnpasswordrnsend D:\share.txtrnbye"), fs);
fwrite("00", 1, sizeof("00"), fs);
fclose(fs);
}
system("ftp -s:1.txt 127.0.0.1");
}
If my memory serves me, then everything worked on the localhost, and when transmitting over the network, an error occurred in the line with send. What is convenient here is a) briefly b) you do not need to install a client, but use the already built-in tool for ftp from Microsoft. Although in my opinion it must be activated through programs and components. If you figure out what the problem of this method is and write in the comments, it will be great.
Having not found an answer on a bunch of forums, I left this code and decided to use the interface for socket networks. I already had the experience of passing an array of chars to another program. By the way, you can read from Tanenbaum, Computer networks, in the chapter about the transport layer. There is an example of a client and a server, though not for the βmany clients - one serverβ connection, but only βone client - one serverβ. Since the transmission is over the Internet, you need to encrypt the data somehow. For this, a block cipher is used - the Feistel network. Plus on the server it is necessary to make several (more than one client) clients. To do this, we will use Threads, the image for transmission will take a screenshot of the screen from the client, be encrypted and transmitted to the server, where it will be decrypted and immediately displayed on the screen through the default program for opening * .tga images.
Server code:
#include <iostream>
#include <WinSock.h>
#pragma comment (lib,"WS2_32.lib")
#include <fstream>
#include <algorithm>
#include <string>
#include <iterator>
#include <vector>
void error(const char* msg)
{
//perror(msg);
std::cout<<'n'<<WSAGetLastError();
WSACleanup();
std::cin.ignore();
exit(1);
}
void bzero(char*buf, int l)
{
for (int i = 0; i < l; i++)
buf[i] = '';
}
struct arg_s
{
unsigned char* buffer2;
bool exit;
};
char** buffer;
struct arg_sa
{
struct arg_s* lalk;
int current;
};
#define type struct arg_sa
int sockfd, * newsockfd;//ΡΠ»ΡΡΠ°ΡΡΠΈΠΉ ΠΈ ΠΌΠ°ΡΡΠΈΠ² ΠΊΠ»ΠΈΠ΅Π½ΡΡΠΊΠΈΡ
ΡΠΎΠΊΠ΅ΡΠΎΠ²
int buflen2 = 10292000;//ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΡΠΉ ΡΠ°Π·ΠΌΠ΅Ρ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ Π² Π±Π°ΠΉΡΠ°Ρ
Π΄Π»Ρ RGBA*Width*Height
struct sockaddr_in *cli_addr;
int* clilen;
int currentclient,cc;//ΡΡ-ΠΊΠ»ΠΈΠ΅Π½Ρ ΠΏΠΎ ΡΡΠ΅ΡΡ(Π΄Π»Ρ Π·Π°ΠΏΠΈΡΠΈ ΠΈΠ½ΠΊΡΠ΅ΠΌΠ΅Π½ΡΠ° ΠΈΠΌΠ΅Π½ΠΈ ΡΠ°ΠΉΠ»Π° ΠΊΠ»ΠΈΠ΅Π½ΡΠ° ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ)
typedef unsigned long long uint64_t;
typedef unsigned int uint32_t;
#define N 8//ΡΠ°Π·ΠΌΠ΅Ρ Π±Π»ΠΎΠΊΠ°
#define F32 0xFFFFFFFF
uint32_t RK[N];//ΡΠ°ΡΠ½Π΄ΠΎΠ²ΡΠ΅ ΠΊΠ»ΡΡΠΈ
#define size64 sizeof(uint64_t)
#define ROR(x,n,xsize)((x>>n)|(x<<(xsize-n)))
#define ROL(x,n,xsize)((x<<n)|(x>>(xsize-n)))
#define RKEY(r)((ROR(K,r*3,size64*8))&F32)
const uint64_t K = 0x96EA704CFB1CF671;//ΠΊΠ»ΡΡ ΡΠΈΡΡΠΎΠ²Π°Π½ΠΈΡ
struct hostent* server;
uint32_t F(uint32_t subblk, uint32_t key)
{
return subblk + key;//ΡΡΠ½ΠΊΡΠΈΡ ΡΠΈΡΡΠΎΠ²Π°Π½ΠΈΡ
}
void createRoundKeys()
{
for (int i = 0; i < N; i++)
RK[i] = (ROR(K, i * 8, size64 * 8)) & F32;
}
uint64_t decrypt(uint64_t c_block)//ΡΠ°ΡΡΠΈΡΡΠΎΠ²ΠΊΠ° Π±Π»ΠΎΠΊΠΎΠ² ΡΠ΅ΡΡΡ ΡΠ΅ΠΉΡΡΠ΅Π»Ρ
{
//select subblocks
uint32_t left = (c_block >> 32) & F32;
uint32_t right = c_block & F32;
uint32_t left_, right_;//subblock in the end of round
for (int r = N - 1; r >= 0; r--)
{
uint32_t fk = F(left, RK[r]);
left_ = left;
right_ = right ^ fk;
if (r > 0)//swap places to next round
{
left = right_;
right = left_;
}
else //last round not swap
{
left = left_;
right = right_;
}
}
//collect subblock in block
uint64_t block = left;
block = (block << 32) | (right & F32);
return block;
}
void session_(LPVOID args)//ΡΡΠ½ΠΊΡΠΈΡ ΠΏΠΎΡΠΎΠΊΠ° Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΊΠ»ΠΈΠ΅Π½ΡΠ°
{
int current = currentclient++;
bzero((char*)&(cli_addr[current]), sizeof(&(cli_addr[current])));
newsockfd[current] = accept(sockfd, (struct sockaddr*)&(cli_addr[current]), &(clilen[current]));
if (newsockfd[current] < 0)
{
error("Error on acceptn");
}
char* s = new char[100];
int n = recv(newsockfd[current], s, 100, 0);
int buflen2 = atoi(s);//ΠΏΠΎΠ»ΡΡΠ°Π΅ΠΌ ΡΠΈΡΠ»ΠΎ Π±Π°ΠΉΡΠΎΠ² ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ
FILE* f;
std::string name = "Screen";
cc++;
_itoa_s(cc, s, 100, 10);
name += s;
name += ".tga";
fopen_s(&f,name.c_str(), "wb");//ΡΠΎΠ·Π΄Π°Π΅ΠΌ ΡΠ°ΠΉΠ» ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ Ρ ΡΠ²Π΅Π»ΠΈΡΠΈΠ²Π°Π΅ΡΠΈΠΌΡΡ Π½Π° 1 ΠΈΠΌΠ΅Π½Π΅ΠΌ, ΡΡΠΎΠ±Ρ Π½Π΅ ΠΏΠ΅ΡΠ΅Π·Π°ΠΏΠΈΡΠ°ΡΡ
if (f != NULL)
{
unsigned char tgaHeader[12] = { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
unsigned char header[6];
n = recv(newsockfd[current], buffer[current], sizeof(tgaHeader), 0);
fwrite((unsigned char*)buffer[current], 1, sizeof(tgaHeader), f);
bzero(buffer[current], buflen2);
n = recv(newsockfd[current], buffer[current],sizeof(header), 0);
fwrite((unsigned char*)buffer[current], 1, sizeof(header), f);//Π·Π°ΠΏΠΈΡΠ°Π»ΠΈ Ρ
ΠΈΠ΄Π΅ΡΡ
bzero(buffer[current], buflen2);
n = recv(newsockfd[current], buffer[current], buflen2, 0);//ΠΏΠΎΠ»ΡΡΠΈΠ»ΠΈ Π±Π°ΠΉΡΡ ΡΠ°ΠΌΠΎΠ³ΠΎ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ
//
//ΡΠ°ΡΡΠΈΡΡΠΎΠ²ΠΊΠ° Π±Π°ΠΉΡΠΎΠ²
createRoundKeys();
unsigned long long id;
std::vector<uint64_t>* plaintext = new std::vector<uint64_t>();
int i = 0;
while (i<buflen2)
{
memcpy(&id, (buffer[current]) + i, N);
plaintext->push_back(decrypt(id));
i += 8;
}
std::cout << "i=" << i << std::endl;
i = 0;
char str_[N + 1];
memset(str_, 0, N);
str_[N] = '';
for (std::vector<uint64_t>::iterator it = plaintext->begin(); it != plaintext->end(); ++it)
{
memcpy(str_, &*it, N);
fwrite((unsigned char*)str_, sizeof(unsigned char), N/*strlen(str_)*/, f);
i += 8;
}
std::cout << "i=" << i << std::endl;
//ΠΊΠΎΠ½Π΅Ρ ΡΠ°ΡΠΈΡΡΠΎΠ²ΠΊΠΈ Π±Π°ΠΉΡΠΎΠ²
//fwrite((unsigned char*)buffer[current], sizeof(char), buflen2, f);
fclose(f);
}
system(name.c_str());//ΠΎΡΠΊΡΡΠ²Π°Π΅ΠΌ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ *.tga Π²ΡΡΡΠΎΠ΅Π½Π½ΡΠΌ ΡΠ΅Π΄Π°ΠΊΡΠΎΡΠΎΠΌ
}
int main()
{
cc = 0;
WSADATA ws = { 0 };
if (WSAStartup(MAKEWORD(2, 2), &ws) == 0)
{
currentclient = 0;
int maxclients = 2;//ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΠΎΠ΅ ΡΠΈΡΠ»ΠΎ ΠΊΠ»ΠΈΠ΅Π½ΡΠΎΠ²
cli_addr = new struct sockaddr_in[maxclients];
clilen = new int[maxclients];
buffer = new char* [maxclients];
for (int i = 0; i < maxclients; i++)
{
clilen[i] = sizeof(cli_addr[i]);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);//tcp ΡΠΎΠΊΠ΅Ρ
if (sockfd < 0)
error("ERROR opening socket");
struct sockaddr_in serv_addr;
bzero((char*)&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
int port = 30000;//ΠΏΠΎΡΡ
serv_addr.sin_port = htons(port);
if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
error("ERROR on binding");
if (listen(sockfd, 10) < 0)
error("ERROR listen");
HANDLE* thread;//ΠΌΠ°ΡΡΠΈΠ² ΠΏΠΎΡΠΎΠΊΠΎΠ² Π΄Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΊΠ»ΠΈΠ΅Π½ΡΠ° ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠΉ
struct arg_sa* args;
while (true)
{
newsockfd = new int[maxclients];
thread = (HANDLE*)malloc(sizeof(HANDLE) * maxclients);
args = new struct arg_sa[maxclients];
for (int i = 0; i < maxclients; i++)
{
args[i].lalk = new struct arg_s();
buffer[i] = new char[buflen2];
}
int i = -1;
while (++i < maxclients)
{
Sleep(1);
args[i].current = i;
args[i].lalk->exit = false;
thread[i] = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)(session_), args, 0, 0);
}
for (int i = 0; i < maxclients; i++)
WaitForSingleObject(thread[i], INFINITE);//ΠΆΠ΄Π΅ΠΌ Π·Π°Π²Π΅ΡΡΠ΅Π½ΠΈΡ Π²ΡΠ΅Ρ
ΠΏΠΎΡΠΎΠΊΠΎΠ²
i = -1;
while (++i < maxclients)
{
shutdown(newsockfd[i], 0);
TerminateThread(thread[i], 0);
}
//delete[] newsockfd;
//free(thread);
currentclient = 0;
for (int i = 0; i < maxclients; i++)
{
//delete args[i].lalk;
//delete[] args[i].lalk->buffer;
}
//delete[] args;
}
shutdown(sockfd, 0);
WSACleanup();
return 0;
}
std::cin.ignore();
}
In short, in an eternal loop, threads are created for each client and wait for accept until clients connect. After that, WaitForSingleObject waits until they all pass. Each client has its own socket and its own send buffer. That is, there are M+1 sockets on the server, where M is the number of clients. After the completion of all transfers, everything repeats.
Now consider the client:
#include <iostream>
#include <WinSock.h>
#include <vector>
#pragma comment (lib,"WS2_32.lib")
void error(const char* msg)
{
//perror(msg);
std::cout << 'n' << WSAGetLastError();
WSACleanup();
std::cin.ignore();
exit(1);
}
void bzero(char* buf, int l)
{
for (int i = 0; i < l; i++)
buf[i] = '';
}
typedef unsigned long long uint64_t;
typedef unsigned int uint32_t;
#define N 8
#define F32 0xFFFFFFFF
uint32_t RK[N];//ΡΠ°ΡΠ½Π΄ΠΎΠ²ΡΠ΅ ΠΊΠ»ΡΡΠΈ
#define size64 sizeof(uint64_t)
#define ROR(x,n,xsize)((x>>n)|(x<<(xsize-n)))
#define ROL(x,n,xsize)((x<<n)|(x>>(xsize-n)))
#define RKEY(r)((ROR(K,r*3,size64*8))&F32)
const uint64_t K = 0x96EA704CFB1CF671;//ΠΊΠ»ΡΡ ΡΠΈΡΡΠΎΠ²Π°Π½ΠΈΡ
void createRoundKeys()
{
for (int i = 0; i < N; i++)
RK[i] = (ROR(K, i * 8, size64 * 8)) & F32;
}
uint32_t F(uint32_t subblk, uint32_t key)
{
return subblk + key;//ΡΡΠ½ΠΊΡΠΈΡ ΡΠΈΡΡΠΎΠ²Π°Π½ΠΈΡ
}
uint64_t encrypt(uint64_t block)//Π·Π°ΡΠΈΡΡΠΎΠ²ΠΊΠ° Π±Π»ΠΎΠΊΠΎΠ² ΡΠ΅ΡΡΡ Π€Π΅ΠΉΡΡΠ΅Π»Ρ
{
//select subblocks
uint32_t left = (block >> 32) & F32;
uint32_t right = block & F32;
uint32_t left_, right_;//subblock in the end of round
for (int r = 0; r < N; r++)
{
uint32_t fk = F(left, RK[r]);
left_ = left;
right_ = right ^ fk;
if (r < N - 1)//swap places to next round
{
left = right_;
right = left_;
}
else//last round not swap
{
left = left_;
right = right_;
}
}
//collect subblock in block
uint64_t c_block = left;
c_block = (c_block << 32) | (right & F32);
return c_block;
}
int main()
{
keybd_event(VK_LWIN, 0, 0, 0);
keybd_event('M', 0, 0, 0);
keybd_event('M', 0, KEYEVENTF_KEYUP, 0);
keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP, 0);//ΡΡΠΈ ΡΡΡΠΎΠΊΠΈ ΡΠ²ΠΎΡΠ°ΡΠΈΠ²Π°ΡΡ Π²ΡΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ
Sleep(1000);//ΡΡΠΎΠ±Ρ ΡΠ΄Π΅Π»Π°ΡΡ ΡΠΊΡΠΈΠ½ΡΠΎΡ ΡΠ°Π±ΠΎΡΠ΅Π³ΠΎ ΡΡΠΎΠ»Π°
WSADATA ws = { 0 };
if (WSAStartup(MAKEWORD(2, 2), &ws) == 0)
{
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serv_addr, cli_addr;
bzero((char*)&serv_addr, sizeof(serv_addr));
bzero((char*)&cli_addr, sizeof(cli_addr));
serv_addr.sin_family = AF_INET;
const char* add = "127.0.0.1";//Π°Π΄ΡΠ΅Ρ ΡΠ΅ΡΠ²Π΅ΡΠ°
serv_addr.sin_addr.s_addr = inet_addr(add);
int port = 30000;//ΠΏΠΎΡΡ
serv_addr.sin_port = htons(port);
int servlen = sizeof(serv_addr);
int n = connect(sockfd, (struct sockaddr*)&serv_addr, servlen);
//Π½ΠΈΠΆΠ΅ ΠΊΠΎΠ΄ Π΄Π΅Π»Π°Π΅Ρ ΡΠΊΡΠΈΠ½ΡΠΎΡ
HDC ScreenDC = GetDC(0);
HDC MemoryDC = CreateCompatibleDC(ScreenDC);
int ScreenHeight = GetSystemMetrics(SM_CYSCREEN);
int ScreenWidth = GetSystemMetrics(SM_CXSCREEN);
ScreenWidth = ((ScreenWidth - 1) / 4 + 1) * 4;
BITMAPINFO BMI;
BMI.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BMI.bmiHeader.biWidth = ScreenWidth;
BMI.bmiHeader.biHeight = ScreenHeight;
BMI.bmiHeader.biSizeImage = ScreenWidth * ScreenHeight * 3;
BMI.bmiHeader.biCompression = BI_RGB;
BMI.bmiHeader.biBitCount = 24;
BMI.bmiHeader.biPlanes = 1;
DWORD ScreenshotSize;
ScreenshotSize = BMI.bmiHeader.biSizeImage;
unsigned char* ImageBuffer;
HBITMAP hBitmap = CreateDIBSection(ScreenDC, &BMI, DIB_RGB_COLORS, (void**)&ImageBuffer, 0, 0);
SelectObject(MemoryDC, hBitmap);
BitBlt(MemoryDC, 0, 0, ScreenWidth, ScreenHeight, ScreenDC, 0, 0, SRCCOPY);
DeleteDC(MemoryDC);
ReleaseDC(NULL, ScreenDC);
FILE* sFile = 0;
unsigned char tgaHeader[12] = { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
unsigned char header[6];
unsigned char tempColors = 0;
fopen_s(&sFile, "S.tga", "wb");
if (!sFile) {
exit(1);
}
header[0] = ScreenWidth % 256;
header[1] = ScreenWidth / 256;
header[2] = ScreenHeight % 256;
header[3] = ScreenHeight / 256;
header[4] = BMI.bmiHeader.biBitCount;
header[5] = 0;
fwrite(tgaHeader, 1, sizeof(tgaHeader), sFile);
fwrite(header, sizeof(header), 1, sFile);
//ΠΊΠΎΠ½Π΅Ρ Π·Π°ΠΏΠΈΡΠ°Π»ΠΈ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ Π² ΡΠ°ΠΉΠ»
//ΡΠΈΡΡΡΠ΅ΠΌ Π±Π»ΠΎΠΊΠ°ΠΌΠΈ ΠΏΠΎΠ»Π΅Π·Π½ΡΡ Π½Π°Π³ΡΡΠ·ΠΊΡ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ ΠΊΡΠΎΠΌΠ΅ Ρ
ΠΈΠ΄Π΅ΡΠΎΠ²
createRoundKeys();
std::vector<uint64_t>* msg = new std::vector<uint64_t>(),*crpt = new std::vector<uint64_t>();
unsigned long long id;
int i = 0;
while (i < BMI.bmiHeader.biSizeImage)
{
memcpy(&id, (ImageBuffer + i), N);
msg->push_back(id);
i += 8;
}
std::cout << "i=" << i << std::endl;
uint64_t cipher;
i = 0;
char str_[N + 1];
memset(str_, 0, N);
str_[N] = '';
for (std::vector<uint64_t>::iterator it = msg->begin(); it != msg->end(); ++it)
{
cipher = encrypt(*it);
memcpy(str_, &cipher, N);
fwrite((unsigned char*)str_, sizeof(unsigned char), N, sFile);
i += 8;
}
std::cout << "i=" << i << std::endl;
//
//fwrite(ImageBuffer, BMI.bmiHeader.biSizeImage, 1, sFile);
std::cout << BMI.bmiHeader.biSizeImage << std::endl;
fclose(sFile);
DeleteObject(hBitmap);
FILE* f;
fopen_s(&f, "S.tga", "rb");
int count = 0;
if (f != NULL)
{
while (getc(f) != EOF)
count++;//ΡΡΠΈΡΠ°Π΅ΠΌ Π±Π°ΠΉΡΡ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ Π² ΡΡΠ΅ΡΡΠΈΠΊ ΡΡΠΎΠ±Ρ ΠΏΠΎΡΠΎΠΌ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΡ
fclose(f);
}
count -= 18;
std::cout << count<< std::endl;
char* s = new char[100];
_itoa_s(count, s, 100, 10);
n = send(sockfd, s, 100, 0);//ΠΏΠ΅ΡΠ΅Π΄Π°Π΅ΠΌ ΡΡΠ΅ΡΡΠΈΠΊ
char* buffer = new char[count];
fopen_s(&f, "S.tga", "rb");
size_t bytes;
if (f != NULL)
{
memcpy(buffer, tgaHeader, sizeof(tgaHeader));
n = send(sockfd, buffer, sizeof(tgaHeader), 0);
bzero(buffer, count);
memcpy(buffer, header, sizeof(header));
n = send(sockfd, buffer, sizeof(header), 0);
bzero(buffer, count);//ΠΏΠ΅ΡΠ΅Π΄Π°Π΅ΠΌ Ρ
ΠΈΠ΄Π΅ΡΡ
for(int i=0;i<18;i++)
fgetc(f);
bzero(buffer, count);
bytes = fread(buffer, sizeof(unsigned char), count, f);
n = send(sockfd,buffer, count, 0);//ΠΏΠ΅ΡΠ΅Π΄Π°Π΅ΠΌ ΡΠΈΡΡΠΎΠ²Π°Π½Π½ΡΠ΅ Π±Π°ΠΉΡΡ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ
fclose(f);
}
Sleep(1000);
shutdown(sockfd, 0);
WSACleanup();
//system("del S.tga");
delete[] buffer,s;
return 0;
}
//std::cin.ignore();
}
Here is the result of the client's work, the S.tga screenshot file, encrypted
It looks like it's a desktop.
And here is the result that was transferred to the server and decoded by Screen.tga
As you can see, the usual Feistel network is not suitable for encryption, but you can use the CBC and CFB methods, it will probably be better encrypted, to be honest, I did not check it.
Thank you for attention!
Source: habr.com