برای اکثر موارد استفاده، Digital Signature اولیه را با ML_DSA_44 یا نوع کلید ECDSA_P256 توصیه میکنیم.
عنصر اولیه «امضای دیجیتال» تضمین میکند که هیچکس دادههایتان را دستکاری نکرده باشد و ثابت میکند که دادهها ازطرف شما ارسال شده است. این کلید نامتقارن است و از کلید خصوصی برای امضا کردن دادهها و از کلید عمومی برای درستیسنجی آن استفاده میکند.
مثالهای زیر به شما کمک میکند تا بااستفاده از عنصر اولیه «امضای دیجیتال» شروع کنید:
C++
// 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/absl_check.h" #include "absl/status/status.h" #include "absl/status/statusor.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" 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; constexpr absl::string_view kSign = "sign"; constexpr absl::string_view kVerify = "verify"; void ValidateParams() { // ... } } // namespace namespace tink_cc_examples { // Digital signature example CLI implementation. absl::Status DigitalSignatureCli(absl::string_view mode, const std::string& keyset_filename, const std::string& input_filename, const std::string& signature_filename) { absl::Status result = crypto::tink::SignatureConfig::Register(); if (!result.ok()) return result; // Read the keyset from file. absl::StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = ReadJsonCleartextKeyset(keyset_filename); if (!keyset_handle.ok()) return keyset_handle.status(); // Read the input. absl::StatusOr<std::string> input_file_content = ReadFile(input_filename); if (!input_file_content.ok()) return input_file_content.status(); if (mode == kSign) { absl::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(); absl::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 absl::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. absl::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'; } ABSL_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://developers.google.com/tink/key-management-overview. 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() {} }
Obj-C
Python
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')
امضای دیجیتال
عنصر اولیه «امضای دیجیتال» به شما امکان میدهد درستیسنجی کنید که کسی دادههایتان را دستکاری نکرده باشد. این ویژگی اصالت و تمامیت دادههای امضاشده را فراهم میکند، اما محرمانه بودن آنها را تضمین نمیکند. این روش نامتقارن است، یعنی از یک جفت کلید (کلید عمومی و کلید خصوصی) استفاده میکند.
عنصر اولیه «امضای دیجیتال» دارای ویژگیهای زیر است:
- اصالت: ایجاد امضایی که
PublicKeyVerify.Verify(signature, message)آن را درستیسنجی کند غیرممکن است، مگر اینکه کلید خصوصی را داشته باشید. - نامتقارن: ایجاد امضا از کلید متفاوتی نسبتبه تأیید آن استفاده میکند. این کار به شما امکان میدهد کلید عمومی را برای درستیسنجی امضاها در اختیار طرفهایی قرار دهید که خودشان نمیتوانند امضا ایجاد کنند.
اگر به عدم تقارن نیاز ندارید، بهجای آن از عنصر اولیه سادهتر و کارآمدتر MAC استفاده کنید.
عملکرد امضاهای دیجیتال در Tink بهصورت جفتهای عناصر اولیه نشان داده میشود:
- برای امضای دادهها از PublicKeySign استفاده کنید
- PublicKeyVerify برای درستیسنجی امضا
اگر کلید خصوصی در جایی قرار دارد که پیام نمیتواند بهراحتی به آن ارسال شود، مثل HSM یا KMS، به Prehash و SignPrehash مراجعه کنید که امضا را به یک مرحله درهمسازی فقط با کلید عمومی و یک مرحله امضا فقط با کلید خصوصی تقسیم میکند.
انتخاب نوع کلید
توصیه میکنیم برای اکثر موارد استفاده از ML_DSA_44 یا ECDSA_P256 استفاده کنید، اما گزینههای متنوعی وجود دارد. بهطورکلی، موارد زیر صادق است:
- ML-DSA-44 دربرابر تهدیدات کوانتومی ایمن است. ما بهطور فعال درحال پیادهسازی آن هستیم و اکنون برای زبانهای برنامهنویسی که در آنها دردسترس است توصیه میشود.
برای الگوریتمهای غیرپساکوانتومی زیر، میتوانید انتظار داشته باشید که در آینده نزدیک مجبور خواهید شد نوع کلید را تغییر دهید.
- ECDSA_P256 پرکاربردترین گزینه و پیشفرض معقولی است. توجه داشته باشید که امضاهای ECDSA قابل تغییر هستند.
- ED25519 امضاهای قطعی ایجاد میکند و عملکرد بهتری نسبت به ECDSA_P256 ارائه میدهد.
- RSA_SSA_PKCS1_3072_SHA256_F4 امضاهای قطعی ایجاد میکند و بهترین عملکرد درستیسنجی را ارائه میدهد (اما امضا کردن بسیار کندتر از ECDSA_P256 یا ED25519 است).
حداقل ضمانتهای امنیتی
- دادههایی که باید امضا شوند میتوانند طول دلخواه داشته باشند
- سطح امنیت ۱۲۸ بیتی دربرابر حملههای پیام انتخابی تطبیقی برای طرحهای مبتنی بر منحنی بیضوی
- سطح امنیتی ۱۱۲ بیتی دربرابر حملات پیام انتخابی تطبیقی برای طرحهای مبتنی بر RSA (اجازه میدهد از کلیدهای ۲۰۴۸ بیتی استفاده شود)
شکلپذیری
اگر مهاجمی بتواند امضای معتبر دیگری برای پیامی که قبلاً امضا شده است ایجاد کند، طرح امضا قابل تغییر است. اگرچه این موضوع برای اکثر سناریوها مشکلی ایجاد نمیکند، در برخی موارد برنامهنویسان بهطور ضمنی فرض میکنند که امضاهای معتبر یکتا هستند، و این میتواند منجر به نتایج غیرمنتظره شود.