Wir empfehlen für die meisten Anwendungsfälle das Primitiv „Digitale Signatur“ mit dem Schlüsseltyp „ECDSA_P256“.
Das Primitive „Digitale Signatur“ sorgt dafür, dass niemand Ihre Daten manipuliert hat, und beweist, dass die Daten von Ihnen stammen. Sie ist asymmetrisch, d. h., die Daten werden mit dem privaten Schlüssel signiert und mit dem öffentlichen Schlüssel verifiziert.
Die folgenden Beispiele sollen Ihnen den Einstieg in die Verwendung der Primitiven „Digitale Signatur“ erleichtern:
// A utility for signing and verifying files using digital signatures. #include <iostream> #include <memory> #include <ostream> #include <string> #include "absl/flags/flag.h" #include "absl/flags/parse.h" #include "absl/log/check.h" #include "absl/strings/string_view.h" #include "tink/config/global_registry.h" #include "util/util.h" #include "tink/keyset_handle.h" #include "tink/public_key_sign.h" #include "tink/public_key_verify.h" #include "tink/signature/signature_config.h" #include "tink/util/status.h" ABSL_FLAG(std::string, keyset_filename, "", "Keyset file in JSON format"); ABSL_FLAG(std::string, mode, "", "Mode of operation (sign|verify)"); ABSL_FLAG(std::string, input_filename, "", "Filename to operate on"); ABSL_FLAG(std::string, signature_filename, "", "Path to the signature file"); namespace { using ::crypto::tink::KeysetHandle; using ::crypto::tink::PublicKeySign; using ::crypto::tink::PublicKeyVerify; using ::crypto::tink::util::Status; using ::crypto::tink::util::StatusOr; constexpr absl::string_view kSign = "sign"; constexpr absl::string_view kVerify = "verify"; void ValidateParams() { // ... } } // namespace namespace tink_cc_examples { // Digital signature example CLI implementation. Status DigitalSignatureCli(absl::string_view mode, const std::string& keyset_filename, const std::string& input_filename, const std::string& signature_filename) { Status result = crypto::tink::SignatureConfig::Register(); if (!result.ok()) return result; // Read the keyset from file. StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = ReadJsonCleartextKeyset(keyset_filename); if (!keyset_handle.ok()) return keyset_handle.status(); // Read the input. StatusOr<std::string> input_file_content = ReadFile(input_filename); if (!input_file_content.ok()) return input_file_content.status(); if (mode == kSign) { StatusOr<std::unique_ptr<PublicKeySign>> public_key_sign = (*keyset_handle) ->GetPrimitive<crypto::tink::PublicKeySign>( crypto::tink::ConfigGlobalRegistry()); if (!public_key_sign.ok()) return public_key_sign.status(); StatusOr<std::string> signature = (*public_key_sign)->Sign(*input_file_content); if (!signature.ok()) return signature.status(); return WriteToFile(*signature, signature_filename); } else { // mode == kVerify StatusOr<std::unique_ptr<PublicKeyVerify>> public_key_verify = (*keyset_handle) ->GetPrimitive<crypto::tink::PublicKeyVerify>( crypto::tink::ConfigGlobalRegistry()); if (!public_key_verify.ok()) return public_key_verify.status(); // Read the signature. StatusOr<std::string> signature_file_content = ReadFile(signature_filename); if (!signature_file_content.ok()) return signature_file_content.status(); return (*public_key_verify) ->Verify(*signature_file_content, *input_file_content); } } } // namespace tink_cc_examples int main(int argc, char** argv) { absl::ParseCommandLine(argc, argv); ValidateParams(); std::string mode = absl::GetFlag(FLAGS_mode); std::string keyset_filename = absl::GetFlag(FLAGS_keyset_filename); std::string input_filename = absl::GetFlag(FLAGS_input_filename); std::string signature_filename = absl::GetFlag(FLAGS_signature_filename); std::clog << "Using keyset in " << keyset_filename << " to " << mode; if (mode == kSign) { std::clog << " file " << input_filename << "; the resulting signature is written to " << signature_filename << '\n'; } else { // mode == kVerify std::clog << " the signature in " << signature_filename << " over the content of " << input_filename << '\n'; } CHECK_OK(tink_cc_examples::DigitalSignatureCli( mode, keyset_filename, input_filename, signature_filename)); return 0; }
import ( "bytes" "fmt" "log" "github.com/tink-crypto/tink-go/v2/insecurecleartextkeyset" "github.com/tink-crypto/tink-go/v2/keyset" "github.com/tink-crypto/tink-go/v2/signature" ) func Example() { // A private keyset created with // "tinkey create-keyset --key-template=ECDSA_P256 --out private_keyset.cfg". // Note that this keyset has the secret key information in cleartext. privateJSONKeyset := `{ "key": [{ "keyData": { "keyMaterialType": "ASYMMETRIC_PRIVATE", "typeUrl": "type.googleapis.com/google.crypto.tink.EcdsaPrivateKey", "value": "EkwSBggDEAIYAhogEiSZ9u2nDtvZuDgWgGsVTIZ5/V08N4ycUspTX0RYRrkiIHpEwHxQd1bImkyMvV2bqtUbgMh5uPSTdnUEGrPXdt56GiEA3iUi+CRN71qy0fOCK66xAW/IvFyjOGtxjppRhSFUneo=" }, "keyId": 611814836, "outputPrefixType": "TINK", "status": "ENABLED" }], "primaryKeyId": 611814836 }` // The corresponding public keyset created with // "tinkey create-public-keyset --in private_keyset.cfg" publicJSONKeyset := `{ "key": [{ "keyData": { "keyMaterialType": "ASYMMETRIC_PUBLIC", "typeUrl": "type.googleapis.com/google.crypto.tink.EcdsaPublicKey", "value": "EgYIAxACGAIaIBIkmfbtpw7b2bg4FoBrFUyGef1dPDeMnFLKU19EWEa5IiB6RMB8UHdWyJpMjL1dm6rVG4DIebj0k3Z1BBqz13beeg==" }, "keyId": 611814836, "outputPrefixType": "TINK", "status": "ENABLED" }], "primaryKeyId": 611814836 }` // Create a keyset handle from the cleartext private keyset in the previous // step. The keyset handle provides abstract access to the underlying keyset to // limit the access of the raw key material. WARNING: In practice, // it is unlikely you will want to use a insecurecleartextkeyset, as it implies // that your key material is passed in cleartext, which is a security risk. // Consider encrypting it with a remote key in Cloud KMS, AWS KMS or HashiCorp Vault. // See https://github.com/google/tink/blob/master/docs/GOLANG-HOWTO.md#storing-and-loading-existing-keysets. privateKeysetHandle, err := insecurecleartextkeyset.Read( keyset.NewJSONReader(bytes.NewBufferString(privateJSONKeyset))) if err != nil { log.Fatal(err) } // Retrieve the Signer primitive from privateKeysetHandle. signer, err := signature.NewSigner(privateKeysetHandle) if err != nil { log.Fatal(err) } // Use the primitive to sign a message. In this case, the primary key of the // keyset will be used (which is also the only key in this example). data := []byte("data") sig, err := signer.Sign(data) if err != nil { log.Fatal(err) } // Create a keyset handle from the keyset containing the public key. Because the // public keyset does not contain any secrets, we can use [keyset.ReadWithNoSecrets]. publicKeysetHandle, err := keyset.ReadWithNoSecrets( keyset.NewJSONReader(bytes.NewBufferString(publicJSONKeyset))) if err != nil { log.Fatal(err) } // Retrieve the Verifier primitive from publicKeysetHandle. verifier, err := signature.NewVerifier(publicKeysetHandle) if err != nil { log.Fatal(err) } if err = verifier.Verify(sig, data); err != nil { log.Fatal(err) } fmt.Printf("sig is valid") // Output: sig is valid }
package signature; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.crypto.tink.InsecureSecretKeyAccess; import com.google.crypto.tink.KeysetHandle; import com.google.crypto.tink.PublicKeySign; import com.google.crypto.tink.PublicKeyVerify; import com.google.crypto.tink.RegistryConfiguration; import com.google.crypto.tink.TinkJsonProtoKeysetFormat; import com.google.crypto.tink.signature.SignatureConfig; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; /** * A command-line utility for digitally signing and verifying a file. * * <p>It loads cleartext keys from disk - this is not recommended! * * <p>It requires the following arguments: * * <ul> * <li>mode: either 'sign' or 'verify'. * <li>key-file: Read the key material from this file. * <li>input-file: Read the input from this file. * <li>signature-file: name of the file containing a hexadecimal signature of the input file. */ public final class SignatureExample { public static void main(String[] args) throws Exception { if (args.length != 4) { System.err.printf("Expected 4 parameters, got %d\n", args.length); System.err.println( "Usage: java SignatureExample sign/verify key-file input-file signature-file"); System.exit(1); } String mode = args[0]; if (!mode.equals("sign") && !mode.equals("verify")) { System.err.println("Incorrect mode. Please select sign or verify."); System.exit(1); } Path keyFile = Paths.get(args[1]); byte[] msg = Files.readAllBytes(Paths.get(args[2])); Path signatureFile = Paths.get(args[3]); // Register all signature key types with the Tink runtime. SignatureConfig.register(); // Read the keyset into a KeysetHandle. KeysetHandle handle = TinkJsonProtoKeysetFormat.parseKeyset( new String(Files.readAllBytes(keyFile), UTF_8), InsecureSecretKeyAccess.get()); if (mode.equals("sign")) { // Get the primitive. PublicKeySign signer = handle.getPrimitive(RegistryConfiguration.get(), PublicKeySign.class); // Use the primitive to sign data. byte[] signature = signer.sign(msg); Files.write(signatureFile, signature); } else { byte[] signature = Files.readAllBytes(signatureFile); // Get the primitive. PublicKeyVerify verifier = handle.getPrimitive(RegistryConfiguration.get(), PublicKeyVerify.class); verifier.verify(signature, msg); } } private SignatureExample() {} }
import tink from tink import secret_key_access from tink import signature def example(): """Sign and verify using digital signatures.""" # Register the signature key managers. This is needed to create # PublicKeySign and PublicKeyVerify primitives later. signature.register() # A private keyset created with # "tinkey create-keyset --key-template=ECDSA_P256 --out private_keyset.cfg". # Note that this keyset has the secret key information in cleartext. private_keyset = r"""{ "key": [{ "keyData": { "keyMaterialType": "ASYMMETRIC_PRIVATE", "typeUrl": "type.googleapis.com/google.crypto.tink.EcdsaPrivateKey", "value": "EkwSBggDEAIYAhogEiSZ9u2nDtvZuDgWgGsVTIZ5/V08N4ycUspTX0RYRrkiIHpEwHxQd1bImkyMvV2bqtUbgMh5uPSTdnUEGrPXdt56GiEA3iUi+CRN71qy0fOCK66xAW/IvFyjOGtxjppRhSFUneo=" }, "keyId": 611814836, "outputPrefixType": "TINK", "status": "ENABLED" }], "primaryKeyId": 611814836 }""" # The corresponding public keyset created with # "tinkey create-public-keyset --in private_keyset.cfg" public_keyset = r"""{ "key": [{ "keyData": { "keyMaterialType": "ASYMMETRIC_PUBLIC", "typeUrl": "type.googleapis.com/google.crypto.tink.EcdsaPublicKey", "value": "EgYIAxACGAIaIBIkmfbtpw7b2bg4FoBrFUyGef1dPDeMnFLKU19EWEa5IiB6RMB8UHdWyJpMjL1dm6rVG4DIebj0k3Z1BBqz13beeg==" }, "keyId": 611814836, "outputPrefixType": "TINK", "status": "ENABLED" }], "primaryKeyId": 611814836 }""" # Create a keyset handle from the cleartext keyset in the previous # step. The keyset handle provides abstract access to the underlying keyset to # limit the exposure of accessing the raw key material. WARNING: In practice, # it is unlikely you will want to use tink.json_proto_keyset_format.parse, as # it implies that your key material is passed in cleartext which is a security # risk. private_keyset_handle = tink.json_proto_keyset_format.parse( private_keyset, secret_key_access.TOKEN ) # Retrieve the PublicKeySign primitive we want to use from the keyset # handle. sign_primitive = private_keyset_handle.primitive(signature.PublicKeySign) # Use the primitive to sign a message. In this case the primary key of the # keyset will be used (which is also the only key in this example). sig = sign_primitive.sign(b'msg') # Create a keyset handle from the keyset containing the public key. Because # this keyset does not contain any secrets, we can use # `parse_without_secret`. public_keyset_handle = tink.json_proto_keyset_format.parse_without_secret( public_keyset ) # Retrieve the PublicKeyVerify primitive we want to use from the keyset # handle. verify_primitive = public_keyset_handle.primitive(signature.PublicKeyVerify) # Use the primitive to verify that `sig` is valid signature for the message. # Verify finds the correct key in the keyset. If no key is found or # verification fails, it raises an error. verify_primitive.verify(sig, b'msg') # Note that we can also get the public keyset handle from the private keyset # handle. The verification works the same as above. public_keyset_handle2 = private_keyset_handle.public_keyset_handle() verify_primitive2 = public_keyset_handle2.primitive(signature.PublicKeyVerify) verify_primitive2.verify(sig, b'msg')
Digitale Signatur
Mit dem Primitiven „Digitale Signatur“ können Sie prüfen, ob Ihre Daten nicht manipuliert wurden. Sie sorgt für Authentizität und Integrität, aber nicht für Geheimhaltung der signierten Daten. Die digitale Signatur ist asymmetrisch, d. h. sie verwendet ein Schlüsselpaar (öffentlicher und privater Schlüssel).
Das Primitive „Digitale Signatur“ hat folgende Eigenschaften:
- Authentizität: Es ist nicht möglich, eine Signatur zu erstellen, die mit
PublicKeyVerify.Verify(signature, message)
validiert werden kann, es sei denn, Sie haben den privaten Schlüssel. - Asymmetrisch: Beim Erstellen der Signatur wird ein anderer Schlüssel verwendet als für die Verifizierung. Dadurch können Sie den öffentlichen Schlüssel verteilen, um Signaturen für Parteien zu verifizieren, die Signaturen nicht selbst erstellen können.
Wenn Sie keine Asymmetrie benötigen, können Sie stattdessen das einfachere und effizientere MAC-Primitive verwenden.
Die Funktionalität digitaler Signaturen wird in Tink als Paar von Primitiven dargestellt:
- PublicKeySign zum Signieren von Daten
- PublicKeyVerify zum Verifizieren der Signatur
Schlüsseltyp auswählen
Wir empfehlen für die meisten Anwendungsfälle ECDSA_P256, es gibt aber eine Vielzahl von Optionen. Im Allgemeinen gilt:
- ECDSA_P256 ist die am häufigsten verwendete Option und eine angemessene Standardeinstellung. ECDSA-Signaturen sind jedoch veränderbar.
- ED25519 erstellt deterministische Signaturen und bietet eine bessere Leistung als ECDSA_P256.
- Mit RSA_SSA_PKCS1_3072_SHA256_F4 werden deterministische Signaturen erstellt und die beste Überprüfungsleistung erzielt. Die Signatur ist jedoch viel langsamer als bei ECDSA_P256 oder ED25519.
Mindestsicherheitsgarantien
- Die zu signierenden Daten können beliebig lang sein.
- 128-Bit-Sicherheitsebene gegen adaptive Angriffe mit ausgewählten Nachrichten für elliptische-Kurven-basierte Systeme
- 112-Bit-Sicherheitsebene gegen adaptive Chosen-Message-Angriffe für RSA-basierte Systeme (ermöglicht 2.048-Bit-Schlüssel)
Verformbarkeit
Ein Signaturschema ist manipulierbar, wenn ein Angreifer eine andere gültige Signatur für eine bereits signierte Nachricht erstellen kann. In den meisten Fällen ist das kein Problem. In einigen Fällen gehen Programmierer jedoch implizit davon aus, dass gültige Signaturen eindeutig sind, was zu unerwarteten Ergebnissen führen kann.