Говоря о семи распространенных алгоритмах шифрования и их реализации

интервью задняя часть Микросервисы Архитектура алгоритм

предисловие

цифровой подписи,шифрование информацииЭто технология, которая часто используется во фронтенде и бэкенде.Сценарии приложений включают в себя вход пользователя в систему, транзакцию, обмен информацией,oauthПодождите, разные сценарии приложений также должны будут использовать разные алгоритмы шифрования подписи или должны соответствовать разнымАлгоритм шифрования подписидля достижения целей бизнеса. Вот краткое введение в несколько распространенных алгоритмов шифрования подписи и приложений в некоторых типичных сценариях.

текст

1. Цифровая подпись

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

Уведомление: на картинкепроцесс шифрованияотличается отшифрование с открытым ключом,БолееВведение нажмите здесь.подписатьНаиболее фундаментальное использование состоит в том, чтобы иметь возможность однозначноПодтвердите личность отправителя,не допуститьатака «человек посередине»,CSRF Подделка междоменной идентификации. На основании этого вСертификация устройства,Аутентификация пользователя,сторонняя сертификацияи другие системы сертификации будут использоватьсяАлгоритм подписи(могут быть различия в том, как каждый реализован).

2. Шифрование и дешифрование

2.1 Шифрование

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

2.2. Расшифровка

шифрованиеизобратный процессзарасшифровать, Собираюсьинформация о кодированиипреобразовать висходные данныепроцесс.

3. Симметричное и асимметричное шифрование

алгоритм шифрованияСимметричное шифрованиеиАсимметричное шифрование, в котором шифрование и дешифрование алгоритма симметричного шифрованиятот же ключ, ключ шифрования и расшифровка алгоритма асимметричного шифрованияразные ключи, кроме того, есть классключ не требуетсяизхеш-алгоритм.

ОбщийСимметричное шифрованиеАлгоритм в основном имеетDES,3DES,AESи т. д., общийАсимметричные алгоритмыЕстьRSA,DSAЖдать,хеш-алгоритмЕстьSHA-1,MD5Ждать.

3.1 Симметричное шифрование

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

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

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

3.2 Асимметричное шифрование

Алгоритм асимметричного шифрования,Также известен какалгоритм шифрования с открытым ключом. Для этого требуется два ключа, один из которых называетсяоткрытый ключ (public key),которыйоткрытый ключ, другой звонилзакрытый ключ (private key),которыйзакрытый ключ.

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

  1. При использованииоткрытый ключк даннымшифровать, только с соответствующимзакрытый ключталантрасшифровать.

  2. При использованиизакрытый ключк даннымшифровать, только с соответствующимоткрытый ключталантрасшифровать.

пример: Сторона А создаетпара ключейи возьмем один из них какоткрытый ключОткройте для других, получите открытый ключСторона БИспользуйте этот ключ для секретной информациишифроватьЗатем отправьте его Стороне А, и Сторона А воспользуется другим, сохраненным ею.закрытый ключ (закрытый ключ),правильношифрованиеинформация послерасшифровать.

4. Общие алгоритмы шифрования подписи

4.1 Алгоритм MD5

MD5используетсяхэш-функция, его типичное применение — генерировать часть информацииСводка информации,отзащищенный от несанкционированного доступа. строго говоря,MD5не видАлгоритм шифрованияноАлгоритм дайджеста. Независимо от того, как долго ввод,MD5выведет длину128bitsстрока (обычно используется16 базаВыражается как32символы).

public static final byte[] computeMD5(byte[] content) {
    try {
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        return md5.digest(content);
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException(e);
    }
}

4.2 Алгоритм SHA1

SHA1да иMD5как популярныйалгоритм дайджеста сообщения,Тем не мениеSHA1СравниватьMD5изболее безопасный. Для длин менее2 ^ 64битовое сообщение,SHA1произведет160немногоДайджест сообщения. на основеMD5,SHA1Функция сводки информации , инеобратимый(в общем), можно использовать для проверкицелостность файлаа такжецифровой подписисцена.

public static byte[] computeSHA1(byte[] content) {
    try {
        MessageDigest sha1 = MessageDigest.getInstance("SHA1");
        return sha1.digest(content);
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException(e);
    }
}

4.3 Алгоритм HMAC

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

HMAC отправительиполучательобаkeyделать расчеты без этогоkeyтретье лицо, этоНевозможно рассчитатьиз правильногохэш-значениеДа так, чтоПредотвращение подделки данных.

package net.pocrd.util;
import net.pocrd.annotation.NotThreadSafe;
import net.pocrd.define.ConstField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Arrays;


@NotThreadSafe
public class HMacHelper {
    private static final Logger logger = LoggerFactory.getLogger(HMacHelper.class);
    private Mac mac;

    /**
     * MAC算法可选以下多种算法
     * HmacMD5/HmacSHA1/HmacSHA256/HmacSHA384/HmacSHA512
     */
    private static final String KEY_MAC = "HmacMD5";
    public HMacHelper(String key) {
        try {
            SecretKey secretKey = new SecretKeySpec(key.getBytes(ConstField.UTF8), KEY_MAC);
            mac = Mac.getInstance(secretKey.getAlgorithm());
            mac.init(secretKey);
        } catch (Exception e) {
            logger.error("create hmac helper failed.", e);
        }
    }
    public byte[] sign(byte[] content) {
        return mac.doFinal(content);
    }
    
    public boolean verify(byte[] signature, byte[] content) {
        try {
            byte[] result = mac.doFinal(content);
            return Arrays.equals(signature, result);
        } catch (Exception e) {
            logger.error("verify sig failed.", e);
        }
        return false;
    }
}

Заключение теста:HMACПример алгоритма находится вмногопоточная средаследующийнебезопасный. Но нужномногопоточный доступКогда вспомогательный класс для синхронизации использоватьThreadLocalзана кэш потокаЭкземпляр может избежать операций блокировки.

4.4 Алгоритм AES/DES/3DES

AES,DES,3DESобасимметрияизблочный алгоритм шифрования,Шифрование и дешифрованиеПроцессобратимый. Обычно используютсяAES128,AES192,AES256(устанавливается по умолчаниюJDKПока не поддерживаетсяAES256, необходимо установить соответствующийjceПатч для обновленияjce1.7,jce1.8).

4.4.1 Алгоритм DES

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

DESалгоритм шифрованияключсохранять конфиденциальность иобщедоступный алгоритм, включая алгоритмы шифрования и дешифрования. Таким образом, только мастер и отправительтот же ключлюдей могут интерпретироватьDESДанные зашифрованного текста, зашифрованные алгоритмом шифрования. Таким образом, расшифровкаDESАлгоритм шифрования на самом делеКодировка ключа поиска. за56битовая длинаключНапример, если использоватьИсчерпывающий методДля поиска количество операций2 ^ 56Второсортный.

4.4.2 Алгоритм 3DES

основан наDESизСимметричный алгоритм,правильночасть данныхиспользоватьтри разных ключапровестиТройное шифрование,более высокая прочность.

4.4.3 Алгоритм AES

AESАлгоритм шифрования находится в криптографииРасширенный стандарт шифрования, алгоритм шифрования используетСимметричная блочная система шифрования, минимальная поддерживаемая длина ключа128немного,192немного,256Бит, длина пакета128бит, алгоритм должен легко реализовываться в различных аппаратных и программных средствах. Этот алгоритм шифрования используется федеральным правительством США.Стандарт блочного шифрования.

AESсам должен заменитьDESиз,AESс лучшимбезопасность,эффективныйигибкость.

import net.pocrd.annotation.NotThreadSafe;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;

@NotThreadSafe
public class AesHelper {
    private SecretKeySpec keySpec;
    private IvParameterSpec iv;

    public AesHelper(byte[] aesKey, byte[] iv) {
        if (aesKey == null || aesKey.length < 16 || (iv != null && iv.length < 16)) {
            throw new RuntimeException("错误的初始密钥");
        }
        if (iv == null) {
            iv = Md5Util.compute(aesKey);
        }
        keySpec = new SecretKeySpec(aesKey, "AES");
        this.iv = new IvParameterSpec(iv);
    }

    public AesHelper(byte[] aesKey) {
        if (aesKey == null || aesKey.length < 16) {
            throw new RuntimeException("错误的初始密钥");
        }
        keySpec = new SecretKeySpec(aesKey, "AES");
        this.iv = new IvParameterSpec(Md5Util.compute(aesKey));
    }

    public byte[] encrypt(byte[] data) {
        byte[] result = null;
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance("AES/CFB/NoPadding");
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
            result = cipher.doFinal(data);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return result;
    }

    public byte[] decrypt(byte[] secret) {
        byte[] result = null;
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance("AES/CFB/NoPadding");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
            result = cipher.doFinal(secret);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return result;
    }

    public static byte[] randomKey(int size) {
        byte[] result = null;
        try {
            KeyGenerator gen = KeyGenerator.getInstance("AES");
            gen.init(size, new SecureRandom());
            result = gen.generateKey().getEncoded();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return result;
    }
}

4.5 Алгоритм RSA

RSAАлгоритм шифрования в настоящее время является наиболее влиятельнымалгоритм шифрования с открытым ключом, и обычно считается текущимЛучшая схема с открытым ключомодин.RSAявляется первым, который используется одновременношифрованиеицифровой подписиалгоритм, который можетсопротивлениеизвестно до сих порВсе атаки на пароль,БылISOРекомендуется в качестве стандарта шифрования данных с открытым ключом.

RSA Алгоритм шифрованияОснованный на очень простом факте теории чисел: объединении двух большихпростое числоУмножать легко, но хочется сделатьфакторизациячрезвычайно сложно, поэтомупродуктпубличный какключ шифрования.

import net.pocrd.annotation.NotThreadSafe;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.KeyFactory;
import java.security.Security;
import java.security.Signature;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

@NotThreadSafe
public class RsaHelper {
    private static final Logger logger = LoggerFactory.getLogger(RsaHelper.class);
    private RSAPublicKey publicKey;
    private RSAPrivateCrtKey privateKey;

    static {
        Security.addProvider(new BouncyCastleProvider()); //使用bouncycastle作为加密算法实现
    }

    public RsaHelper(String publicKey, String privateKey) {
        this(Base64Util.decode(publicKey), Base64Util.decode(privateKey));
    }

    public RsaHelper(byte[] publicKey, byte[] privateKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            if (publicKey != null && publicKey.length > 0) {
                this.publicKey = (RSAPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey));
            }
            if (privateKey != null && privateKey.length > 0) {
                this.privateKey = (RSAPrivateCrtKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public RsaHelper(String publicKey) {
        this(Base64Util.decode(publicKey));
    }

    public RsaHelper(byte[] publicKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            if (publicKey != null && publicKey.length > 0) {
                this.publicKey = (RSAPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public byte[] encrypt(byte[] content) {
        if (publicKey == null) {
            throw new RuntimeException("public key is null.");
        }

        if (content == null) {
            return null;
        }

        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            int size = publicKey.getModulus().bitLength() / 8 - 11;
            ByteArrayOutputStream baos = new ByteArrayOutputStream((content.length + size - 1) / size * (size + 11));
            int left = 0;
            for (int i = 0; i < content.length; ) {
                left = content.length - i;
                if (left > size) {
                    cipher.update(content, i, size);
                    i += size;
                } else {
                    cipher.update(content, i, left);
                    i += left;
                }
                baos.write(cipher.doFinal());
            }
            return baos.toByteArray();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public byte[] decrypt(byte[] secret) {
        if (privateKey == null) {
            throw new RuntimeException("private key is null.");
        }

        if (secret == null) {
            return null;
        }

        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            int size = privateKey.getModulus().bitLength() / 8;
            ByteArrayOutputStream baos = new ByteArrayOutputStream((secret.length + size - 12) / (size - 11) * size);
            int left = 0;
            for (int i = 0; i < secret.length; ) {
                left = secret.length - i;
                if (left > size) {
                    cipher.update(secret, i, size);
                    i += size;
                } else {
                    cipher.update(secret, i, left);
                    i += left;
                }
                baos.write(cipher.doFinal());
            }
            return baos.toByteArray();
        } catch (Exception e) {
            logger.error("rsa decrypt failed.", e);
        }
        return null;
    }

    public byte[] sign(byte[] content) {
        if (privateKey == null) {
            throw new RuntimeException("private key is null.");
        }
        if (content == null) {
            return null;
        }
        try {
            Signature signature = Signature.getInstance("SHA1WithRSA");
            signature.initSign(privateKey);
            signature.update(content);
            return signature.sign();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public boolean verify(byte[] sign, byte[] content) {
        if (publicKey == null) {
            throw new RuntimeException("public key is null.");
        }
        if (sign == null || content == null) {
            return false;
        }
        try {
            Signature signature = Signature.getInstance("SHA1WithRSA");
            signature.initVerify(publicKey);
            signature.update(content);
            return signature.verify(sign);
        } catch (Exception e) {
            logger.error("rsa verify failed.", e);
        }
        return false;
    }
}

4.6 Алгоритм ЕСС

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

import net.pocrd.annotation.NotThreadSafe;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.KeyFactory;
import java.security.Security;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

@NotThreadSafe
public class EccHelper {
    private static final Logger logger = LoggerFactory.getLogger(EccHelper.class);
    private static final int SIZE = 4096;
    private BCECPublicKey  publicKey;
    private BCECPrivateKey privateKey;

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    public EccHelper(String publicKey, String privateKey) {
        this(Base64Util.decode(publicKey), Base64Util.decode(privateKey));
    }

    public EccHelper(byte[] publicKey, byte[] privateKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
            if (publicKey != null && publicKey.length > 0) {
                this.publicKey = (BCECPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey));
            }
            if (privateKey != null && privateKey.length > 0) {
                this.privateKey = (BCECPrivateKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey));
            }
        } catch (ClassCastException e) {
            throw new RuntimeException("", e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public EccHelper(String publicKey) {
        this(Base64Util.decode(publicKey));
    }

    public EccHelper(byte[] publicKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
            if (publicKey != null && publicKey.length > 0) {
                this.publicKey = (BCECPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public byte[] encrypt(byte[] content) {
        if (publicKey == null) {
            throw new RuntimeException("public key is null.");
        }
        try {
            Cipher cipher = Cipher.getInstance("ECIES", "BC");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            int size = SIZE;
            ByteArrayOutputStream baos = new ByteArrayOutputStream((content.length + size - 1) / size * (size + 45));
            int left = 0;
            for (int i = 0; i < content.length; ) {
                left = content.length - i;
                if (left > size) {
                    cipher.update(content, i, size);
                    i += size;
                } else {
                    cipher.update(content, i, left);
                    i += left;
                }
                baos.write(cipher.doFinal());
            }
            return baos.toByteArray();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public byte[] decrypt(byte[] secret) {
        if (privateKey == null) {
            throw new RuntimeException("private key is null.");
        }
        try {
            Cipher cipher = Cipher.getInstance("ECIES", "BC");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            int size = SIZE + 45;
            ByteArrayOutputStream baos = new ByteArrayOutputStream((secret.length + size + 44) / (size + 45) * size);
            int left = 0;
            for (int i = 0; i < secret.length; ) {
                left = secret.length - i;
                if (left > size) {
                    cipher.update(secret, i, size);
                    i += size;
                } else {
                    cipher.update(secret, i, left);
                    i += left;
                }
                baos.write(cipher.doFinal());
            }
            return baos.toByteArray();
        } catch (Exception e) {
            logger.error("ecc decrypt failed.", e);
        }
        return null;
    }

    public byte[] sign(byte[] content) {
        if (privateKey == null) {
            throw new RuntimeException("private key is null.");
        }
        try {
            Signature signature = Signature.getInstance("SHA1withECDSA", "BC");
            signature.initSign(privateKey);
            signature.update(content);
            return signature.sign();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public boolean verify(byte[] sign, byte[] content) {
        if (publicKey == null) {
            throw new RuntimeException("public key is null.");
        }
        try {
            Signature signature = Signature.getInstance("SHA1withECDSA", "BC");
            signature.initVerify(publicKey);
            signature.update(content);
            return signature.verify(sign);
        } catch (Exception e) {
            logger.error("ecc verify failed.", e);
        }
        return false;
    }
}

5. Сравнение различных алгоритмов шифрования

5.1 Сравнение алгоритмов хеширования

название безопасность скорость
SHA-1 высокий медленный
MD5 середина быстрый

5.2 Сравнение алгоритмов симметричного шифрования

название имя ключа скорость бега безопасность НЧ
DES 56 бит Быстрее Низкий середина
3DES 112-битный или 168-битный медленный середина высокий
AES 128, 192, 256 бит быстрый высокий Низкий

5.3 Сравнение алгоритмов асимметричного шифрования

название зрелость безопасность расчет скорости НЧ
RSA высокий высокий середина середина
ECC высокий высокий медленный высокий

5.4 Алгоритмы симметричного и асимметричного шифрования

5.4.1 Симметричные алгоритмы

  1. ключевой менеджмент: более сложный, не подходит для Интернета, обычно используется для внутренних систем.

  2. безопасность:середина

  3. скорость шифрования: хорошо скореенесколько порядков(Программное шифрование и скорость дешифрования не ниже высокой100раз, количество шифровок и расшифровок в секундуM битыданные), подходит для шифрования и дешифрования обработки больших объемов данных

5.4.2 Асимметричные алгоритмы

  1. ключевой менеджмент: Клавишами легко управлять

  2. безопасность:высокий

  3. скорость шифрования: относительно медленный, подходящийнебольшой объем данныхШифрование и дешифрование или подпись данных

резюме

В этой статье описываетсяцифровой подписи,Шифровать и расшифровывать,Симметричное и асимметричное шифрование, а потом подробностиMD5,SHA-1,HMAC,DES/AES,RSAиECCЭти несколько алгоритмов шифрования и примеры кода.


Добро пожаловать в технический публичный аккаунт: Zero One Technology Stack

零壹技术栈

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