1. 程式人生 > >Keystone幾種token生成的方式分析

Keystone幾種token生成的方式分析

.cn 組裝 cap ack 3-0 con 隨機 tput dom

從Keystone的配置文件中,我們可見,Token的提供者目前支持四種。 Token Provider:UUID, PKI, PKIZ, or Fernet

結合源碼及官方文檔,我們用一個表格來闡述一下它們之間的差異。

技術分享

接下去,從源碼上來進一步分析流程。

(1)首先,uuid,這種方式較簡單,沒有特別的加密、編碼流程,核心的生成過程,即 uuid.uuid4().hex,生成一串友好的序列。

(2)PKI的流程,首先需要使用 keystone-manage pki_setup命令生成CA及相關的令牌機制,其代碼如下所示:

def _get_token_id(self, token_data):
try:


token_json = jsonutils.dumps(token_data, cls=utils.PKIEncoder)
token_id = str(cms.cms_sign_token(token_json,
CONF.signing.certfile,
CONF.signing.keyfile)) #DEFAULT_TOKEN_DIGEST_ALGORITHM=sha256

其中,‘token_data’是獲取的user、role、endpoint、catlog等信息集合,而最重要的語句是cms使用簽名生成token的過程:cms_sign_token,使用默認的sha256方式加密,處理過程使用process,進行數據的讀取、處理,

process = subprocess.Popen([‘openssl‘, ‘cms‘, ‘-sign‘,
‘-signer‘, signing_cert_file_name,
‘-inkey‘, signing_key_file_name,
‘-outform‘, ‘PEM‘,
‘-nosmimecap‘, ‘-nodetach‘,
‘-nocerts‘, ‘-noattr‘,

‘-md‘, message_digest, ],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True)

最後output, err = process.communicate(data) 生成Token-id,這個過程涉及到openssl相關的加密技術,再次不作詳述。我們只需清楚這個生成的大致流程。

(3)PKIZ的Token生成流程,和PKI類似,即在它的基礎上進行的壓縮,核心邏輯如下:

def _get_token_id(self, token_data):
try:


token_json = jsonutils.dumps(token_data, cls=utils.PKIEncoder)
token_id = str(cms.pkiz_sign(token_json,
CONF.signing.certfile,
CONF.signing.keyfile))

和PKI不同的是采用cms.pkiz_sign這個過程,進一步看這個過程,

def pkiz_sign(text,
signing_cert_file_name,
signing_key_file_name,
compression_level=6,
message_digest=DEFAULT_TOKEN_DIGEST_ALGORITHM):
signed = cms_sign_data(text,


signing_cert_file_name,
signing_key_file_name,
PKIZ_CMS_FORM,
message_digest=message_digest)

compressed = zlib.compress(signed, compression_level)
encoded = PKIZ_PREFIX + base64.urlsafe_b64encode( #PKIZ_PREFIX =pkiz_
compressed).decode(‘utf-8‘)
return encoded

在PKI原來的基礎上,引入了ZIib壓縮,並且使用pkiz_作為token的開頭。

(4)Fernet的技術,也需要首先使用命令keystone-manage fernet_setup生成必要的令牌信息,只是它有0,1版本的區分,並且可以指定版本的個數,默認是不超過3個。

(猜測這個是為了提高安全性,和輪詢替換相關)Fernet的Token-id生成過程,和參數scope有很大的關系,它的核心代碼中,支持Project模式、Domain模式、無模式等等,處理方法略有差異,因此Fernet的Token的邏輯,與用戶的項目、域有很大的關系,Token-id的生成隨機因素降低了。我們以其中Projec的模式進行分析。

payload = FederatedProjectScopedPayload.assemble(
    user_id,
    methods,
    project_id,
    expires_at,
    audit_ids,
    federated_info)
versioned_payload = (version,) + payload
serialized_payload = msgpack.packb(versioned_payload)
token = self.pack(serialized_payload)
從上面的代碼中,可見首先是對負載數據payload的處理,組裝過程,(應該是一個數理學家設計的。。。)
b_user_id = cls.attempt_convert_uuid_hex_to_bytes(user_id)  
             #user_id 傳進來前 u‘b8aff9260dba489a831300630da4079f‘ ->後 UUID(‘b8aff926-0dba-489a-8313-00630da4079f‘)
methods = auth_plugins.convert_method_list_to_integer(methods)  列表對應int的方法對應的是2*int(methods)
b_project_id = cls.attempt_convert_uuid_hex_to_bytes(project_id)  
expires_at_int = cls._convert_time_string_to_int(expires_at)   過期時間的整數形式表示
b_audit_ids = list(map(provider.random_urlsafe_str_to_bytes,   
  	                           audit_ids))                 審計為列表,並用隨機的二進制生成

組裝過程返回一個列表,進一步組成帶有version的負載,然後加密crypto.encrypt(payload).rstrip(‘=‘),最後然後進行編碼處理,返回Token-id。

通過以上四種的Token生成方式分析,可見每種方式都有優缺點,如果希望少去和Keystone交互,減少認證請求帶寬,可以采用PKI、PKIZ、Fernet的方式,而這幾種方式的邏輯各有差異,涉及到加密方法的安全性,CA的權威性,這都需要進一步斟酌,uuid是keystone目前版本默認的安裝方式,其使用簡單,只是存在每次請求token都需要和keytone交互。總之,它們的存在都有自己的優跟缺。

Keystone幾種token生成的方式分析