Tink는 키 세트를 사용하여 키 순환을 사용 설정합니다. 공식적으로 키 세트는 하나의 키가 기본 키로 지정된 비어 있지 않은 키 목록1입니다 (예: 새 일반 텍스트에 서명하고 암호화하는 데 사용되는 키). 또한 키 세트의 키는 고유 ID2와 키 세트에서 키를 삭제하지 않고도 키를 사용 중지할 수 있는 키 상태를 갖습니다.
키 집합은 사용자가 KeysetHandle 클래스를 통해 키에 액세스하는 기본 방법입니다. 이렇게 하면 모든 사용자에게 한 번에 여러 키를 처리하는 코드가 있습니다. 암호화를 사용하는 대부분의 사용자에게는 여러 키를 처리하는 것이 필수입니다. 키를 변경할 수 있어야 하며 (예: 이전 키가 유출될 수 있음) 코드가 실행되는 머신과 모든 암호문에 전 세계적으로 즉시 적용할 수 있는 원자적 '다음 키로 전환'은 거의 없습니다. 따라서 사용자는 한 키에서 다른 키로 변경될 때 작동하는 코드를 작성해야 합니다.
예: AEAD
AEAD 프리미티브의 키가 여러 개 포함된 AEAD 키 집합을 생각해 보세요. 앞에서 설명한 대로 각 키는 \(\mathrm{Enc}\) 및 \(\mathrm{Dec}\)라는 두 가지 함수를 고유하게 지정합니다. 이제 키 세트는 두 가지 새로운 함수인 \(\mathrm{Enc}\) 및 \(\mathrm{Dec}\) 도 지정합니다. \(\mathrm{Enc}\) 는 키 세트의 기본 키 함수 \(\mathrm{Enc}\) 와 같고, \(\mathrm{Dec}\) 는 모든 키를 사용하여 복호화하려고 시도하며 특정 순서로 키를 확인합니다 (Tink가 이 작업의 성능을 개선하는 방법은 아래 참고).
키 세트는 전체 키입니다. 즉, 사용되는 \(\mathrm{Enc}\) 및\(\mathrm{Dec}\) 함수에 관한 완전한 설명입니다. 즉, 사용자는 KeysetHandle를 입력으로 사용하는 클래스를 작성하여 클래스가 객체의 전체 설명 \(\mathrm{Enc}\) 이 필요하며 \(\mathrm{Dec}\) 올바르게 작동한다는 아이디어를 표현할 수 있습니다. 이를 통해 사용자는 이 클래스를 사용하려면 암호화 원시 항목에 관한 설명을 제공해야 한다는 것을 전달하는 API를 작성할 수 있습니다.
키 순환
먼저 KMS에서 키 세트를 가져오고, 이 키 세트에서 AEAD 객체를 만든 다음, 이 객체를 사용하여 암호문을 암호화하고 복호화하는 프로그램을 작성하는 Tink 사용자를 생각해 보세요.
이러한 사용자는 키 순환을 위해 자동으로 준비되며 현재 선택한 알고리즘이 더 이상 표준을 충족하지 않는 경우 알고리즘을 전환합니다.
하지만 이러한 키 순환을 구현할 때는 다소 주의해야 합니다. 먼저 KMS는 키 집합에 새 키를 추가해야 하지만 아직 기본 키로 설정해서는 안 됩니다. 그런 다음 이 키 세트를 사용하는 모든 바이너리에 새 키 세트를 출시해야 이 키 세트를 사용하는 모든 바이너리에 키 세트의 최신 키가 포함됩니다. 그런 다음에만 새 키를 기본 키로 설정해야 하며, 결과 키 세트는 키 세트를 사용하는 모든 바이너리에 다시 배포됩니다.
암호문의 키 식별자
AEAD 키 집합의 예를 다시 살펴보겠습니다. 순진하게 암호문을 복호화하려면 키 세트를 암호화하는 데 사용된 키를 알 수 없으므로 Tink가 키 세트의 모든 키를 사용하여 복호화해야 합니다. 이로 인해 상당한 성능 오버헤드가 발생할 수 있습니다.
따라서 Tink에서는 ID에서 파생된 5바이트 문자열을 암호문에 접두사로 추가할 수 있습니다. 위의 '전체 키' 철학에 따라 이 접두사는 키의 일부이며 이 키로 파생된 모든 암호문에는 이 접두사가 있어야 합니다. 사용자는 키를 만들 때 키에 이러한 접두사를 사용해야 하는지 또는 접두사가 없는 암호문 형식을 사용해야 하는지 선택할 수 있습니다.
키가 키 세트에 있으면 Tink는 키 세트에 있는 키의 ID에서 이 태그를 계산합니다. 키 세트 내에서 ID가 고유2하다는 사실은 태그가 고유하다는 것을 의미합니다. 따라서 태그된 키만 사용하면 단일 키로 복호화할 때와 비교하여 성능이 저하되지 않습니다. Tink는 복호화할 때 키 중 하나만 시도하면 됩니다.
그러나 태그는 키의 일부이므로 키에 특정 ID가 하나만 있는 경우에만 키가 키 세트에 있을 수 있음을 의미합니다. 이는 여러 언어로 키 객체의 구현을 설명할 때 몇 가지 의미가 있습니다.
Tink의 일부는 여전히 키 세트를 세트로 취급합니다. 하지만 이는 변경되어야 합니다.
순서가 일반적으로 중요하기 때문입니다. 예를 들어 Aead를 사용한 키 순환의 일반적인 수명 주기를 생각해 보세요. 먼저 새 키가 키 세트에 추가됩니다. 이 키는 아직 기본 키가 아니지만 활성 상태입니다. 이 새 키 세트는 모든 바이너리에 적용됩니다. 모든 바이너리가 새 키를 알게 되면 키가 기본 키가 됩니다 (이 시점에서만 이 키를 사용해도 안전함). 이 두 번째 단계에서 키 순환은 마지막으로 추가된 키를 알아야 합니다. ↩
Google 내부 라이브러리와의 호환성을 위해 Tink는 ID가 반복되는 키 세트를 허용합니다. 이 지원은 향후 삭제될 예정입니다. ↩
[null,null,["최종 업데이트: 2025-07-25(UTC)"],[[["\u003cp\u003eIn Tink, a Keyset is a list of keys, with one primary key and unique IDs for key rotation and management.\u003c/p\u003e\n"],["\u003cp\u003eKeysets are crucial for enabling key rotation without requiring users to implement it themselves.\u003c/p\u003e\n"],["\u003cp\u003eTink optimizes decryption by using key identifiers in ciphertexts to avoid trying all keys in a keyset.\u003c/p\u003e\n"],["\u003cp\u003eWhile Keysets offer significant advantages, users should consider careful key rotation procedures.\u003c/p\u003e\n"],["\u003cp\u003eTink primarily uses Keysets for key access, ensuring user code can handle multiple keys, which is often necessary for robust cryptography.\u003c/p\u003e\n"]]],["Tink uses **Keysets**, a list of keys with one designated as primary, to enable key rotation. Keyset keys have unique IDs and a status, allowing for disabling without removal. Encryption uses the primary key, while decryption iterates through all keys. Ciphertexts can be prefixed with a unique 5-byte tag derived from the key's ID to optimize decryption. Key rotation involves adding a new key, distributing the updated keyset, then setting the new key as primary, ensuring all binaries are updated.\n"],null,["# Keysets\n\n| In Tink, a **Keyset** is a list of keys, with one designated primary key.\n\nTink uses Keysets to enable key rotation. Formally, a keyset is a non-empty\nlist^[1](#fn1)^ of keys in which one key is designated primary (the key\nwhich is used for example to sign and encrypt new plaintexts). In addition, keys\nin a keyset get a unique ID^[2](#fn2)^ and a key status which allows\nto disable keys without removing them from a keyset.\n\nKeysets are the main way in which users can access keys (via the class\n`KeysetHandle`). This ensures that every user has code to handle multiple keys\nat once. For most users of cryptography, handling multiple keys is\n*a necessity*: it needs to be possible to change keys (old keys can be leaked,\nfor example), and there is almost never an atomic \"switch to the next key\"\nwhich can be applied to the machines the code runs and all ciphertexts,\nglobally, and in an instant. Hence, the user needs to write code which\nworks when one changes from one key to the next one.\n| **Note:** The full support of Keysets is one big advantage Tink has over other libraries: they facilitate key rotation, which otherwise every user would have to implement by themselves.\n\nExample: AEAD\n-------------\n\nConsider an AEAD keyset, which contains multiple keys for the AEAD\nprimitive. As explained before, each key uniquely specifies two functions:\n\\\\(\\\\mathrm{Enc}\\\\) and \\\\(\\\\mathrm{Dec}\\\\). The keyset now also specifies two new\nfunctions: \\\\(\\\\mathrm{Enc}\\\\) and \\\\(\\\\mathrm{Dec}\\\\) - \\\\(\\\\mathrm{Enc}\\\\) simply\nequals the function \\\\(\\\\mathrm{Enc}\\\\) of the primary key of the keyset, while\nthe function \\\\(\\\\mathrm{Dec}\\\\) tries to decrypt with all keys, going through\nthem in some order (see [below](#keyids) for how Tink improves the performance\nof this).\n\nIt is interesting to note that Keysets are *full keys* :\nthey are a complete description of the functions \\\\(\\\\mathrm{Enc}\\\\) and\n\\\\(\\\\mathrm{Dec}\\\\) used. This means that users can write a class which takes as\ninput a `KeysetHandle`, expressing the idea that the class needs a *complete\ndescription of objects \\\\(\\\\mathrm{Enc}\\\\) and \\\\(\\\\mathrm{Dec}\\\\) to function\nproperly*. This enables user to write APIs which communicate that: to use\nthis class you need to provide me the description of a cryptographic primitive.\n\nKey rotation\n------------\n\nConsider a Tink user, writing a program which first obtains a keyset from\na KMS, then creates an AEAD object from this keyset, and finally uses this\nobject to encrypt and decrypt ciphertexts.\n\nSuch a user is automatically prepared for key rotation; and switching\nalgorithms in case their current choice does not meet the standard anymore.\n\nOne has to be somewhat careful though when implementing such key rotation:\nFirst, the KMS should add a new key to the keyset (but not yet set it as a\nprimary). Then, the new keyset needs to be rolled out to\nall binaries, so that every binary using this keyset has the newest key in\nthe keyset. Only then should the new key be made primary, and the resulting\nkeyset is again distributed to all binaries using the keyset.\n\nKey identifiers in ciphertexts\n------------------------------\n\nConsider again the example of an AEAD keyset. If done naively,\ndecrypting a ciphertext requires Tink to try to decrypt with all the keys in\nthe Keyset, as there is no way of knowing which key was used to encrypt the\nkeyset. This can cause a large performance overhead.\n\nBecause of this, Tink allows to prefix ciphertexts with a 5-byte string derived\nfrom the ID. Following the philosophy of 'Full Keys' above, this prefix\nis *part of the key*,\nand all ciphertexts ever derived with this key should have this prefix. When\nusers create keys, they can choose whether the key should use such a prefix, or\nif a ciphertext format without it should be used.\n| **Note:** Some primitives don't allow such tagging; for example in a [PRF](https://en.wikipedia.org/wiki/Pseudorandom_function_family) there is no obvious way how to embed such a prefix. For these keys, Tink does not offer such a facility.\n\nWhen a key is in a keyset, Tink computes this tag from the ID the key has in the\nkeyset. The fact that IDs are unique[^2^](#fn2) within a keyset implies\nthat the tags are unique. Hence, if only tagged keys are used, there is no\nperformance\nloss compared to decrypting with a single key: Tink only needs to try one of\nthe keys when decrypting.\n\nHowever, since the tag is part of the key, this also implies that the key can\nonly be in a keyset if it has one specific ID. This has some implications when\ndescribing the implementation of [key objects](/tink/design/key_objects) in\ndifferent languages. \n\n*** ** * ** ***\n\n1. Some parts of Tink still treat Keysets as a set. However, this\n should be changed.\n\n The reason is that the order is in general\n important: for example, consider the typical lifecycle of a key rotation with\n Aead. First, a new key is added to a keyset. This key is not made primary yet,\n but active. This new keyset is rolled out to all binaries. Once all binaries\n know the new key, the key is made primary (only at this point using this key\n is safe). In this second step, key rotation needs to know the last key added. [↩](#fnref1)\n\n2. For compatibility with a Google internal library, Tink\n allows to have keysets in which IDs are repeated. This support will be\n removed in the future. [↩](#fnref2)"]]