Yandex Cloud
Поиск
Связаться с намиПодключиться
  • Документация
  • Блог
  • Все сервисы
  • Статус работы сервисов
    • Популярные
    • Инфраструктура и сеть
    • Платформа данных
    • Контейнеры
    • Инструменты разработчика
    • Бессерверные вычисления
    • Безопасность
    • Мониторинг и управление ресурсами
    • Машинное обучение
    • Бизнес-инструменты
  • Все решения
    • По отраслям
    • По типу задач
    • Экономика платформы
    • Безопасность
    • Техническая поддержка
    • Каталог партнёров
    • Обучение и сертификация
    • Облако для стартапов
    • Облако для крупного бизнеса
    • Центр технологий для общества
    • Облако для интеграторов
    • Поддержка IT-бизнеса
    • Облако для фрилансеров
    • Обучение и сертификация
    • Блог
    • Документация
    • Контент-программа
    • Мероприятия и вебинары
    • Контакты, чаты и сообщества
    • Идеи
    • Истории успеха
    • Тарифы Yandex Cloud
    • Промоакции и free tier
    • Правила тарификации
  • Документация
  • Блог
Проект Яндекса
© 2025 ООО «Яндекс.Облако»
Yandex Key Management Service
  • Начало работы
    • Обзор
      • Ключевая пара электронной подписи
      • Электронная подпись
    • Шифрование по схеме envelope encryption
    • Консистентность ключей
    • Квоты и лимиты
  • Управление доступом
  • Правила тарификации
  • Справочник Terraform
  • Метрики Monitoring
  • Аудитные логи Audit Trails
  • Вопросы и ответы
  • Обучающие курсы

В этой статье:

  • Работа с электронной подписью
  • Поддерживаемые алгоритмы электронной подписи
  • Проверка электронной подписи
  • Подпись на эллиптических кривых (ECDSA)
  • Подпись RSA
  • Примеры использования
  1. Концепции
  2. Электронная подпись
  3. Электронная подпись

Электронная подпись в KMS

Статья создана
Yandex Cloud
Обновлена 27 февраля 2025 г.
  • Работа с электронной подписью
    • Поддерживаемые алгоритмы электронной подписи
  • Проверка электронной подписи
    • Подпись на эллиптических кривых (ECDSA)
    • Подпись RSA
  • Примеры использования

Электронная подпись — это результат криптографической операции, который позволяет обеспечить дополнительную защиту данных. Главные задачи электронной подписи:

  • подтверждение подлинности данных;
  • проверка целостности данных;
  • защита данных от изменений;
  • удостоверение источника данных.

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

Электронная подпись основана на асимметричной криптографии. Асимметричная ключевая пара электронной подписи состоит из двух частей: открытого ключа подписи (Public key) и закрытого ключа подписи (Private key). Закрытый ключ используется для создания подписи, а открытый — для ее проверки.

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

Каждая ключевая пара соответствует одному ключу в квотах KMS.

Работа с электронной подписьюРабота с электронной подписью

Участники процесса работы с электронной подписью — это владелец закрытого ключа ключевой пары и получатели подписанных данных. Процесс создания электронной подписи и ее проверки происходит по следующей схеме:

  1. Владелец подписи создает асимметричную ключевую пару, поддерживающую электронную подпись.
  2. Владелец создает электронную подпись своих данных. При этом рассчитывается хэш-значение пользовательских данных, которое подписывается закрытым ключом по заданному алгоритму. Используемая хэш-функция указывается в названии алгоритма.
  3. Владелец подписи передает получателю данные, электронную подпись и открытый ключ асимметричной ключевой пары.
  4. Получатель использует открытый ключ для проверки электронной подписи.
  5. Если расшифрованный получателем хэш совпадает с хэшем данных, значит, подпись корректна.

Поддерживаемые алгоритмы электронной подписиПоддерживаемые алгоритмы электронной подписи

Для электронной подписи сервис KMS предоставляет криптографические алгоритмы ECDSA и RSA. Для обоих алгоритмов шифрования доступен выбор размера ключа и алгоритма хэширования (дайджеста):

  • RSA_2048_SIGN_PSS_SHA_256
  • RSA_2048_SIGN_PSS_SHA_384
  • RSA_2048_SIGN_PSS_SHA_512
  • RSA_3072_SIGN_PSS_SHA_256
  • RSA_3072_SIGN_PSS_SHA_384
  • RSA_3072_SIGN_PSS_SHA_512
  • RSA_4096_SIGN_PSS_SHA_256
  • RSA_4096_SIGN_PSS_SHA_384
  • RSA_4096_SIGN_PSS_SHA_512
  • ECDSA_NIST_P256_SHA_256
  • ECDSA_NIST_P384_SHA_384
  • ECDSA_NIST_P521_SHA_512
  • ECDSA_NIST_SECP256_K1_SHA_256

Проверка электронной подписиПроверка электронной подписи

Проверка электронной подписи выполняется стороной, у которой нет доступа к закрытому ключу электронной подписи. Для проверки подписи используется открытый ключ.

Подпись на эллиптических кривых (ECDSA)Подпись на эллиптических кривых (ECDSA)

Для проверки электронной подписи:

  1. Получите открытый ключ электронной подписи.

  2. Выполните проверку:

    Bash
    Java
    Go
    Python

    Проверьте электронную подпись с помощью утилиты OpenSSL:

    openssl dgst \
      -<алгоритм_хэширования> \
      -verify <путь_к_файлу_открытого_ключа> \
      -signature <путь_к_файлу_подписи> \
      <путь_к_подписанному_файлу>
    

    Где:

    • <алгоритм_хэширования> — алгоритм хэширования, использованный при создании ключевой пары подписи. Возможные значения:
      • sha256 — для алгоритмов SHA-256;
      • sha384 — для алгоритмов SHA-384;
      • sha512 — для алгоритмов SHA-512.
    • -verify — путь к файлу с открытым ключом подписи.
    • -signature — путь к файлу с электронной подписью.
    • <путь_к_подписанному_файлу> — путь к файлу, для которого проверяется электронная подпись.

    Если подпись корректна, утилита OpenSSL вернет статус Verified OK.

    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    import org.bouncycastle.util.io.pem.PemObject;
    import org.bouncycastle.util.io.pem.PemReader;
    
    import javax.crypto.BadPaddingException;
    import javax.crypto.IllegalBlockSizeException;
    import javax.crypto.NoSuchPaddingException;
    import java.io.IOException;
    import java.io.StringReader;
    import java.security.*;
    import java.security.spec.*;
    import java.util.Base64;
    
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    
    public class VerifyEcdsaSign {
    
        public static void main(String[] args) throws Exception {
            String publicKeyPem = 
            """
            -- -- - BEGIN PUBLIC KEY-- -- -
            <содержимое_открытого_ключа>
                -- -- - END PUBLIC KEY-- -- - 
            """;
            String signatureStr = "<подпись>";
            byte[] signatureDer = Base64.getDecoder().decode(signatureStr);
            System.out.println(verifyEcdsaSignature(publicKeyPem, signatureDer, "<сообщение>", "<тип_алгоритма>"));
        }
    
        public static boolean verifyEcdsaSignature(String publicKeyPem, byte[] signatureDer, String message, String hash_algorithm)
        throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException,
        SignatureException, IOException {
    
            // Public key and subscription decoding
            PemReader pemReader = new PemReader(new StringReader(publicKeyPem));
            PemObject pemObject = pemReader.readPemObject();
            byte[] publicKeyBytes = pemObject.getContent();
    
            // Creating a PublicKey object from the decoded public key
            KeyFactory keyFactory = KeyFactory.getInstance("EC", new BouncyCastleProvider());
            EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
            PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
    
            // Creating a Signature object and initializing it with a public key
            Signature signature = Signature.getInstance(hash_algorithm + "withECDSA", new BouncyCastleProvider());
            signature.initVerify(publicKey);
    
            // Updating a Signature Object with Message Data
            byte[] messageBytes = message.getBytes();
            signature.update(messageBytes);
    
            // Signature verification using original message and decoded signature
            return signature.verify(signatureDer);
        }
    }
    

    Где:

    • <содержимое_открытого_ключа> — содержимое открытого ключа подписи.
    • <подпись> — содержимое электронной подписи в кодировке base64.
    • <сообщение> — строка, содержащая исходное сообщение, подписанное электронной подписью, или хэш подписанного электронной подписью файла.
    • <тип_алгоритма> — использованная для подписи хэш-функция. Возможные значения: SHA256, SHA384 и SHA512.

    Код выполняет проверку электронной подписи на эллиптических кривых (ECDSA). Если подпись корректна, код возвращает true, если нет — false.

    import (
        "crypto/ecdsa"
        "crypto/sha256"
        "crypto/x509"
        "encoding/asn1"
        "encoding/base64"
        "encoding/pem"
        "fmt"
        "hash"
        "log"
        "math/big"
    )
    
    func runEcdsaSignTest() {
        publicKeyPem := `-----BEGIN PUBLIC KEY-----
        <содержимое_открытого_ключа>
        -----END PUBLIC KEY-----`
        signatureB64 := "<подпись>"
        signatureDER, _ := base64.StdEncoding.DecodeString(signatureB64)
        message := "<сообщение>"
    
            fmt.Println(verifyEcdsa(publicKeyPem, signatureDER, message, <тип_алгоритма>))
    }
    
    type ECDSASignature struct {
        R, S *big.Int
    }
    
    func verifyEcdsa(publicKeyPem string, signatureDER []byte, message string, hashFunc hash.Hash) bool {
    
        // Decode the public key
        block, _ := pem.Decode([]byte(publicKeyPem))
        if block == nil {
            log.Fatal("failed to decode PEM block containing public key")
        }
    
        // Parse the public key
        pub, err := x509.ParsePKIXPublicKey(block.Bytes)
        if err != nil {
    	    log.Fatal(err)
        }
    
        publicKey, ok := pub.(*ecdsa.PublicKey)
        if !ok {
    	    log.Fatal("not ECDSA public key")
        }
    
        // Parse the signature
        var signature ECDSASignature
        _, err = asn1.Unmarshal(signatureDER, &signature)
        if err != nil {
    	    log.Fatal(err)
        }
    
        // Compute the hash of the message
        hashFunc.Write([]byte(message))
        hashed := hashFunc.Sum(nil)
    
        // Verify the signature
        return ecdsa.Verify(publicKey, hashed, signature.R, signature.S)
    }
    

    Где:

    • <содержимое_открытого_ключа> — содержимое открытого ключа подписи в кодировке base64.
    • <подпись> — содержимое электронной подписи в кодировке base64.
    • <сообщение> — строка, содержащая исходное сообщение, подписанное электронной подписью, или хэш подписанного электронной подписью файла.
    • <тип_алгоритма> — использованная для подписи хэш-функция. Возможные значения: sha256.New(), sha512.New384() и sha512.New().

    Код выполняет проверку электронной подписи на эллиптических кривых (ECDSA). Если подпись корректна, код возвращает true, если нет — false.

    import base64
    from cryptography.hazmat.primitives import serialization
    from cryptography.hazmat.primitives.asymmetric import ec
    from cryptography.hazmat.primitives import hashes
    from cryptography.exceptions import InvalidSignature
    from cryptography.hazmat.backends import default_backend
    
    # Define hash algorithms
    def verify_ecdsa_signature(public_key_b64, signature_der, message, hash_algorithm):
        hash_algorithms = {
            'SHA256': hashes.SHA256,
            'SHA384': hashes.SHA384,
            'SHA512': hashes.SHA512
        }
    
        # Check if the provided hash algorithm is supported
        if hash_algorithm not in hash_algorithms:
            raise ValueError('Unsupported hash algorithm: ' + hash_algorithm)
    
        # Loading a PEM Encoded Public Key
        public_key = serialization.load_pem_public_key(
            public_key_b64.encode(),
            backend = default_backend()
        )
    
        # Create Signature object and initialize it with the public key
        signature = ec.ECDSA(hash_algorithms[hash_algorithm]())
    
        # Update the Signature object with the message data
        message_bytes = message.encode()
    
        # Verify the signature using the original message and the decoded signature
        try:
            public_key.verify(signature_der, message_bytes, signature)
            return True
        except InvalidSignature:
            return False
    
    def test_verify_signature():
        public_key_b64 = """
        -----BEGIN PUBLIC KEY-----
        <содержимое_открытого_ключа>
        -----END PUBLIC KEY-----"""
        signature_b64 = "<подпись>"
        signature_der = base64.b64decode(signature_b64)
        message = '<сообщение>'
        print(verify_ecdsa_signature(public_key_b64, signature_der, message, "<тип_алгоритма>"))
    

    Где:

    • <содержимое_открытого_ключа> — содержимое открытого ключа подписи.
    • <подпись> — содержимое электронной подписи в кодировке base64.
    • <сообщение> — строка, содержащая исходное сообщение, подписанное электронной подписью, или хэш подписанного электронной подписью файла.
    • <тип_алгоритма> — использованная для подписи хэш-функция. Возможные значения: SHA256, SHA384 и SHA512.

    Код выполняет проверку электронной подписи на эллиптических кривых (ECDSA). Если подпись корректна, код возвращает true, если нет — false.

Подпись RSAПодпись RSA

Для проверки электронной подписи:

  1. Получите открытый ключ электронной подписи.

  2. Выполните проверку:

    Bash
    Java
    Go
    Python

    Проверьте электронную подпись с помощью утилиты OpenSSL:

    openssl dgst \
      -<алгоритм_хэширования> \
      -sigopt rsa_padding_mode:pss \
      -sigopt rsa_pss_saltlen:-1 \
      -verify <путь_к_файлу_открытого_ключа> \
      -signature <путь_к_файлу_подписи> \
      <путь_к_подписанному_файлу>
    

    Где:

    • <алгоритм_хэширования> — алгоритм хэширования, использованный при создании ключевой пары подписи. Возможные значения:
      • sha256 — для алгоритмов SHA-256;
      • sha384 — для алгоритмов SHA-384;
      • sha512 — для алгоритмов SHA-512.
    • -verify — путь к файлу с открытым ключом подписи.
    • -signature — путь к файлу с электронной подписью.
    • <путь_к_подписанному_файлу> — путь к файлу, для которого проверяется электронная подпись.

    Если подпись корректна, утилита OpenSSL вернет статус Verified OK.

    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    import org.bouncycastle.util.io.pem.PemObject;
    import org.bouncycastle.util.io.pem.PemReader;
    
    import javax.crypto.BadPaddingException;
    import javax.crypto.IllegalBlockSizeException;
    import javax.crypto.NoSuchPaddingException;
    import java.io.IOException;
    import java.io.StringReader;
    import java.security.*;
    import java.security.spec.*;
    import java.util.Base64;
    
    public class VerifyRsaSign {
    
        public static void main(String[] args) throws Exception {
            String publicKeyPem = """
            -----BEGIN PUBLIC KEY-----
            <содержимое_открытого_ключа>
            -----END PUBLIC KEY-----""";
            String signatureStr = "<подпись>";
            byte[] signatureBytes = Base64.getDecoder().decode(signatureStr);
            String message = "<сообщение>";
            System.out.println(verifyRsaSignature(publicKeyPem, signatureBytes, message, "<тип_алгоритма>"));
        }
    
        private static boolean verifyRsaSignature(String publicKeyPem, byte[] signatureBytes, String message, String hashAlgorithm)
        throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException,
        SignatureException, InvalidAlgorithmParameterException, IOException {
    
            // Get the public key
            PemReader pemReader = new PemReader(new StringReader(publicKeyPem));
            PemObject pemObject = pemReader.readPemObject();
            byte[] publicKeyBytes = pemObject.getContent();
    
            // Create a PublicKey object using the decoded public key
            KeyFactory keyFactory = KeyFactory.getInstance("RSA", new BouncyCastleProvider());
            EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
            PublicKey pubKey = keyFactory.generatePublic(publicKeySpec);
    
            MessageDigest messageDigest = MessageDigest.getInstance(hashAlgorithm);
            int saltLength = messageDigest.getDigestLength();
    
            // Initialize the PSS signer
            PSSParameterSpec pssSpec = new PSSParameterSpec(hashAlgorithm, "MGF1", new MGF1ParameterSpec(hashAlgorithm), saltLength, 1);
            Signature signer = Signature.getInstance("RSASSA-PSS");
            signer.setParameter(pssSpec);
            signer.initVerify(pubKey);
    
            // Update the signature with the hash of the message
            byte[] messageBytes = message.getBytes();
            signer.update(messageBytes);
    
            // Verify the signature
            return signer.verify(signatureBytes);
        }
    }
    

    Где:

    • <содержимое_открытого_ключа> — содержимое открытого ключа подписи.
    • <подпись> — содержимое электронной подписи в кодировке base64.
    • <сообщение> — строка, содержащая исходное сообщение, подписанное электронной подписью, или хэш подписанного электронной подписью файла.
    • <тип_алгоритма> — использованная для подписи хэш-функция. Возможные значения: SHA256, SHA384 и SHA512.

    Код выполняет проверку электронной подписи RSA. Если подпись корректна, код возвращает true, если нет — false.

    import (
        "crypto"
        "crypto/rsa"
        "crypto/sha256"
        "crypto/x509"
        "encoding/base64"
        "encoding/pem"
        "fmt"
        "log"
    )
    
    func runRsaSignTest() {
        publicKeyB64 := "<содержимое_открытого_ключа>"
        signatureB64 := "<подпись>"
        signatureBytes, _ := base64.StdEncoding.DecodeString(signatureB64)
        message := "<сообщение>"
    
            fmt.Println(verifyRsa(publicKeyB64, signatureBytes, message, <тип_алгоритма>))
    }
    
    func verifyRsa(publicKeyPem string, signatureBytes []byte, message string, hash crypto.Hash) bool {
    
        // Decode the public key
        block, _ := pem.Decode([]byte(publicKeyPem))
        if block == nil {
            log.Fatal("failed to decode PEM block containing public key")
        }
    
        // Parse the public key
        pub, err := x509.ParsePKIXPublicKey(block.Bytes)
        if err != nil {
    	    log.Fatal(err)
        }
    
        publicKey, ok := pub.(*rsa.PublicKey)
        if !ok {
    	    log.Fatal("not RSA public key")
        }
    
        // Calculate the hash of the message
        hasher := hash.New()
        hasher.Write([]byte(message))
        hashed := hasher.Sum(nil)
    
        // Set the PSS options: salt length auto, and the hash function
        pssOptions := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthAuto, Hash: hash}
    
        // Verify the signature
        err = rsa.VerifyPSS(publicKey, hash, hashed, signatureBytes, pssOptions)
        if err != nil {
    	    fmt.Println("Verification failed:", err)
    	    return false
        } else {
    	    return true
        }
    }
    

    Где:

    • <содержимое_открытого_ключа> — содержимое открытого ключа подписи в кодировке base64.
    • <подпись> — содержимое электронной подписи в кодировке base64.
    • <сообщение> — строка, содержащая исходное сообщение, подписанное электронной подписью, или хэш подписанного электронной подписью файла.
    • <тип_алгоритма> — использованная для подписи хэш-функция. Возможные значения: crypto.SHA256, crypto.SHA384 и crypto.SHA512.

    Код выполняет проверку электронной подписи RSA. Если подпись корректна, код возвращает true, если нет — false.

    import base64
    from cryptography.hazmat.primitives import hashes
    from cryptography.hazmat.primitives.asymmetric import padding
    from cryptography.hazmat.primitives import serialization
    from cryptography.exceptions import InvalidSignature
    from cryptography.hazmat.backends import default_backend
    
    # Define hash algorithms and corresponding salt lengths
    def verify_rsa_signature(public_key_b64, signature_bytes, message, hash_algorithm):
        hash_algorithms = {
            'SHA256': hashes.SHA256,
            'SHA384': hashes.SHA384,
            'SHA512': hashes.SHA512
        }
    
        # Check if the provided hash algorithm is supported
        if hash_algorithm not in hash_algorithms:
            raise ValueError('Unsupported hash algorithm: ' + hash_algorithm)
    
        # Loading a PEM Encoded Public Key
        public_key = serialization.load_pem_public_key(
            public_key_b64.encode(),
            backend=default_backend()
        )
    
        # Update the Signature object with the message data
        message_bytes = message.encode()
    
        # Automatically calculate salt length based on hash digest size
        salt_length = hash_algorithms[hash_algorithm]().digest_size
    
        # Verify the signature using the original message and the decoded signature
        try:
            public_key.verify(
                signature_bytes,
                message_bytes,
                padding.PSS(
                    mgf = padding.MGF1(hash_algorithms[hash_algorithm]()),
                    salt_length = salt_length
                ),
                hash_algorithms[hash_algorithm]()
            )
            return True
        except InvalidSignature:
            return False
    
    def test_verify_signature():
        public_key_b64 = """
        -----BEGIN PUBLIC KEY-----
        <содержимое_открытого_ключа>
        -----END PUBLIC KEY-----"""
        signature_b64 = '<подпись>'
        signature_bytes = base64.b64decode(signature_b64)
        message = '<сообщение>'
        print(verify_rsa_signature(public_key_b64, signature_bytes, message, '<тип_алгоритма>'))
    

    Где:

    • <содержимое_открытого_ключа> — содержимое открытого ключа подписи в кодировке base64.
    • <подпись> — содержимое электронной подписи в кодировке base64.
    • <сообщение> — строка, содержащая исходное сообщение, подписанное электронной подписью, или хэш подписанного электронной подписью файла.
    • <тип_алгоритма> — использованная для подписи хэш-функция. Возможные значения: SHA256, SHA384 и SHA512.

    Код выполняет проверку электронной подписи RSA. Если подпись корректна, код возвращает true, если нет — false.

Примеры использованияПримеры использования

  • Подпись и проверка Docker-образов Yandex Container Registry в Yandex Managed Service for Kubernetes

Была ли статья полезна?

Предыдущая
Ключевая пара электронной подписи
Следующая
Шифрование по схеме envelope encryption
Проект Яндекса
© 2025 ООО «Яндекс.Облако»