[Документация Yandex Cloud](../../index.md) > [Yandex Key Management Service](../index.md) > [Концепции](index.md) > Электронная подпись > Электронная подпись

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

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

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

Алгоритм электронной подписи позволяет выполнять две операции: создание подписи и [проверка подписи](#signature-verification). 

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

Электронную подпись можно использовать для проверки подлинности исходного кода, бинарных файлов, образов контейнеров. Например, можно проверить подлинность подписанного электронной подписью образа. Если при проверке подпись окажется недействительной, значит, образ был изменен или поврежден. Также электронную подпись можно использовать для проверки субъекта сертификата, выданного удостоверяющим центром ([Certificate Authority](https://ru.wikipedia.org/wiki/%D0%A6%D0%B5%D0%BD%D1%82%D1%80_%D1%81%D0%B5%D1%80%D1%82%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D0%B8)).

Каждая ключевая пара соответствует одному ключу в [квотах](limits.md#kms-quotas) KMS.

## Работа с электронной подписью {#digital-signing}

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

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

### Поддерживаемые алгоритмы электронной подписи {#supported-algorithms}

Для электронной подписи сервис KMS предоставляет криптографические алгоритмы [ECDSA](https://ru.wikipedia.org/wiki/ECDSA) и [RSA](https://ru.wikipedia.org/wiki/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`

## Проверка электронной подписи {#signature-verification}

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

### Подпись на эллиптических кривых (ECDSA) {#ecdsa-verification}

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

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

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

    {% list tabs group=programming_language %}
    
    - Bash {#bash}
    
      Проверьте электронную подпись с помощью утилиты [OpenSSL](https://www.openssl.org/):
    
      ```(bash)
      openssl dgst \
        -<алгоритм_хэширования> \
        -verify <путь_к_файлу_открытого_ключа> \
        -signature <путь_к_файлу_подписи> \
        <путь_к_подписанному_файлу>
      ```
    
      Где:
      * `<алгоритм_хэширования>` — алгоритм хэширования, использованный при создании ключевой пары подписи. Возможные значения:
          * `sha256` — для алгоритмов SHA-256;
          * `sha384` — для алгоритмов SHA-384;
          * `sha512` — для алгоритмов SHA-512.
      * `-verify` — путь к файлу с открытым ключом подписи.
      * `-signature` — путь к файлу с электронной подписью.
      * `<путь_к_подписанному_файлу>` — путь к файлу, для которого проверяется электронная подпись.
    
      Если подпись корректна, утилита OpenSSL вернет статус `Verified OK`.
    
    - Java {#java}
    
      ```java
      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`.
    
    - Go {#go}
    
      ```golang
      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`.
    
    - Python {#python}
    
      ```python
      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`.
    
    {% endlist %}

### Подпись RSA {#rca-verification}

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

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

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

    {% list tabs group=programming_language %}
    
    - Bash {#bash}
    
      Проверьте электронную подпись с помощью утилиты [OpenSSL](https://www.openssl.org/):
    
      ```(bash)
      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`.
    
    - Java {#java}
    
      ```java
      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`.
    
    - Go {#go}
    
      ```golang
      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`.
    
    - Python {#python}
    
      ```python
      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`.
    
    {% endlist %}

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

* [Подпись и проверка Docker-образов Yandex Container Registry в Yandex Managed Service for Kubernetes](../tutorials/sign-cr-with-cosign.md)