Шифрование данных с помощью алгоритма AES-256 на Java

В данной статье говорится о том, как осуществить шифрование с помощью алгоритма AES-256 на Java.

Как уже говорилось ранее, AES-256 представляет собой алгоритм шифрования с симметричным ключом. Это означает, что для шифрования и дешифрования используется один и тот же криптографический ключ.

Алгоритм используется в военных целях, используется для шифрования передачи сообщений по открытым каналам, для шифрования файлов. Так же с помощью AES-256 злоумышленники шифруют файлы на компьютерах «жертв», используя его в целях вымогательства денег (программы-вымогатели). Расшифровать данные возможно только лишь зная ключ.

Как зашифровать и дешифровать с помощью AES-256 средствами Java

Класс для шифрования и дешифрования потока байт на Java может выглядеть следующим образом:

package Crypto;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.NoSuchAlgorithmException;

/**
 * Created by administrator on 16.03.2018.
 */
public class Aes256Class {

    private SecretKey secretKey;

    public Aes256Class() {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            keyGenerator.init(256);

            this.secretKey = keyGenerator.generateKey();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }

    public byte[] makeAes(byte[] rawMessage, int cipherMode){
        try {
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(cipherMode, this.secretKey);
            byte [] output = cipher.doFinal(rawMessage);
            return output;

        } catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
}

В конструкторе Aes256Class() инициализируется объект keyGenerator, которому задается размер ключа 256 бит. Далее с помощью функции generateKey() генерируется случайный ключ с заданным размером (256 бит).

В функцию makeAes передается исходный массив байт и режим работы объекта класса Cipher:

Cipher.ENCRYPT_MODE либо Cipher.DECRYPT_MODE.

В классе с функцией main можем вызвать описанные выше методы следующим образом:

package Main;

import Crypto.Aes256Class;

import javax.crypto.Cipher;

/**
 * Created by administrator on 16.03.2018.
 */
public class Main {
    public static void main(String[] args) {
        Aes256Class aes256 = new Aes256Class();

        String mes = "Hello";

        for (int i = 0; i < 3; i++) {
            byte[] shifr = aes256.makeAes(mes.getBytes(), Cipher.ENCRYPT_MODE);
            System.out.println(new String(shifr));
            byte[] src = aes256.makeAes(shifr, Cipher.DECRYPT_MODE);
            System.out.println(new String(src));
        }
    }
}

Получаем следующий результат:

Java AES

Объясняю, зачем я зациклил выполнение функции makeAes.

Благодаря шифрованию и дешифрованию одного и того же текста несколько раз, можно увидеть, что шифр получается один и тот же, что не есть хорошо.

Это уже может дать злоумышленнику какую-то информацию. Представьте себе, что ваша система отправляет в канал сообщение, например, «Опасность». И отправляет его 10 раз подряд.

Если каким-то образом сеть будет прослушиваться, перехваченное сообщение будет иметь 10 раз подряд один и тот же шифр.

Для того, чтобы одно и то же сообщение имело разный шифр, можно использовать соль.

Не смотря на то, что AES-256 не поддерживает концепцию соли, ничего не мешает сгенерировать случайный массив байт из, например, 8 элементов (получим 8 байтовую соль) и добавить к сообщению.

При дешифрации сообщения можно просто выбросить эти самые последние 8 байт.

Функция приобретет следующий вид:

public byte[] makeAes(byte[] rawMessage, int cipherMode){
    try {
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(cipherMode, this.secretKey);
        byte [] output = cipher.doFinal(rawMessage);
        if(cipherMode == Cipher.DECRYPT_MODE){
            byte[]result = new byte[output.length-8];
            //Выбрасываем последние 8 байт
            System.arraycopy(output, 0, result, 0, output.length-8);
            return result;
        }
        return output;

    } catch (Exception e){
        e.printStackTrace();
        return null;
    }
}

Вызываем функцию makeAes в методе main:

package Main;

import Crypto.Aes256Class;

import javax.crypto.Cipher;
import java.security.SecureRandom;

/**
 * Created by administrator on 16.03.2018.
 */
public class Main {
    public static void main(String[] args)  {
        Aes256Class aes256 = new Aes256Class();

        //Массив для соли
        byte[] salt = new byte[8];
        //Исходное сообщение
        String mes = "Опасность";

        //Выполним операцию 10 раз
        for (int i = 0; i < 10; i++) {
            //Генерация соли
            SecureRandom random = new SecureRandom();
            random.nextBytes(salt);

            //Преобразуем исходный текст в поток байт и добавим полученную соль
            byte[]srcMessage = mes.getBytes();
            byte[]fullsrcMessage = new byte[srcMessage.length+8];
            System.arraycopy(srcMessage, 0, fullsrcMessage, 0, srcMessage.length);
            System.arraycopy(salt, 0, fullsrcMessage, srcMessage.length, salt.length);

            //Шифруем
            byte[] shifr = aes256.makeAes(fullsrcMessage, Cipher.ENCRYPT_MODE);
            System.out.println(new String(shifr));
            //Дешифруем
            byte[] src = aes256.makeAes(shifr, Cipher.DECRYPT_MODE);
            System.out.println(new String(src));
        }
    }
}

В этом примере шифруем и дешифруем 10 раз сообщение «Опасность». Получаем следующий результат:

Java AES with salt

Как видим, одно и то же сообщение, но шифр разный. Однако здесь есть нюанс, если обратите внимание, то увидите, что первая часть сообщения всегда одна и та же. Это и имелось ввиду, когда говорилось, что AES не поддерживает концепцию соли.

Можете попробовать прикреплять соль к началу сообщения, к середине, или использовать несколько солей. Однако Принцип Керкгоффса подразумевает, что злоумышленнику известен алгоритм шифрования и какую-то закономерность он все же найдет.

Нужна очень «хитрая» реализация алгоритма, задача которой максимально запутать злоумышленника.

Когда я писал статью о реализации данного алгоритма на C# я положился на реализацию AES от Microsoft.

Реализация AES-256 на Java с помощью фреймворка Spring Security

В данной статье приведу реализацию AES с помощью Java фреймворка Spring Security:

package Main;

import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.security.crypto.encrypt.TextEncryptor;
import org.springframework.security.crypto.keygen.KeyGenerators;

/**
 * Created by administrator on 16.03.2018.
 */
public class Main {

    public static void main(String[] args)  {
        final String password = "Here is the password";
        String textToEncrypt = "Hello";

        for (int i = 0; i < 10; i++) {
            String salt = KeyGenerators.string().generateKey();
            TextEncryptor encryptor = Encryptors.text(password, salt);
            String cipherText = encryptor.encrypt(textToEncrypt);
            String decryptedText = encryptor.decrypt(cipherText);
            System.out.println("Src: " + textToEncrypt);
            System.out.println("Cipher: " + cipherText);
            System.out.println("Decrypted: " + decryptedText);
            System.out.println("__________________");
        }
    }
}

Результат работы кода:

AES Spring Security

Как видите, реализация AES-256 от Spring Security не подводит. Как знать, что это именно AES? В официальной документации есть такое упоминание:

The "standard" encryption method is 256-bit AES (Spring Crypto Module)

P.S. Расшифровать подобные сообщения относительно разумными средствами (взломать шифр), не имея ключа в настоящее время не представляется возможным.

Можете попробовать перебором паролей, если у вас в запасе есть сотни миллиардов лет.

P.P.S. Так же на сайте имеется запись о шифровании данных с помощью AES-256 на C#.

Создание собственного ClassLoader в Java

Описываю создание собственного ClassLoader в Java.

Собственные загрузчики классов нужны для реализации рефлексии в Java.

Мы загружаем файл-класс «на лету» и исполняем его методы.

Как загрузить Java класс на лету

Чтобы загрузить Java класс нам нужен подготовленный файл с байт-кодом, имеющий расширение .class, однако в приведенном ниже примере никаких заготовок я не использую, и создаю класс так же — «на лету», используя Java компилятор (javac) и класс Runtime в коде программы.

import java.io.File;
import java.io.FileNotFoundException;
import java.lang.reflect.Method;
import java.util.Properties;

public class Main {
    public static void main(String[] args) throws Exception {
        
        //Получаем доступ ко всем Properties
        Properties p = System.getProperties();
        //Получаем разделитель, используемый в операционной системе
        String sep = p.getProperty("file.separator");
        //Получаем путь к папке JRE
        String jrePath = p.getProperty("java.home");

        //Выполняем необходимые манипуляции для получения пути к файла javac (в моем случае javac.exe)
        int lastIndex = jrePath.lastIndexOf(sep);
        String javac = jrePath.substring(0, lastIndex) + sep + "bin" + sep + "javac";
        if(p.getProperty("sun.desktop").equals("windows"))
            javac+=".exe";
        else javac+=".sh";

        //Проверяем, существует ли такой файл (javac.exe)
        File jc = new File(javac);
        if(!jc.isFile())
            throw new FileNotFoundException("Компилятор по адресу "+ jc.getAbsolutePath() +" недоступен ");

        System.out.println("Компилируем " + jc.getAbsolutePath() + " " + file.getAbsolutePath());

        //Запускаем компилятор javac, чтобы получить байт код внешнего класса
        Process p1 = Runtime.getRuntime().exec(javac+" "+file.getAbsolutePath());

        //Если javac завершился с ошибкой, выбрасываем Exception (здесь он самописный)
        //т.к. мне необходимо было проверять синтаксис класса, который подключался.
        //Таким образом, если возникала ошибка компиляции, то процесс p1 мог вернуть текст
        //ошибки (поток байт) с помощью функции getErrorStream()
        if(p1.waitFor()!=0)
            try {
                throw new MyClassCompilationException("Ошибка компиляции", p1);
            } catch (MyClassCompilationException e) {
                e.printStackTrace();
                return;
            }

        //Здесь мы уже имеем созданный файл с байт-кодом
        System.out.println("Компиляция завершена");

        //Формируем абсолютный путь к файлу с байт-кодом
        int pointIndex = file.getAbsolutePath().lastIndexOf(".");
        String absulutePatch = file.getAbsolutePath().substring(0, pointIndex);

        //Объявляем MyClassLoader. Класс ClassLoader является абстрактным
        //поэтому необходимо его переопределить (каким образом, будет показано ниже)
        MyClassLoader loader = new MyClassLoader();

        //Объявляем переменную типа Class.
        Class cl = loader.findClass(absulutePatch);
        System.out.println(cl);

        //Получаем метод m1 из загруженного класса
        Method method = cl.getMethod("m1", new Class[] {String.class, int.class});
        System.out.println(method);
        //Выполняем метод m1. Нельзя забывать про метод newInstance(), если метод динамический.
        method.invoke(cl.newInstance(), new Object[]{"Test", 8});

        //Выполняем метод m2. Он статический, поэтому newInstance() в методе invoke писать не надо
        Method method2 = cl.getMethod("m2", new Class[]{String.class});
        method2.invoke(cl, "QWERRTY");
    }
}

Загружаемый класс выглядит таким образом:

public class Hello {
  public void m1(String text, int c) {
    for(int i = 0; i < c; i++) {
      System.out.println(text + " Hi"+i);
    }
  }

  public static void m2(String text) {
    System.out.println(text + " from static method");
  }
}

Класс MyClassLoader будет загружать созданный класс в статический контекст, чтобы мы могли обращаться к его методам.

MyClassLoader имеет следующий вид:

import java.io.*;

public class MyClassLoader extends ClassLoader {

    //Переопределяем метод findClass, которому надо передать путь к файлу с расширением .class
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        //Проверяем, существует ли такой файл
        File f = new File(name+".class");
        if(!f.isFile())
            throw new ClassNotFoundException("Нет такого класса " + name);

        InputStream ins = null;

        try{
            //С помощью потока считываем файл в массив байт
            ins = new BufferedInputStream(new FileInputStream(f));
            byte[]b = new byte[(int)f.length()];
            ins.read(b);
            //С помощью функции defineClass загружаем класс
            Class c = defineClass("Hello", b, 0, b.length);
            return c;

        }catch (Exception e){
            e.printStackTrace();
            throw new ClassNotFoundException("Проблемы с байт кодом");
        }
        finally {
            try {
                ins.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

С первом блоке кода, начиная со строки 54 мы начинаем работать с загруженным классом.

Результат работы приведенного выше кода выглядит следующим образом:

ClassLoader

Здесь приведена загрузка простого класса с простыми методами, однако ничего не мешает загрузить туда более емкие классы.

Если будет нужно, опубликую, как взаимодействовать с внешним процессом так, чтобы получать от него сообщение об ошибке в виде потока байт.

В данном случае этим процессов является компилятор javac, который возвращает ошибку, если компилируемый код написан неправильно.

Загрузка изображений в ImageView в Android в отдельном потоке (Java)

Сегодня покажу как загружать изображения с сервера и помещать их в ImageView в Android в отдельном потоке.

Как загрузить изображение с сервера

Начать стоит с того, что изображение должно загружаться в отдельном потоке, а помещаться в контрол в главном потоке.

Чтобы реализовать данную задачу, необходимо создать класс, являющийся наследником класса AsyncTask.

В данном случае я создал класс ImageLoadAsync, наследующий AsyncTask<ImageView, Void, Bitmap>.

package razilov.pro.rus_butik;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.widget.ImageView;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class ImageLoadAsync extends AsyncTask<ImageView, Void, Bitmap> {
    String Url;
    ImageView imgV;

    /* onPostExecute и onPreExecute имеют доступ к UI. Можно взаимодействовать с элементами
     * формы (передав их перед этим в конструкторе) */

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        super.onPostExecute(bitmap);
        imgV.setImageBitmap(bitmap);
    }

    /* Все ссылки и прочий хлам можно передать в конструкторе */
    public ImageLoadAsync(String url, ImageView imgV){
        this.Url = url;
        this.imgV = imgV;
    }


    private Bitmap download_Image(String url) {
        try {
            URL urlstring = new URL(url);
            HttpURLConnection connection = (HttpURLConnection) urlstring.openConnection();
            connection.setDoInput(true);
            connection.connect();
            InputStream input = connection.getInputStream();
            Bitmap myBitmap = BitmapFactory.decodeStream(input);
            return myBitmap;
        } catch (MalformedURLException mex) {
            System.out.println(mex);
            return null;
        } catch (IOException ioex) {
            System.out.println(ioex);
            return null;
        }
    }

    @Override
    protected Bitmap doInBackground(ImageView... params) {
        return download_Image(this.Url);
    }
}

В этом коде в отдельном потоке подгружается Bitmap и передается в поток, имеющий доступ к интерфейсу, откуда не составляет труда присвоить этот Bitmap контролу ImageView.

 

Шифрование данных с помощью алгоритма AES-256 (C#)

В текущей статье напишу два метода для шифрования потока байт с помощью алгоритма AES-256, и расшифровки обратно в массив байт.

Что такое AES-256

AES-256 представляет собой алгоритм шифрования с симметричным ключом. Это означает, что для шифрования и дешифрирования используется один и тот же криптографический ключ.

Symmetric key, симметричный ключ

Алгоритм может пригодиться где угодно — от шифрования файлов до защиты данных, передаваемых по открытым каналам.

Реализация алгоритма на C#

Для реализации алгоритма нужно подключить библиотеку System.Security.Cryptography и указать пространство имен:

using System.Security.Cryptography;

Далее напишем метод для шифрования данных:

/// <summary>
/// Шифрует исходное сообщение AES ключом (добавляет соль)
/// </summary>
/// <param name="src"></param>
/// <returns></returns>
public static byte[] ToAes256(string src)
{
  //Объявляем объект класса AES
  Aes aes = Aes.Create();
  //Генерируем соль
  aes.GenerateIV();
  //Присваиваем ключ. aeskey - переменная (массив байт), сгенерированная методом GenerateKey() класса AES
  aes.Key = aeskey;
  byte[] encrypted;
  ICryptoTransform crypt = aes.CreateEncryptor(aes.Key, aes.IV);
  using (MemoryStream ms = new MemoryStream())
  {
    using (CryptoStream cs = new CryptoStream(ms, crypt, CryptoStreamMode.Write))
    {
      using (StreamWriter sw = new StreamWriter(cs))
      {
        sw.Write(src);
      }
    }
    //Записываем в переменную encrypted зашиврованный поток байтов
    encrypted = ms.ToArray();
  }
  //Возвращаем поток байт + крепим соль
  return encrypted.Concat(aes.IV).ToArray();
}

Метод, для дешифрирования потока байтов:

/// <summary>
/// Расшифровывает криптованного сообщения
/// </summary>
/// <param name="shifr">Шифротекст в байтах</param>
/// <returns>Возвращает исходную строку</returns>
public static string FromAes256(byte[] shifr)
{
  byte[] bytesIv = new byte[16];
  byte[] mess = new byte[shifr.Length - 16];

  //Списываем соль
  for (int i = shifr.Length - 16, j = 0; i < shifr.Length; i++, j++)
    bytesIv[j] = shifr[i];

  //Списываем оставшуюся часть сообщения
  for (int i = 0; i < shifr.Length - 16; i++)
    mess[i] = shifr[i];

  //Объект класса Aes
  Aes aes = Aes.Create();
  //Задаем тот же ключ, что и для шифрования
  aes.Key = aeskey;
  //Задаем соль
  aes.IV = bytesIv;
  //Строковая переменная для результата
  string text = "";
  byte[] data = mess;
  ICryptoTransform crypt = aes.CreateDecryptor(aes.Key, aes.IV);

  using (MemoryStream ms = new MemoryStream(data))
  {
    using (CryptoStream cs = new CryptoStream(ms, crypt, CryptoStreamMode.Read))
    {
      using (StreamReader sr = new StreamReader(cs))
      {
        //Результат записываем в переменную text в вие исходной строки
        text = sr.ReadToEnd();
      }
    }
  }
  return text;
}

Зашифрованное сообщение можно передавать в открытый канал. При перехвате злоумышленники обнаружат лишь шифр, с которым мало что смогут сделать.

Стоит отметить, что сам ключ стоит передавать по закрытому каналу либо используя алгоритмы шифрования с ассиметричным ключом. Например RSA.

Ключ AES шифруется открытым ключом RSA, передается в открытый канал, далее вторая сторона расшифровывает ключ AES закрытым ключом RSA.

Во время передачи шифра в открытый канал может произойти MITM атака — попытка подмены сообщения и т.п. злоумышленником.

Для проверки подлинности сообщения используйте цифровую подпись для каждого сообщения (вычисляется хеш, проверяемый получателем для проверки подлинности сообщения).

Данным методом можно воспользоваться, чтобы зашифровать данные другими алгоритмами с симметричным ключом, например TripleDes.

Как прочитать Excel с помощью OleDB (C#)

В этой статье опишу как прочитать Excel с помощью OleDB.

Иногда бывает нужно вытянуть таблицу из Excel документа, записать в DataTable для последующей обработки.

Не всегда это удобно делать с помощью циклов, поэтому будем считывать таблицы, содержащиеся в документе и запрашивать из них данные с помощью SQL запросов.

oledb C#

Определение строки подключения

Для разных версий Excel будут свои строки подключения.

Строка подключения для Excel 2007 и более новых версий

//Можно использовать, если количество строк менее 65536
Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties='Excel 8.0;HDR=Yes'

//Если строк больше 65536
Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0}; Extended Properties="Excel 12.0 Xml;HDR=YES";

Для работы с данными версиями необходимо установить Microsoft Access Database Engine 2010 Redistributable.

Так же C# может выбрасывать исключения по поводу недостающих драйверов. В этом случае необходимо скачать соответствующие драйверы с сайта Microsoft.

Строка подключения для более ранних версий

Строка подключения для Excel версии 2003 может иметь такой вид:

Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties='Excel 8.0;HDR=Yes'

Если C# выбросит исключение, скачайте недостающий драйвер, Visual Studio подскажет, какой.

Как сделать SQL запрос из таблицы Excel

Для выполнения SQL запроса нужно найти таблицу в документе и выполнить к ней запрос:

//Объявляем OleDB соединение
using(OleDbConnection conn = new OleDbConnection(conStr))
{
  //Открываем подключение
  conn.Open();
  //Запрашиваем таблицы
  DataTable schemaTable = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "TABLE" });
  DataRow schemaRow = schemaTable.Rows[0];
  //Получаеи имя таблицы
  string sheet = schemaRow["TABLE_NAME"].ToString();
  //Объявляем команду
  OleDbCommand com = conn.CreateCommand();
  //Создаем SQL запрос
  com.CommandText = "SELECT * FROM [" + sheet + "]";
  //Выполняем SQL запрос
  OleDbDataReader reader = com.ExecuteReader();
  //Записываем результат в DataTable
  DataTable table = new DataTable();
  table.Load(reader);
  //Выводим DataTable в таблицу на форму (если нужно)
  gridControl1.DataSource = table;
}

На этом шаге мы имеем считанные данные из Excel документа в DataTable.

После обработки данных можно сохранить данные в другой документ или оформить сводную таблицу.

Как работать с Excel с помощью C# обсуждалось ранее.

Так же можете посмотреть, как работать со сводными таблицами в Excel с помощью C# и редактора VBA, встроенного в Excel.

Создаем сводные таблицы на C# в Excel

Сегодня программируем сводные таблицы на C#. Для этого надо будет иметь заготовленную таблицу с данными (обычную).

Заготовленная таблица будет иметь данные о телефонных звонках.

Пример сводной таблицы:

Сводные таблицы на C#

Открываем документ и создаем диапазоны для сводной таблицы

Откроем Excel документ с таблицей:

ex.Workbooks.Open("Excel.xlsx",
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);

Далее создаем диапазон из одной ячейки, в которой указываем, куда будет помещаться сводная таблица:

Excel.Range pivotDestination = sheetPivot.get_Range("A1", "A1");

Объявляем диапазон сводной таблицы:

Excel.Range all = sheetPivot.get_Range("A1", "D1000");

Пишем код создания сводной таблицы

Код для создания сводной таблицы из обычной таблицы с данными имеет такой вид:

workBook.PivotTableWizard(
  Excel.XlPivotTableSourceType.xlDatabase,
all,                        //Таблица, на основе которой строим сводную
pivotDestination,              //Где сохранять
"NumberA",                        //Название таблицы
true,
true,
true,
true,
Type.Missing,
Type.Missing,
false,
false,
Excel.XlOrder.xlDownThenOver,
0,
Type.Missing,
Type.Missing
);

Распределяем поля сводной таблицы

Далее надо определить, какие поля будут отвечать за значения, за столбцы и строки.

Для этого создаем экземпляры полей на основе заголовков столбцов таблицы:

//Создаем поля таблицы
Excel.PivotTable pivotTable = (Excel.PivotTable)sheetPivot.PivotTables("Number");
Excel.PivotField Y = ((Excel.PivotField)pivotTable.PivotFields("Y"));
Excel.PivotField M = ((Excel.PivotField)pivotTable.PivotFields("M"));
Excel.PivotField Src_country = ((Excel.PivotField)pivotTable.PivotFields("Country"));
Excel.PivotField Operator_Name = ((Excel.PivotField)pivotTable.PivotFields("Operator"));
Excel.PivotField Minutes = ((Excel.PivotField)pivotTable.PivotFields("Minutes"));

Распределяем поля:

//Расставляем строки, столбцы и данные
Y.Orientation = Excel.XlPivotFieldOrientation.xlColumnField;
M.Orientation = Excel.XlPivotFieldOrientation.xlColumnField;
Src_country.Orientation = Excel.XlPivotFieldOrientation.xlRowField;
Operator_Name.Orientation = Excel.XlPivotFieldOrientation.xlRowField;
Minutes.Orientation = Excel.XlPivotFieldOrientation.xlDataField;
//Функция, применяемая к данным - есть еще количество, MAX, MIN и т.д.
Minutes.Function = Excel.XlConsolidationFunction.xlSum;

Создаем сводную таблицу без исходной таблицы с данными

Описываю, как создать сводную таблицу на основе SQL-запроса к БД.

Для этого понадобится библиотека ADODB (adodb.dll можно скачать в интернете).

Подключим adodb.dll к проекту:

using ADODB;

Объявим строку подключения (драйвер SQLOLEDB.1):

Provider=SQLOLEDB.1;Password=123456;Persist Security Info=True;User ID=sqlserver;Initial Catalog=DBName;Data Source=serverNameOrIp

Далее пишем код для работы со сводной таблицей:

//Создаем подключение и открываем его
ADODB.Connection sql = new Connection();
sql.CursorLocation = ADODB.CursorLocationEnum.adUseClient;
sql.ConnectionString = conString;
sql.Open();

//Объявляем команду. Здесь функция getQuery() возвращает SQL-запрос
ADODB.Command cmd = new Command();
cmd.CommandType = CommandTypeEnum.adCmdText;
cmd.CommandText = getQuery();
cmd.CommandTimeout = 5000;
cmd.ActiveConnection = sql;

//Объявляем Recordset и выполняем запрос, сохраняя в него данные
ADODB.Recordset rs = new Recordset();
rs.Open(cmd, Type.Missing, ADODB.CursorTypeEnum.adOpenForwardOnly, ADODB.LockTypeEnum.adLockReadOnly, 1);
//тут хранится результат запроса

Excel.PivotCache pc = wb.PivotCaches().Add(
Excel.XlPivotTableSourceType.xlExternal
, Type.Missing);

pc.Recordset = rs;
//Ссылка на все сводные таблицы на листе
Excel.PivotTables pts = (Excel.PivotTables)sheet.GetType().InvokeMember("PivotTables", System.Reflection.BindingFlags.GetProperty, null, sheet, null);
//Создаем новую сводную таблицу
Excel.PivotTable pt = pts.Add(pc, sheet.get_Range("A3", Type.Missing), "NumA", Type.Missing, Excel.XlPivotTableVersionList.xlPivotTableVersionCurrent);

//Распределяю поля и настраиваю форматы полей
((Excel.PivotField)pt.PivotFields("Year")).Orientation = Excel.XlPivotFieldOrientation.xlColumnField;
((Excel.PivotField)pt.PivotFields("Month")).Orientation = Excel.XlPivotFieldOrientation.xlColumnField;
((Excel.PivotField)pt.PivotFields("Country")).Orientation = Excel.XlPivotFieldOrientation.xlRowField;
((Excel.PivotField)pt.PivotFields("Operator")).Orientation = Excel.XlPivotFieldOrientation.xlRowField;
((Excel.PivotField)pt.PivotFields("Minutes")).Orientation = Excel.XlPivotFieldOrientation.xlDataField;
((Excel.PivotField)pt.PivotFields("Сумма по полю Minutes")).Function = Excel.XlConsolidationFunction.xlSum;
((Excel.PivotField)pt.PivotFields("Сумма по полю Minutes")).NumberFormat = "# ##0";
((Excel.PivotField)pt.PivotFields("Operator")).ShowDetail=false;
((Excel.PivotField)pt.PivotFields("Country")).ShowDetail = false;

pt.TableStyle2 = "PivotStyleMedium13";     //Стиль сводной таблицы. Взял через VBA

При работе со сводными таблицами так же окажет помощь встроенный в Excel редактор VBA, о котором речь шла в теме Работа с Excel с помощью C# (Microsoft.Office.Interop.Excel).

Работа с Excel с помощью C# (Microsoft.Office.Interop.Excel)

Оставляю заметку по работе с Excel с помощью C#.

Привожу фрагменты кода, которые искал когда-то сам для работы с Excel документами.

Наработки очень пригодились в работе для формирования отчетности.

Прежде всего нужно подключить библиотеку Microsoft.Office.Interop.Excel.

Подключение Microsoft.Office.Interop.Excel
Visual Studio здесь довольно старой версии. Если у вас версия новая, отличаться будет только вид окна.

Далее создаем псевдоним для работы с Excel:

using Excel = Microsoft.Office.Interop.Excel;

//Объявляем приложение
Excel.Application ex = new Microsoft.Office.Interop.Excel.Application();

//Отобразить Excel
ex.Visible = true;

//Количество листов в рабочей книге
ex.SheetsInNewWorkbook = 2;

//Добавить рабочую книгу
Excel.Workbook workBook = ex.Workbooks.Add(Type.Missing);

//Отключить отображение окон с сообщениями
ex.DisplayAlerts = false;                                       

//Получаем первый лист документа (счет начинается с 1)
Excel.Worksheet sheet = (Excel.Worksheet)ex.Worksheets.get_Item(1);

//Название листа (вкладки снизу)
sheet.Name = "Отчет за 13.12.2017";

//Пример заполнения ячеек
for (int i = 1; i <= 9; i++)
{
  for (int j = 1; j < 9; j++)
  sheet.Cells[i, j] = String.Format("Boom {0} {1}", i, j);
}

//Захватываем диапазон ячеек
Excel.Range range1 = sheet.get_Range(sheet.Cells[1, 1], sheet.Cells[9, 9]);

//Шрифт для диапазона
range1.Cells.Font.Name = "Tahoma";
//Размер шрифта для диапазона
range1.Cells.Font.Size = 10;

//Захватываем другой диапазон ячеек
Excel.Range range2 = sheet.get_Range(sheet.Cells[1, 1], sheet.Cells[9, 2]);
range2.Cells.Font.Name = "Times New Roman";

//Задаем цвет этого диапазона. Необходимо подключить System.Drawing
range2.Cells.Font.Color = ColorTranslator.ToOle(Color.Green);
//Фоновый цвет
range2.Interior.Color = ColorTranslator.ToOle(Color.FromArgb(0xFF, 0xFF, 0xCC));

Расстановка рамок.

Расставляем рамки со всех сторон:

range2.Borders.get_Item(Excel.XlBordersIndex.xlEdgeBottom).LineStyle = Excel.XlLineStyle.xlContinuous;
range2.Borders.get_Item(Excel.XlBordersIndex.xlEdgeRight).LineStyle = Excel.XlLineStyle.xlContinuous;
range2.Borders.get_Item(Excel.XlBordersIndex.xlInsideHorizontal).LineStyle = Excel.XlLineStyle.xlContinuous;
range2.Borders.get_Item(Excel.XlBordersIndex.xlInsideVertical).LineStyle = Excel.XlLineStyle.xlContinuous;
range2.Borders.get_Item(Excel.XlBordersIndex.xlEdgeTop).LineStyle = Excel.XlLineStyle.xlContinuous;

Цвет рамки можно установить так:

range2.Borders.Color = ColorTranslator.ToOle(Color.Red);

Выравнивания в диапазоне задаются так:

rangeDate.VerticalAlignment = Excel.XlVAlign.xlVAlignCenter;
rangeDate.HorizontalAlignment = Excel.XlHAlign.xlHAlignLeft;

Формулы

Определим задачу: получить сумму диапазона ячеек A4:A10.

Для начала снова получим диапазон ячеек:

Excel.Range formulaRange = sheet.get_Range(sheet.Cells[4, 1], sheet.Cells[9, 1]);

Далее получим диапазон вида A4:A10 по адресу ячейки ( [4,1]; [9;1] ) описанному выше:

string adder = formulaRange.get_Address(1, 1, Excel.XlReferenceStyle.xlA1, Type.Missing, Type.Missing);

Теперь в переменной adder у нас хранится строковое значение диапазона ( [4,1]; [9;1] ), то есть A4:A10.

Вычисляем формулу:

//Одна ячейка как диапазон
Excel.Range r = sheet.Cells[10, 1] as Excel.Range;
//Оформления
r.Font.Name = "Times New Roman";
r.Font.Bold = true;
r.Font.Color = ColorTranslator.ToOle(Color.Blue);
//Задаем формулу суммы
r.Formula = String.Format("=СУММ({0}", adder);

Выделение ячейки или диапазона ячеек

Так же можно выделить ячейку или диапазон, как если бы мы выделили их мышкой:

sheet.get_Range("J3", "J8").Activate();
//или
sheet.get_Range("J3", "J8").Select();
//Можно вписать одну и ту же ячейку, тогда будет выделена одна ячейка.
sheet.get_Range("J3", "J3").Activate();
sheet.get_Range("J3", "J3").Select();

Авто ширина и авто высота

Чтобы настроить авто ширину и высоту для диапазона, используем такие команды:

range.EntireColumn.AutoFit(); 
range.EntireRow.AutoFit();

Получаем значения из ячеек

Чтобы получить значение из ячейки, используем такой код:

//Получение одной ячейки как ранга
Excel.Range forYach = sheet.Cells[ob + 1, 1] as Excel.Range;
//Получаем значение из ячейки и преобразуем в строку
string yach = forYach.Value2.ToString();

Добавляем лист в рабочую книгу

Чтобы добавить лист и дать ему заголовок, используем следующее:

var sh = workBook.Sheets;
Excel.Worksheet sheetPivot = (Excel.Worksheet)sh.Add(Type.Missing, sh[1], Type.Missing, Type.Missing);
sheetPivot.Name = "Сводная таблица";

Добавление разрыва страницы

//Ячейка, с которой будет разрыв
Excel.Range razr = sheet.Cells[n, m] as Excel.Range;
//Добавить горизонтальный разрыв (sheet - текущий лист)
sheet.HPageBreaks.Add(razr); 
//VPageBreaks - Добавить вертикальный разрыв

Сохраняем документ

ex.Application.ActiveWorkbook.SaveAs("doc.xlsx", Type.Missing,
  Type.Missing, Type.Missing, Type.Missing, Type.Missing, Excel.XlSaveAsAccessMode.xlNoChange,
  Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);

Как открыть существующий документ Excel

ex.Workbooks.Open(@"C:\Users\Myuser\Documents\Excel.xlsx",
  Type.Missing, Type.Missing, Type.Missing, Type.Missing,
  Type.Missing, Type.Missing, Type.Missing, Type.Missing,
  Type.Missing, Type.Missing, Type.Missing, Type.Missing,
  Type.Missing, Type.Missing);

Комментарии

При работе с Excel с помощью C# большую помощь может оказать редактор Visual Basic, встроенный в Excel.

Для этого в настройках ленты надо добавить пункт «Разработчик». Далее начинаем запись макроса, производим действия и останавливаем запись.

Далее заходим в редактор Visual Basic и смотрим код, который туда записался:

Vusial Basic (VBA)

Например:

Sub Макрос1()
'
' Макрос1 Макрос
'

'
    Range("E88").Select
    ActiveSheet.ListObjects.Add(xlSrcRange, Range("$A$1:$F$118"), , xlYes).Name = _
        "Таблица1"
    Range("A1:F118").Select
    ActiveSheet.ListObjects("Таблица1").TableStyle = "TableStyleLight9"
    Range("E18").Select
    ActiveWindow.SmallScroll Down:=84
End Sub

В данном макросе записаны все действия, которые мы выполнили во время его записи. Эти методы и свойства можно использовать в C# коде.

Данный метод так же может оказать помощь в формировании относительных формул, например, выполнить сложение чисел, находящиеся слева от текущей ячейки на 4 столбца, и т.п. Пример:

//Складываем значения предыдущих 12 ячеек слева
rang.Formula = "=СУММ(RC[-12]:RC[-1])";

Так же во время работы может возникнуть ошибка: метод завершен неверно. Это может означать, что не выбран лист, с которым идет работа.

Чтобы выбрать лист, выполните sheetData.Select(Type.Missing); где sheetData это нужный лист.

Настройка майнера Minerd на Linux Ubuntu 16.04 (Пул minergate.com)

Оставляю заметку о том, как запустить майнинг XMR на Linux Ubuntu.

Git должен быть уже установлен.

sudo apt-get update
sudo apt-get -y install git make automake gcc libcurl4-openssl-dev libmysqlclient-dev
git clone https://github.com/wolf9466/cpuminer-multi
cd cpuminer-multi
./autogen.sh
CFLAGS="-march=native" ./configure --disable-aes-ni
make

Список команд для запуска майнинга других валют можно найти здесь.

Команда для запуска майнига

./minerd -a cryptonight -o stratum+tcp://xmr.pool.minergate.com:45560 -u user@myemail.ru -p x

Так же на сайте есть статья о запуске майнинга на Linux Fedora.

Ручная настройка сети и MySQL сервера на Ubuntu Server 16.04

Заметка по установке Linux Ubuntu Server 16.04 на сервер HP  (думаю, что подобным образом его можно поставить на любой другой сервер) и о том, как настроить MySQL сервер.

Во время установки выбираем установить LAMP, SSH server, Samba сервер.

Система спросит, надо будет выбрать перечисленное пробелами.

DHCP сервера у меня не было, поэтому отметил «Настроить сеть вручную». Настройкой занялся после установки ОС.

Установленный сервер Ubuntu выглядит таким образом:

Installed ubuntu16.04

Настройка сети

Логинимся в систему и выполняем следующую команду:

sudo lshw -C network

Получаем примерно следующее:

lshw

Здесь нас интересует имя сетевого адаптера logical name: enp0s3.

Далее выполняем следующую команду:

sudo nano /etc/network/interfaces

В данном файле прописываем настройки сети для адаптера enp0s3:

interfaces

(Настройки у вас должны быть, конечно, свои)

Сохраняем файл комбинацией клавиш Ctrl+O и выходим из программы с помощью Ctrl+X.

Перезапускаем сеть:

sudo /etc/init.d/networking restart

На этом этапе сеть должна уже работать и соседние компьютеры пинговаться.

Настройка MySQL

Далее настраиваю MySQL, так как необходима возможность подключения к MySQL серверу с других компьютеров.

Так же необходимо изменить движок таблиц по умолчанию с InnoDB на MyISAM (в моем случае), так как транзакций не будет, а на движке MyISAM все будет работать быстрее. Для этого необходимо присвоить переменной default_storage_engine значение MyISAM в файле mysqld.cnf.

Пишем в консоли:

sudo nano /etc/mysql/mysql.conf.d/mysql.cnf

Прописываем переменную в конфиге и комментируем строку bind-address = 127.0.0.1 чтобы мы могли подключаться к MySQL с других компьютеров:

mysqld conf

По умолчанию MySQL сервер не позволит пользователю root подключиться с других компьютеров.

Создадим нового пользователя и дадим ему полные права. Для этого запустим mysql client на сервере либо по SSH:

mysql -h 127.0.0.1 -u root -p

Введем пароль, установленный для root и получим следующее:

mysql

На этом этапе и можем писать SQL запросы. В конце каждого запроса ставим символ ‘;‘ и нажимаем Enter.

Создаем нового пользователя MySQL со всеми правами

Создаем нового пользователя (здесь myuser11 это пароль):

CREATE USER 'myuser'@'localhost' IDENTIFIED BY 'myuser11';
CREATE USER 'myuser'@'%' IDENTIFIED BY 'myuser11';

Даем все права:

GRANT ALL ON *.* TO 'myuser'@'localhost';
GRANT ALL ON *.* TO 'myuser'@'%';

Обновляем все права доступа командой:

FLUSH PRIVILEGES;

На этом этапе мы имеем настроенный MySQL сервер с админом myuser, который может подключаться к MySQL с других компьютеров.

Подключаемся к удаленному рабочему столу Linux Ubuntu 16.04 из Windows (VNC)

В данной записи рассматриваем возможность подключения к удаленному рабочему столу на Linux Ubuntu.

В Ubuntu за доступ к удаленным рабочим столам отвечает программа Vino.

Ищем ее в поиске и запускаем. Получаем такое окно:

ubuntu vino

Можно настроить программу под ваши нужды и подключаться к данной машине по протоколу VNC.

Но есть важное замечание. Из систем Windows подключение выполнить нельзя.

Чтобы подключиться из Windows необходимо выполнить следующие действия:

  1. Установить dconf-editor;
  2. Отключить принудительное шифрование;
  3. Подготовить программу для подключения к удаленным рабочим столам (я использовал mRemoteNG).

Шаг 1 (Открываем терминал и пишем команду)

sudo apt install dconf-editor

Шаг 2 (Запускаем dconf и убираем галочку с require-encryption)

Убираем галочку с require-encryption

Шаг 3 (Подключаемся к удаленному рабочему столу)

Подключаемся к удаленному рабочему с помощью программы mRemoteNG.

Подключение через mRemoteNG