เราขอแนะนําให้ใช้พรอมต์ลายเซ็นดิจิทัลที่มีประเภทคีย์ ECDSA_P256 สําหรับกรณีการใช้งานส่วนใหญ่
องค์ประกอบพื้นฐานของลายเซ็นดิจิทัลช่วยให้มั่นใจว่าไม่มีใครดัดแปลงข้อมูลของคุณ และพิสูจน์ว่าข้อมูลนั้นมาจากคุณ การเข้ารหัสแบบไม่สมมาตรจะใช้คีย์ส่วนตัวเพื่อลงนามในข้อมูลและคีย์สาธารณะเพื่อยืนยัน
ตัวอย่างต่อไปนี้จะช่วยคุณเริ่มต้นใช้งานพรอมต์ลายเซ็นดิจิทัล
// 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')
ลายเซ็นดิจิทัล
องค์ประกอบพื้นฐานของลายเซ็นดิจิทัลช่วยให้คุณยืนยันได้ว่าไม่มีใครได้ดัดแปลงข้อมูลของคุณ ซึ่งจะสร้างความน่าเชื่อถือและความสมบูรณ์ให้กับข้อมูลที่เซ็นชื่อ แต่จะไม่รักษาความลับ ซึ่งเป็นแบบไม่สมมาตร ซึ่งหมายความว่าจะใช้คู่คีย์ (คีย์สาธารณะและคีย์ส่วนตัว)
องค์ประกอบพื้นฐานลายเซ็นดิจิทัลมีพร็อพเพอร์ตี้ต่อไปนี้
- ความถูกต้อง: การสร้างลายเซ็นที่
PublicKeyVerify.Verify(signature, message)
จะตรวจสอบได้นั้นเป็นไปไม่ได้ เว้นแต่คุณจะมีคีย์ส่วนตัว - แบบไม่สมมาตร: การสร้างลายเซ็นใช้คีย์อื่นนอกเหนือจากการยืนยันลายเซ็น ซึ่งจะช่วยให้คุณแจกจ่ายคีย์สาธารณะเพื่อยืนยันลายเซ็นแก่บุคคลที่ไม่สามารถสร้างลายเซ็นได้
หากไม่ต้องการความไม่สมมาตร ให้พิจารณาใช้พรอมต์ MAC พื้นฐานที่ง่ายและมีประสิทธิภาพมากกว่าแทน
ฟังก์ชันการทำงานของลายเซ็นดิจิทัลจะแสดงใน Tink เป็นคู่ของพรอมิเตอต่อไปนี้
- PublicKeySign สําหรับการรับรองข้อมูล
- PublicKeyVerify สําหรับการยืนยันลายเซ็น
เลือกประเภทคีย์
เราขอแนะนำให้ใช้ ECDSA_P256 สำหรับกรณีการใช้งานส่วนใหญ่ แต่ก็มีตัวเลือกอื่นๆ อีกมากมาย โดยทั่วไปแล้ว ข้อมูลต่อไปนี้เป็นจริง
- ECDSA_P256 เป็นตัวเลือกที่ใช้กันอย่างแพร่หลายที่สุดและเป็นค่าเริ่มต้นที่เหมาะสม อย่างไรก็ตาม โปรดทราบว่าลายเซ็น ECDSA ปรับเปลี่ยนได้
- ED25519 จะสร้างลายเซ็นแบบกำหนดได้และมีประสิทธิภาพดีกว่า ECDSA_P256
- RSA_SSA_PKCS1_3072_SHA256_F4 จะสร้างลายเซ็นแบบกำหนดได้และให้การยืนยันประสิทธิภาพที่ดีที่สุด (แต่การรับรองจะช้ากว่า ECDSA_P256 หรือ ED25519 มาก)
การรับประกันความปลอดภัยขั้นต่ำ
- ข้อมูลที่จะเซ็นชื่อมีความยาวเท่าใดก็ได้
- ระดับความปลอดภัย 128 บิตเพื่อป้องกันการโจมตีแบบเลือกข้อความแบบปรับเปลี่ยนได้สำหรับรูปแบบที่อิงตามรูปไข่
- ระดับความปลอดภัย 112 บิตเพื่อป้องกันการโจมตีแบบเลือกข้อความแบบปรับเปลี่ยนได้สำหรับรูปแบบที่อิงตาม RSA (อนุญาตคีย์ 2048 บิต)
ความสามารถในการปรับเปลี่ยน
รูปแบบลายเซ็นจะเปลี่ยนรูปแบบได้หากผู้โจมตีสร้างลายเซ็นที่ถูกต้องรูปแบบอื่นสำหรับข้อความที่ลงนามแล้วได้ แม้ว่านี่จะไม่ได้เป็นปัญหาสำหรับสถานการณ์ส่วนใหญ่ แต่ในบางกรณี นักเขียนโปรแกรมจะถือว่าลายเซ็นที่ถูกต้องนั้นไม่ซ้ำกันโดยปริยาย ซึ่งอาจทำให้เกิดผลลัพธ์ที่ไม่คาดคิด