Crypto chat with VK API Technologies
Омаров Магомед Шамилович
Студент 4го курса У-351гр
ФКТВТиЭ
Крипто чат с использованием технологий VK API
Crypto chat with VK API Technologies
Аннотация. Статья посвящена описанию программной системы, разработанной на языке Java для мобильной платформы Android, для шифрования передаваемой текстовой информации с использованием ассиметричного или симметричного алгоритма шифрования с использованием технологий VK API
Ключевые слова: Информационная безопасность, крипто чат, криптография, шифрование, RSA, XOR, DES, AES
Description. The article describes a software system developed in Java for the Android mobile platform, which encrypts the messages using asymmetrical or symmetrical encryption with VK API technologies
Keywords: Information security, crypto chat, cryptography, encryption, RSA, XOR, DES, AES
Программная система состоит из сервера VK и клиентского мобильного приложения, разработанного для платформы Android.
API (application programming interface) — это посредник между разработчиком приложений и какой-либо средой, с которой это приложение должно взаимодействовать. API упрощает создание кода, поскольку предоставляет набор готовых классов, функций или структур для работы с имеющимися данными.
1. Методы и объекты
API ВКонтакте — это интерфейс, который позволяет получать информацию из базы данных vk.com с помощью http-запросов к специальному серверу. Вам не нужно знать в подробностях, как устроена база, из каких таблиц и полей каких типов она состоит — достаточно того, что API-запрос об этом «знает». Синтаксис запросов и тип возвращаемых ими данных строго определены на стороне самого сервиса.
Например, для получения данных о пользователе с идентификатором 210700286 необходимо составить запрос такого вида:
https://api.vk.com/method/users.get?user_id=210700286&v=5.52
Рассмотрим отдельно все его составляющие.
https:// — протокол соединения.
api.vk.com/methods — адрес API-сервиса.
users.get — название метода API ВКонтакте. Методы представляют собой условные команды, которые соответствуют той или иной операции с базой данных — получение информации, запись или удаление. Например, users.get — метод для получения информации о пользователе, video.add — метод для добавления видеозаписи в свой список, likes.delete — метод для удаления отметки «Мне нравится».
Все методы разделены на секции. Например, для работы с сообществами Вам нужны методы секции groups, для работы с фотографиями — photos, и так далее. Полный список методов по секциям доступен на этой странице.
?user_id=210700286&v=5.52 — параметры запроса. После названия метода нужно передать его входные данные (если они есть) — как обычные GET-параметры в http-запросе. В нашем примере мы сообщаем серверу, что хотим получить данные о пользователе с id=210700286 и формат этих данных должен соответствовать версии API 5.52 (о версиях мы еще поговорим позже). Входные параметры всегда перечислены на странице с описанием метода.
В ответ сервер вернет JSON-объект с запрошенными данными (или сообщение об ошибке, если что-то пошло не так). JSON — это формат записи данных в виде пар «имя свойства»: «значение».
Ответ на наш запрос выглядит так:
{"response":[{"id":210700286,"first_name":"Lindsey","last_name":"Stirling"}]}
Структура ответа каждого метода также строго задана, и при работе с API Вы заранее знаете, что в поле id придет число, а в поле first_name — строка. Такие правила оговариваются на страницах с описанием метода и соответствующих объектов, которые он возвращает в ответе. Например, users.get — здесь описаны входные параметры метода и структура его ответа, а здесь — user подробно расписано каждое поле объекта из ответа.
Объект из ответа может быть не уникален для конкретного метода. Например, объект пользователя с набором полей, содержащих данные о его образовании, возрасте, интересах, может возвращаться в ответе от методов users.get, users.search, groups.getMembers и еще нескольких.
ВКонтакте — социальная сеть, где есть дружеские связи, настройки приватности и даже черные списки. Многое зависит от того, кто просматривает страницу: кто-то увидит на ней всю ту же информацию, что и владелец, а кто-то — лишь общедоступные данные.
В API этот принцип сохраняется. Если Вы скрыли список своих групп от не-друзей, то и через API Ваши не-друзья не должны его увидеть. Поэтому почти все методы требуют авторизации пользователя перед началом работы.
В общем случае для идентификации в API используется специальный ключ доступа, который называется access_token. Токен — это строка из цифр и латинских букв, которую Вы передаете на сервер вместе с запросом. Из этой строки сервер получает всю нужную ему информацию. Есть разные способы получения токена, более того, он может быть выдан не только пользователю, но и сообществу, и сразу всему приложению.
В данной программой системы использован самый простой способ Implicit flow.
Программный код для получения токена имеет след вид :
- public class auth extends AppCompatActivity {
- …
- WebView wb = (WebView) findViewById(R.id.web);
- WebSettings webSettings = wb.getSettings();
- webSettings.setJavaScriptEnabled(true);
- SimpleWebViewClient webViewClient = new SimpleWebViewClient();
- wb.setWebViewClient(webViewClient);
- wb.loadUrl("https://oauth.vk.com/authorize?client_id=5213947&redirect_uri=oauth.vk.com/blank.html&display=mobile&scope=4098&&response_type=token&v=5.58");
- …}
После перехода по данному адресу браузер созданный внутри приложения выведет сообщение , предостерегающее нас от передачи адреса браузера на сторонние сервисы , т.к. есть риск утери данных
Для получения токена из адресной строки используется функция GetToken.
- public void GetToken (View view) {
- WebView wb = (WebView) findViewById(R.id.web);
- token = wb.getUrl().toString().split("=")[1].toString().split("&")[0];
- Snackbar.make(view, "Получаю токен...", Snackbar.LENGTH_LONG).setAction("Action", null).show();
5. wb.setVisibility(View.INVISIBLE); }
Токен — это Ваш ключ доступа. При выполнении определенных условий человек, получивший Ваш токен, может нанести существенный ущерб Вашим данным и данным других людей. Поэтому очень важно не передавать свой токен третьим лицам. В данной системе токен хранится в глобальной переменной token .
Поле expires_in содержит время жизни токена в секундах. 86400 секунд — это ровно сутки. Через сутки полученный токен перестанет действовать, для продолжения работы нужно будет получить новый. Есть возможность получить токен без срока действия — для этого в scope добавьте значение offline.
Поле user_id содержит id пользователя, для которого получен токен.
Для загрузки списка друзей используем подкласс супер класса AssyncTask GetFriends.
- class GetFriend extends AsyncTask<String, Void, String> {
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- }
- @Override
- protected String doInBackground(String... params) {
- StringBuilder sb = new StringBuilder();
- try {
- HttpURLConnection connection = (HttpURLConnection) newURL(params[0]).openConnection();
- connection.connect();
- InputStream inputStream =connection.getInputStream();
- BufferedReader reader = newBufferedReader(new InputStreamReader(inputStream));
- String line = null;
- while ((line = reader.readLine()) !=null) {
- sb.append(line);
- }
- TimeUnit.SECONDS.sleep(2);
- } catch (InterruptedException e) {
- e.printStackTrace();
- } catch (MalformedURLException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- return sb.toString();
- }
- @Override
- protected void onPostExecute(String result) {
- String friendsCount = null;
- String posts = "";
- super.onPostExecute(result);
- try {
- int i;
- JSONObject obj = new JSONObject(result);
- friendsCount = obj.getJSONObject("response").getString("count");
- JSONArray arr = obj.getJSONObject("response").getJSONArray("items");
- friends = new String[Integer.parseInt(friendsCount)];
- for (i = 0; i < arr.length(); i++) {
- friends[i] =
- "id" +arr.getJSONObject(i).getString("id")
- + ":" + '\n' +arr.getJSONObject(i).getString("first_name")
- + " " +arr.getJSONObject(i).getString("last_name")
- + " <<" +arr.getJSONObject(i).getString("online") + ">>";
- friends[i] =friends[i].replace("<<1>>", " online");
- friends[i] =friends[i].replace("<<0>>", " offline");
- }
- showDialog(55);
- } catch (JSONException e) {
- e.printStackTrace();
- }
- }
- }
Список друзей записан в глобальный массив friends.
Для получение истории сообщений используется подкласс суперкласса AssyncTask getHistory.
1.
- class GetHistory extends AsyncTask<String, Void, String> {
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- }
- @Override
- protected String doInBackground(String... params) {
- StringBuilder sb = new StringBuilder();
- try {
- HttpURLConnection connection = (HttpURLConnection) newURL(params[0]).openConnection();
- connection.connect();
- InputStream inputStream =connection.getInputStream();
- BufferedReader reader = newBufferedReader(newInputStreamReader(inputStream));
- String line = null;
- while ((line =reader.readLine()) != null) {
- sb.append(line);
- }
- TimeUnit.SECONDS.sleep(2);
- } catch (InterruptedException e) {
- e.printStackTrace();
- } catch (MalformedURLException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- return sb.toString();
- }
- @Override
- protected void onPostExecute(String result) {
- String messegesCount = null;
- String posts = "";
- super.onPostExecute(result);
- ////парсинг
- try {
- JSONObject obj = new JSONObject(result);
- messegesCount = obj.getJSONObject("response").getString("count");
- JSONArray arr = obj.getJSONObject("response").getJSONArray("items");
- for (int i = 0; i <arr.length(); i++) {
- String msg =arr.getJSONObject(i).getString("body");
- if(msg.contains(":CRMSGWLDMR:")) {
- msg =decrypt(msg.replace(":CRMSGWLDMR:", "") , key).replace("+"," ");
- }
- if(arr.getJSONObject(i).getString("from_id").equals(user_id))
- posts +=" Собеседник:" + msg + "\n";
- else
- posts += "Я" + msg + "\n";
- }
- } catch (JSONException e) {
- e.printStackTrace();
- }
- if (result.contains("count")) {
- final AlertDialog.Builder builder = new AlertDialog.Builder(auth.this);
- if (!user_id_name.equals(null))
- builder.setTitle("История" + user_id_name);
На 50й строчке кода идет проверка на наличие в полученном сообщение флага «:CRMSGWLDMR:» он используется для того чтобы систем а поняла что сообщение зашифровано и есть необхожимость расшифровать сообщение (52-я строчка кода )
Для получение истории сообщений используется подкласс суперкласса AssyncTask sendMessage.
- class SendMessage extends AsyncTask<String, Void, String> {
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- }
- @Override
- protected String doInBackground(String... params) {
- StringBuilder sb = new StringBuilder();
- try {
- HttpURLConnection connection = (HttpURLConnection) newURL(params[0]).openConnection();
- connection.connect();
- InputStream inputStream =connection.getInputStream();
- BufferedReader reader = newBufferedReader(newInputStreamReader(inputStream));
- String line = null;
- while ((line =reader.readLine()) != null) {
- sb.append(line);
- }
- TimeUnit.SECONDS.sleep(2);
- } catch (InterruptedException e) {
- e.printStackTrace();
- } catch (MalformedURLException e){
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- return sb.toString();
- }
- @Override
- protected void onPostExecute(String result) {
- GetHistory("https://api.vk.com/method/messages.getHistory?offset=0&count=5&user_id="+ user_id + "&access_token=" + token + "&v=5.59");
- }
- }
Для шифрования и дешифрованния передаваемых или получаемых сообщений в данной программной системе используются модули с алгоритмом шифрования XOR. Стоит отметить что данные модули легко заменяемы в программном коде .
- public static String xor_encrypt(String message, String key){
- try {
- if (message==null || key==null ) return null;
- char[] keys=key.toCharArray();
- char[] mesg=message.toCharArray();
- BASE64Encoder encoder = new BASE64Encoder();
- int ml=mesg.length;
- int kl=keys.length;
- char[] newmsg=new char[ml];
- for (int i=0; i<ml; i++){
- newmsg[i]=(char)(mesg[i]^keys[i%kl]);
- }
- mesg=null;
- keys=null;
- String temp = newString(newmsg);
- return new String(newBASE64Encoder().encodeBuffer(temp.getBytes()));
- }
- catch ( Exception e ) {
- return null;
- }
- }
- public static String xor_decrypt(String message, String key){
- try {
- if (message==null || key==null ) return null;
- BASE64Decoder decoder = new BASE64Decoder();
- char[] keys=key.toCharArray();
- message = new String(decoder.decodeBuffer(message));
- char[] mesg=message.toCharArray();
- int ml=mesg.length;
- int kl=keys.length;
- char[] newmsg=new char[ml];
- for (int i=0; i<ml; i++){
- newmsg[i]=(char)(mesg[i]^keys[i%kl]);
- }
- mesg=null; keys=null;
- return new String(newmsg);
- }
- catch ( Exception e ) {
- return null;
- }
- }