Requests: Укажите пароль для SSL-сертификата на стороне клиента

Созданный на 3 сент. 2013  ·  121Комментарии  ·  Источник: psf/requests

Насколько мне известно, в настоящее время невозможно указать пароль для сертификата на стороне клиента, который вы используете для аутентификации.
Это небольшая проблема, потому что обычно вы всегда хотите защитить паролем свой файл .pem, который содержит закрытый ключ. openssl даже не позволит вам создать его без пароля.

Documentation Planned

Самый полезный комментарий

@botondus Я думаю, что нашел более простой способ добиться этого с помощью библиотеки запросов. Я документирую это для других людей, которые столкнулись с этой проблемой.

Я предполагаю, что у вас есть сертификат .p12 и кодовая фраза для ключа.

Сгенерируйте сертификат и закрытый ключ.

// Generate the certificate file.
openssl pkcs12 -in /path/to/p12cert -nokeys -out certificate.pem
// Generate private key with passpharse, First enter the password provided with the key and then an arbitrary PEM password //(say: 1234) 
openssl pkcs12 -in /path/to/p12cert -nocerts -out privkey.pem

Что ж, мы еще не закончили, и нам нужно сгенерировать ключ, который не требует пароля PEM каждый раз, когда ему нужно разговаривать с сервером.

Сгенерировать ключ без парольной фразы.

// Running this command will prompt for the pem password(1234), on providing which we will obtain the plainkey.pem
openssl rsa -in privkey.pem -out plainkey.pem

Теперь у вас будут certificate.pem и plainkey.pem , оба файла, необходимые для взаимодействия с API с помощью запросов.

Вот пример запроса с использованием этих сертификатов и ключей.

import requests
url = 'https://exampleurl.com'
headers = {
            'header1': '1214141414',
            'header2': 'adad-1223-122'
          }
response = requests.get(url, headers=headers, cert=('~/certificate.pem', '~/plainkey.pem'), verify=True)
print response.json()

Надеюсь это поможет:

cc @kennethreitz @Lukasa @ sigmavirus24

Все 121 Комментарий

Что-то вроде:

requests.get('https://kennethreitz.com', cert='server.pem', cert_pw='my_password')

Совершенно уверен, что вы должны использовать для этого параметр cert : cert=('server.pem', 'my_password')

@ sigmavirus24
Кортеж для (certificate, key) . В настоящее время нет поддержки зашифрованных файлов ключей.
Stdlib получил поддержку только для версии 3.3.

Хех, @ t-8ch, вы случайно связались с файлом в локальной FS. ;) Правильная ссылка .

Совершенно верно @ t-8ch. Вот почему я никогда не должен отвечать на вопросы из автобуса. : /

Итак, текущий консенсус заключается в том, что мы этого не поддерживаем. Сколько работы потребуется, чтобы добавить поддержку в версии Python, отличные от 3.3?

Насколько сложно было бы выдать ошибку при таком условии? Я только что столкнулся с этой глупой проблемой, и мне потребовалось два часа, чтобы понять, было бы неплохо, если бы она выдала ошибку, в настоящее время она просто зацикливается. Спасибо за отличную библиотеку!

Постойте, где петля сидит? Где в исполнении мы терпим поражение? Можете ли вы распечатать трассировку, откуда мы зацикливаемся?

Вроде вот тут висит:

r = requests.get (url,
auth = headeroauth,
cert = self.cert_tuple,
заголовки = заголовки,
таймаут = 10,
verify = True)

Я попытался увеличить или уменьшить тайм-аут, но безрезультатно, но я полагаю, что до истечения тайм-аута он хорошо знает, что не может использовать сертификат. Спасибо!

Ах, извините, я не понял. Я хотел позволить ему зависнуть, а затем убить его с помощью Ctrl + C, чтобы python генерировал исключение KeyboardInterrupt, а затем чтобы увидеть, где мы находимся в трассировке. Я хочу знать, где в запросах останавливается выполнение.

Что происходит (или, по крайней мере, что я видел во многих случаях), так это то, что OpenSSL, получив сертификат, защищенный паролем, предложит пользователю ввести пароль. Он не отображается в журналах (потому что приглашение печатается напрямую), и время ожидания не истекает, потому что он ждет, пока пользователь нажмет клавишу ввода.

Излишне говорить, что это неуклюжее и опасное поведение, когда код выполняется на сервере (потому что это приведет к зависанию вашего воркера без возможности восстановления, кроме как убить процесс).

Есть ли способ заставить запросы вызывать исключение в этом случае вместо запроса пароля, или это полностью вне вашего контроля и в руках OpenSSL?

@maxnoel Я почти уверен, что это в руках OpenSSL, но если вы ответите на вопрос @Lukasa (последний комментарий по этой проблеме), было бы очень полезно дать определенный ответ относительно того, можем ли мы чем-то помочь .

Вы можете подтвердить, что OpenSSL блокирует stdin для ключевой фразы из интерактивной подсказки Python:

>>> r = requests.get("https://foo.example.com/api/user/bill", cert=("client.crt", "client.key"))
Enter PEM pass phrase:
>>>

Если вы запускаете фоновый процесс, я предполагаю, что OpenSSL заблокирует ожидание этого ввода.

Это правильно. Могут ли запросы сделать что-нибудь, чтобы этого не произошло? Создание исключения при отсутствии пароля было бы гораздо более полезным, чем запрос чего-либо на стандартном вводе (особенно в неинтерактивной программе).

Боюсь, что не знаю никакого способа. @reaperhulk?

Есть способы помешать OpenSSL делать это, но я не уверен, раскрываются ли они pyOpenSSL. Где запросы вызывают pyopenssl для загрузки сертификата клиента? Я могу немного покопаться.

@reaperhulk Это делается в urllib3, здесь .

Мы также делаем что-то очень похожее для stdlib, что будет отдельной проблемой.

Таким образом , мы можем сделать это с помощью PyOpenSSL патч , как это . В версии stdlib нам нужно использовать load_cert_chain с паролем.

Эта проблема решена? В настоящее время я сталкиваюсь с этим, пытаясь подключиться к серверу Apache.

Это не так.

Как насчет контейнеров в формате PKCS # 12 (и зашифрованных), которые могут содержать сертификат / ключ клиента? Подпадает ли это под тот же запрос функции?

@mikelupo Ага .

@telam @mikelupo
У меня та же проблема, и я много гуглил, наконец, я решил ее с помощью pycurl.
В моей ситуации я использую openssl для преобразования моего файла .pfx в файл .pem, который содержит как сертификат, так и ключ (зашифрованный парольной фразой), а затем вызываю следующий код.

import pycurl
import StringIO

b = StringIO.StringIO()
c = pycurl.Curl()
url = "https://example.com"
c.setopt(pycurl.URL, url)
c.setopt(pycurl.WRITEFUNCTION, b.write)
c.setopt(pycurl.CAINFO, "/path/cacert.pem")
c.setopt(pycurl.SSLKEY, "/path/key_file.pem")
c.setopt(pycurl.SSLCERT, "/path/cert_file.pem")
c.setopt(pycurl.SSLKEYPASSWD, "your pass phrase")
c.perform()
c.close()
response_body = b.getvalue()

Кстати, для безопасности лучше не делать жесткого кода для pass phrase

Конечно. Тем не менее, проблема на самом деле не в том, что требуется парольная фраза, а в том, что OpenSSL заставляет вашу программу зависать, ожидая, когда кто-то наберет парольную фразу в stdin, даже в случае неинтерактивной, графической или удаленной программы.

Если требуется кодовая фраза, но ее не указано, вместо нее должно быть возбуждено исключение.

если вы используете пароль по умолчанию для ключа, openssl не зависнет.
он вернет неверный текст пароля. вы можете сразу изменить свой поток py
чтобы затем уведомить пользователя без этой явной остановки

любой план по добавлению этой функции

Мы хотим добавить его, но у нас нет расписания, чтобы добавить его в настоящее время.

@botondus Я думаю, что нашел более простой способ добиться этого с помощью библиотеки запросов. Я документирую это для других людей, которые столкнулись с этой проблемой.

Я предполагаю, что у вас есть сертификат .p12 и кодовая фраза для ключа.

Сгенерируйте сертификат и закрытый ключ.

// Generate the certificate file.
openssl pkcs12 -in /path/to/p12cert -nokeys -out certificate.pem
// Generate private key with passpharse, First enter the password provided with the key and then an arbitrary PEM password //(say: 1234) 
openssl pkcs12 -in /path/to/p12cert -nocerts -out privkey.pem

Что ж, мы еще не закончили, и нам нужно сгенерировать ключ, который не требует пароля PEM каждый раз, когда ему нужно разговаривать с сервером.

Сгенерировать ключ без парольной фразы.

// Running this command will prompt for the pem password(1234), on providing which we will obtain the plainkey.pem
openssl rsa -in privkey.pem -out plainkey.pem

Теперь у вас будут certificate.pem и plainkey.pem , оба файла, необходимые для взаимодействия с API с помощью запросов.

Вот пример запроса с использованием этих сертификатов и ключей.

import requests
url = 'https://exampleurl.com'
headers = {
            'header1': '1214141414',
            'header2': 'adad-1223-122'
          }
response = requests.get(url, headers=headers, cert=('~/certificate.pem', '~/plainkey.pem'), verify=True)
print response.json()

Надеюсь это поможет:

cc @kennethreitz @Lukasa @ sigmavirus24

Я слышал, что Amazon делает именно это внутри компании.

Я тоже столкнулся с этой проблемой. Меня беспокоит то, что я не хочу хранить простой закрытый ключ к файловой системе (может быть риск кражи другими). Поэтому, на мой взгляд, более расширяемый способ реализации этого - поддержка использования чего-то вроде PEM encoded string of private key вместо пути к файлу для указания закрытого ключа. Просто оставили разработчикам шифрование / расшифровку закрытого ключа / сертификата в их пользу.
После прочтения исходного кода запросов кажется, что это не совсем просто реализовать, поскольку запросы зависят от ssl-библиотеки python, которая поддерживает только файл сертификата / закрытого ключа. Мне просто интересно, можем ли мы использовать pyopenssl вместо python stdlib? pyopenssl имеет оболочку соединения openssl, см. https://pyopenssl.readthedocs.io/en/latest/api/ssl.html#connection -objects. Таким образом, мы можем использовать объект pkey в качестве закрытого ключа вместо пути к файлу.

Запросы уже поддерживают PyOpenSSL, если он и несколько других необходимых зависимостей установлены. Однако это никогда не станет обязательным: для нас важно, чтобы мы хорошо работали со стандартной библиотекой.

В будущем выпуске у нас будет поддержка передачи объектов SSLContext в urllib3 для обработки TLS: это включит эту функцию.

Для тех, кто сталкивается с этой проблемой, пока запросы не добавят возможность передавать ssl.SSLContext / OpenSSL.SSL.Context в urllib3, вот обходной путь, который фактически поддерживает использование зашифрованного сертификата / ключевого файла (требуется, чтобы PyOpenSSL был установлен и использовался вместо стандартной библиотеки ssl, который должен быть, если он установлен)

import requests

 # Get the password from the user/configfile/whatever
password = ...

# Subclass OpenSSL.SSL.Context to use a password callback that gives your password
class PasswordContext(requests.packages.urllib3.contrib.pyopenssl.OpenSSL.SSL.Context):
    def __init__(self, method):
        super(PasswordContext, self).__init__(method)
        def passwd_cb(maxlen, prompt_twice, userdata):
            return password if len(password) < maxlen else ''
        self.set_passwd_cb(passwd_cb)

# Monkey-patch the subclass into OpenSSL.SSL so it is used in place of the stock version
requests.packages.urllib3.contrib.pyopenssl.OpenSSL.SSL.Context = PasswordContext

# Use requests as normal, e.g.
endpoint = 'https://example.com/authenticated'
ca_certs = '/path/to/ca/certs/bundle'
certfile = '/path/to/certificate'
keyfile = '/path/to/encrypted/keyfile'
requests.get(endpoint, verify=ca_certs, cert=(certfile, keyfile))

@ahnolds : Это также работает для файлов PKCS # 12, или это только PEM?

@Lukasa : Действительно ли дело PKCS # 12 должно рассматриваться здесь, или мне следует открыть для этого отдельный вопрос?

PKCS # 12 - более сложная проблема, но в основном вам нужно будет сделать все необходимое для настройки вашего SSLContext.

@Lukasa : Я больше думал о предоставлении хорошего высокоуровневого API в запросах. Например, просто указав client_cert.p12 filename и пароль с помощью параметра cert=... keyword.

@vog Какой код, по вашему мнению, потребуется для этого?

@Lukasa Я не уверен насчет внутреннего устройства requests , поэтому, возможно, я недооцениваю то, что уже есть, но я думаю, что нужно сделать одно из следующих действий:

  • Либо у нас есть способ предоставить имя файла PKCS # 12 непосредственно нижним уровням (urllib3 и т. Д.). И, возможно, пароль тоже. (Потому что я не знаю никого, кто хочет, чтобы библиотека URL-адресов интерактивно просила администратора ввести свой пароль PKCS # 12 в инструменте, работающем на стороне сервера.)
  • Если это невозможно, нам нужно будет преобразовать PKCS # 12 (+ пароль) в PEM, а затем передать их на более низкие уровни. Это делается несколькими вызовами непосредственно привязки OpenSSL . Однако результатом является сертификат PEM в виде строки, и я еще не нашел способа предоставить (незашифрованный) PEM в виде строки нижним уровням (за исключением, возможно, использования оболочки OpenSSL / python «ssl» «buffer», например, wrap_bio , но это доступно только в последних версиях Python 3, но не в Python 2).
  • Так что, если это тоже невозможно, нам нужно будет не только преобразовать PKCS # 12 в PEM, но также создать временный файл, содержащий (незашифрованные) данные PEM.

Обратите внимание, что последний пункт - это то, чем я, по сути, сейчас занимаюсь, но мне это совсем не нравится. Почему я не могу предоставить OpenSSL простую строку, содержащую сертификат? Более того, почему я не могу просто передать имя файла и пароль PKCS # 12 нижним уровням?

Я собираюсь отметить @reaperhulk как эксперта по OpenSSL, но я понимаю, что для OpenSSL нет API-интерфейсов для загрузки сертификатов формата PKCS # 12 для клиентских сертификатов. Это означает, что нам нужно будет полностью перейти на PEM. Выполнение этого в памяти, безусловно, возможно, но в какой-то момент мне интересно, не хотим ли мы просто считать этого эксперта достаточно, чтобы мы делегировали его любому SSLContext, который вы нам передаете.

@Lukasa Спасибо, что серьезно

Вы хотите получить доступ к услуге через сертификат клиента. Почти везде вы получаете это как файл и пароль (где файл закодирован в PKCS # 12). В большинстве API, таких как стандартная библиотека Java, вы просто даете ей имя файла и пароль, и все готово.

Однако в Python это чертовски сложно.

Вот почему этим почти никто не занимается. Вместо этого они вручную конвертируют свой файл и пароль в файл PEM через OpenSSL и используют этот файл. Это административные издержки для каждого пользователя такого приложения. Потому что они не могут просто назвать файл (PKCS # 12) и пароль.

Я думаю, что библиотека requests должна сделать его как минимум таким же простым, как в Java.

requests уже отлично справляется с упрощением глупых сложных API, а вариант использования PKCS # 12 - всего лишь еще один пример глупого сложного API.

вариант использования PKCS # 12 - всего лишь еще один пример глупого сложного API.

Да, я совершенно не согласен с этим: я был бы полностью счастлив иметь какое-то решение для поддержки PKCS # 12 где-нибудь в стеке.

Я пытаюсь понять, какой код требуется для того, чтобы это работало, и, как результат, где его следует разместить. Мои рассуждения таковы:

  1. Вообще говоря, Requests добавляет к своей поверхности API только в том случае, если в этом есть существенная полезность (то есть, если он используется большим количеством людей или используется очень активно некоторыми), и если то, что мы делаем, трудно сделать правильно. или имеет тонкие крайние случаи.
  2. Обычно поддержка PKCS # 12 будет считаться добавлением к поверхности API, но если она вообще не меняет синтаксис cert= (просто расширяет то, что он будет поддерживать), и не приводит к регрессу поведения ( то есть мы можем достоверно определить разницу между файлами PKCS # 12 и файлами PEM, или мы можем легко обработать обе логические цепочки), я бы сказал, что это считается достаточно незначительным изменением на поверхности, которое, вероятно, того стоит .
  3. Однако есть и другие места, куда можно пойти. Например, на уровне транспортного адаптера, или в качестве помощника на панели инструментов запросов, или что-то еще.

Это означает, что я хочу взвесить, насколько это тонко, насколько сложен код, требует ли он дополнительных зависимостей, а затем использовать эту информацию, чтобы решить, где лучше всего разместить код. Например, у меня сейчас _подозрение_, что стандартная библиотека не может обрабатывать PKCS # 12, что означало бы, что _at best_ Запросы могли бы использовать PKCS # 12 только с установленным [security] extra. В еще худшем случае у нас может вообще не быть функций, доступных в какой-либо привязке OpenSSL, и в этом случае нам придется сделать несколько настоящих сумасшедших вещей, чтобы заставить его работать. Вот почему я хотел, чтобы @reaperhulk высказался : он, вероятно, сможет прояснить это для нас быстрее, чем я смогу провести исследование.

Я хотел бы, чтобы эта поддержка была добавлена: мне просто нужно, чтобы некоторые люди, которые знают, в чем заключается объем работы, прокомментировали здесь и дали мне знать, насколько велика гора, которую нам нужно переместить на самом деле.

Еще одна деталь для реализации PKCS # 12: старые версии привязок Python OpenSSL не работают, если пароль указан как объект unicode вместо байтовой строки. Поэтому его следует преобразовать перед передачей в load_pkcs12() следующим образом:

if isinstance(password, unicode):
    password_bytes = password.encode('utf8')
else:
    password_bytes = password
pkcs12 = OpenSSL.crypto.load_pkcs12(pkcs12_data, password_bytes)

Полный конвертер может выглядеть так, где pkcs12_data должна быть байтовой строкой с двоичными данными, а password может быть байтовой строкой или строкой юникода:

def pkcs12_to_pem(pkcs12_data, password):
    # Old versions of OpenSSL.crypto.load_pkcs12() fail if the password is a unicode object
    if isinstance(password, unicode):
        password_bytes = password.encode('utf8')
    else:
        password_bytes = password
    p12 = OpenSSL.crypto.load_pkcs12(pkcs12_data, password_bytes)
    p12_cert = p12.get_certificate()
    p12_key = p12.get_privatekey()
    pem_cert = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, p12_cert)
    pem_key = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, p12_key)
    pem = pem_cert + pem_key
    return pem

Мне кажется, что обсуждение PKCS # 12 выходит за рамки первоначальной проблемы, поскольку вопрос, о котором идет речь, заключается в том, должны ли запросы поддерживать PKCS # 12 изначально. Я бы проголосовал за то, чтобы у него возникла отдельная проблема, но, очевидно, это зависит от ответственных людей.

Тем не менее, в качестве обходного пути, который не требует незашифрованных временных файлов, метод OpenSSL.crypto.dump_privatekey имеет необязательный параметр парольной фразы, поэтому таким образом вы можете получить копию зашифрованного закрытого ключа в формате PEM. Это свело бы это к проблеме зашифрованного PEM, с которой мы начали.

В качестве альтернативы, вы также, вероятно, могли бы написать хак, аналогичный тому, который я предлагал перед использованием метода use_privatekey для OpenSSL.SSL.Context . Сверху моей головы (непроверено) что-то вроде

# From somewhere
pkcs12_data = ...
password_bytes = ...

class Pkcs12Context(requests.packages.urllib3.contrib.pyopenssl.OpenSSL.SSL.Context):
    def __init__(self, method):
        super(PasswordContext, self).__init__(method)
        p12 = OpenSSL.crypto.load_pkcs12(pkcs12_data, password_bytes)
        self.use_certificate(p12.get_certificate())
        self.use_privatekey(p12.get_privatekey())
# Monkey-patch the subclass into OpenSSL.SSL so it is used in place of the stock version
requests.packages.urllib3.contrib.pyopenssl.OpenSSL.SSL.Context = Pkcs12Context

Затем просто используйте requests.get т. Д. Без указания сертификата, поскольку он уже обработан в конструкторе.

Сейчас просматриваю эту ветку. Перефразировка оригинала:

учитывая зашифрованный сертификат клиента в формате PEM, могут ли запросы обрабатывать предоставление пароля?

Поскольку это есть в текущих стандартных библиотеках, было бы здорово интегрировать эту опцию. Это было бы чрезвычайно ценно с точки зрения безопасности предприятия (где наши сертификаты зашифрованы и должны оставаться таковыми).

На этом этапе это можно сделать, передав пользовательский контекст ssl в urllib3 с помощью транспортного адаптера. Это может делать все, что позволяет стандартная библиотека ssl-контекста. Вы можете видеть пример прохождения пользовательского контекста здесь .

Я смог использовать .pfx и .p12 с запросами, преобразовав их в .pem с помощью временного файла. См. Https://gist.github.com/erikbern/756b1d8df2d1487497d29b90e81f8068

Если есть интерес, могу подать PR. Было бы здорово избежать временного файла и диспетчера контекста. Дайте мне знать.

Боюсь, что это вряд ли будет объединено, но обратите внимание, что теперь вы можете передавать контекст PyOpenSSL напрямую в запросы через транспортные адаптеры, поэтому вы можете обойти эту проблему.

Боюсь, что это вряд ли будет объединено, но обратите внимание, что теперь вы можете передавать контекст PyOpenSSL напрямую в запросы через транспортные адаптеры, поэтому вы можете обойти эту проблему.

извините за путаницу, но вы говорите, что поддержка pfx / p12 в целом, скорее всего, не будет объединена? (при условии, что все сделано правильно, через контекст и т. д.). Рад попробовать, но, очевидно, не стоит потраченного времени, если его не собираются объединить.

Я считаю, что «вряд ли будет объединено» было о решении для временного файла.

@erikbern Чтобы быть ясным, я рад подойти и объединить любое решение, которое работает отчасти последовательно. Например, приемлемым решением будет использование PKCS # 12 через модуль Contrib PyOpenSSL в urllib3.

Однако решение с временным файлом неприемлемо (как отмечает @vog). Это означает, что поддержка PKCS # 12 вряд ли будет работать со стандартной библиотекой, поскольку модуль стандартной библиотеки ssl не поддерживает его, и поэтому он не будет поддерживаться во всех конфигурациях запросов.

Звучит отлично. Я также согласен с тем, что временный файл плохой, поскольку хранение расшифрованных ключей на диске представляет угрозу безопасности. Могу взглянуть на это на следующей неделе. Спасибо за внимание к модулю ssl - если ограничение выходит за пределы requests тогда, очевидно, становится сложнее

Я изучил его, и модуль ssl добавил аргумент cadata котором вы можете передавать данные pem в виде необработанной строки: https://docs.python.org/3/library/ssl.html # ssl.SSLContext.load_verify_locations

Нам пришлось бы пропатчить urllib3 во множестве мест, чтобы эта работа работала, так что я могу начать с этого

@erikbern Для ясности, почти любое подобное решение будет работать лучше, если просто передать правильно настроенный объект SSLContext в urllib3 с помощью TransportAdapter .

https://github.com/kennethreitz/requests/issues/2519 кажется идентичным этой проблеме, поэтому их, вероятно, следует объединить

Любое обновление по этой проблеме. Я пытаюсь использовать сертификат клиента, который зашифрован паролем и не могу заставить его работать. Стоит ли искать другие варианты помимо запросов? Не могли бы вы ответить как можно скорее.

Мы документируем это? Я чувствую, что это наша самая востребованная функция.

Я считаю, что эта ветка началась в 2013 году, и я не нашел объяснения до конца. Ребята, вы предоставили возможность предоставить пароль? или это все еще продолжается?

Я пытаюсь использовать запросы в создаваемом мной продукте безопасности приложений. Так что любые указатели будут полезны

@AnoopPillai Вы проверяли этот комментарий? https://github.com/requests/requests/issues/1573#issuecomment -188125157

Да, я прочитал этот комментарий, это работа, но в моем случае я не хочу преобразовывать его в 2 файла сертификатов, так как это придется делать вне моего приложения. Более того, мы используем нечто вроде хранилища для хранения пароля для зашифрованного файла .pem.

Этот пароль извлекается приложением динамически во время выполнения, поэтому жесткое кодирование не требуется.

@AnoopPillai Хорошо.

@kennethreitz Нет, мы не документировали это.

@AnoopPillai Да, это прекрасно работает. Вам просто нужно использовать несколько крючков более низкого уровня. В этом случае мы позволяем передавать SSLContext в urllib3 непосредственно на уровне транспортного адаптера. Это позволяет вам получить доступ к базовым функциям, которые позволят вам предоставить парольную фразу или функцию парольной фразы. Вот как мы рекомендуем вам это поддержать.

Спасибо, Лукаса, что сообщил мне, что есть способ сделать это.
Я новичок в Python и использую версию 3.6. Не могли бы вы подсказать мне, где я могу найти такие варианты, как шифры для передачи пароля сертификата клиента.
@Erikbern Я не

@AnoopPillai Тебе понадобится load_cert_chain .

@ Лукаса, не

Извините, ребята, причиной может быть отсутствие опыта работы с python, но я не могу изменить код, который Лукаса объяснил выше. Мой код:

class DESAdapter(HTTPAdapter):
    """
    A TransportAdapter that re-enables 3DES support in Requests.
    """
    def init_poolmanager(self, *args, **kwargs):
        context = create_urllib3_context(load_cert_chain='rtmqa-clientid.pem',password='weblogic')
        kwargs['ssl_context'] = context
        return super(DESAdapter, self).init_poolmanager(*args, **kwargs)

    def proxy_manager_for(self, *args, **kwargs):
        context = create_urllib3_context(load_cert_chain='rtmqa-clientid.pem', password='weblogic')
        kwargs['ssl_context'] = context
        return super(DESAdapter, self).proxy_manager_for(*args, **kwargs)
s = requests.Session()
s.mount(url, DESAdapter())
r = s.get(url, headers=request_header).json()

и я получаю сообщение об ошибке
TypeError: create_urllib3_context () получил неожиданный аргумент ключевого слова load_cert_chain

Это ошибка, да. Вы хотите вызвать create_urllib3_context и получить его возвращаемое значение, а затем вызвать load_cert_chain для возвращенного объекта. Попробуйте поиграть с этими функциями в интерактивном интерпретаторе, чтобы увидеть, как они работают.

Urllib3..util.ssl_.py, установленный на моем Mac, не имеет последней опции для пароля.
это код

    if certfile:
        context.load_cert_chain(certfile, keyfile)
    if HAS_SNI:  # Platform-specific: OpenSSL with enabled SNI
        return context.wrap_socket(sock, server_hostname=server_hostname)

Параметр пароля отсутствует. Как мне обновить ssl_.py, чтобы получить последнюю версию?

@AnoopPillai Нет. Вызовите функцию без аргументов, затем вызовите load_cert_chain для возвращенного объекта. Изменять urllib3 не нужно.

Чтобы было ясно, вот так:

ctx = create_urllib3_context()
ctx.load_cert_chain(your_arguments_here)

Задокументируем это :)

@ erikbern Я попробовал ваше решение для временного файла, но у меня возникла следующая ошибка:

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/urllib3/contrib/pyopenssl.py", line 441, in wrap_socket
    cnx.do_handshake()
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/OpenSSL/SSL.py", line 1716, in do_handshake
    self._raise_ssl_error(self._ssl, result)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/OpenSSL/SSL.py", line 1456, in _raise_ssl_error
    _raise_current_error()
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/OpenSSL/_util.py", line 54, in exception_from_error_queue
    raise exception_type(errors)
OpenSSL.SSL.Error: [('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')]

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/urllib3/connectionpool.py", line 595, in urlopen
    self._prepare_proxy(conn)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/urllib3/connectionpool.py", line 816, in _prepare_proxy
    conn.connect()
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/urllib3/connection.py", line 326, in connect
    ssl_context=context)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/urllib3/util/ssl_.py", line 329, in ssl_wrap_socket
    return context.wrap_socket(sock, server_hostname=server_hostname)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/urllib3/contrib/pyopenssl.py", line 448, in wrap_socket
    raise ssl.SSLError('bad handshake: %r' % e)
ssl.SSLError: ("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/requests/adapters.py", line 440, in send
    timeout=timeout
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/urllib3/connectionpool.py", line 639, in urlopen
    _stacktrace=sys.exc_info()[2])
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/urllib3/util/retry.py", line 388, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='credit-cards-accounts-qa.kdc.capitalone.com', port=443): Max retries exceeded with url: /credit-cards-accounts/credit-cards/accounts/XqLuxBTABbIDvpw56ba34p2WV9JoWUSkPJ09hrBlWD8= (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",),))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/tsu892/Desktop/Office/Pythone-work/ASR-pythone/ASR-python3.6/test-request.py", line 48, in <module>
    r = requests.get(url, headers=request_header, cert=cert).json()
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/requests/api.py", line 72, in get
    return request('get', url, params=params, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/requests/api.py", line 58, in request
    return session.request(method=method, url=url, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/requests/sessions.py", line 508, in request
    resp = self.send(prep, **send_kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/requests/sessions.py", line 618, in send
    r = adapter.send(request, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/requests/adapters.py", line 506, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='credit-cards-accounts-qa.kdc.capitalone.com', port=443): Max retries exceeded with url: /credit-cards-accounts/credit-cards/accounts/XqLuxBTABbIDvpw56ba34p2WV9JoWUSkPJ09hrBlWD8= (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",),))

Ниже мой код:

import requests
import json
import OpenSSL.crypto
import tempfile
import os
import contextlib
import ssl

json_file='apiInput.json'
hdr_key=[]
hdr_value=[]
json_data=open(json_file)
data = json.load(json_data)
request_body={}
#pprint(data)
json_data.close()
request_data = data['request1']
request_header=request_data['header-data']
url=request_header['url']

@contextlib.contextmanager
def pfx_to_pem():
    print('inside pfx tp pem')
    with tempfile.NamedTemporaryFile(suffix='.pem') as t_pem:
        f_pem = open(t_pem.name, 'wb')
        fr_pfx = open('rtmqa-clientid.pfx', 'rb').read()
        p12 = OpenSSL.crypto.load_pkcs12(fr_pfx,'xxxxxxxxx')
        f_pem.write(OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, p12.get_privatekey()))
        f_pem.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, p12.get_certificate()))
        ca = p12.get_ca_certificates()
        if ca is not None:
            for cert in ca:
                f_pem.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert))
        f_pem.close()
        yield t_pem.name

with pfx_to_pem() as cert:
    print(cert)
    r = requests.get(url, headers=request_header, cert=cert).json()
print(r.status_code)
print(r.json())

извините, трудно понять, почему это ломается, глядя на ваш комментарий. Я использовал его для множества приложений, и у меня не было никаких проблем

@Lukasa Я попытался изменить этот код (код вставлен ниже) и получил ту же ошибку, что и метод

import requests
import json
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.ssl_ import create_urllib3_context

json_file='apiInput.json'
hdr_key=[]
hdr_value=[]
json_data=open(json_file)
data = json.load(json_data)
request_body={}
#pprint(data)
json_data.close()
request_data = data['request1']
request_header=request_data['header-data']
url=request_header['url']

class DESAdapter(HTTPAdapter):
    """
    A TransportAdapter that re-enables 3DES support in Requests.
    """
    def init_poolmanager(self, *args, **kwargs):
        context = create_urllib3_context()
        context.load_cert_chain('rtmqa-clientid.pem',password='weblogic')
        kwargs['ssl_context'] = context
        return super(DESAdapter, self).init_poolmanager(*args, **kwargs)

    def proxy_manager_for(self, *args, **kwargs):
        context = create_urllib3_context()
        context.load_cert_chain('rtmqa-clientid.pem',password='weblogic')
        kwargs['ssl_context'] = context
        return super(DESAdapter, self).proxy_manager_for(*args, **kwargs)

s = requests.Session()
s.headers=request_header
s.mount(url, DESAdapter())
r = s.get(url)
/Users/tsu892/Python3.6/bin/python /Users/tsu892/Desktop/Office/Pythone-work/ASR-pythone/ASR-python3.6/Test-ASRreq.py
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/urllib3/contrib/pyopenssl.py", line 441, in wrap_socket
    cnx.do_handshake()
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/OpenSSL/SSL.py", line 1716, in do_handshake
    self._raise_ssl_error(self._ssl, result)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/OpenSSL/SSL.py", line 1456, in _raise_ssl_error
    _raise_current_error()
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/OpenSSL/_util.py", line 54, in exception_from_error_queue
    raise exception_type(errors)
OpenSSL.SSL.Error: [('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')]

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/urllib3/connectionpool.py", line 595, in urlopen
    self._prepare_proxy(conn)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/urllib3/connectionpool.py", line 816, in _prepare_proxy
    conn.connect()
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/urllib3/connection.py", line 326, in connect
    ssl_context=context)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/urllib3/util/ssl_.py", line 329, in ssl_wrap_socket
    return context.wrap_socket(sock, server_hostname=server_hostname)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/urllib3/contrib/pyopenssl.py", line 448, in wrap_socket
    raise ssl.SSLError('bad handshake: %r' % e)
ssl.SSLError: ("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/requests/adapters.py", line 440, in send
    timeout=timeout
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/urllib3/connectionpool.py", line 639, in urlopen
    _stacktrace=sys.exc_info()[2])
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/urllib3/util/retry.py", line 388, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='credit-cards-accounts-qa.kdc.capitalone.com', port=443): Max retries exceeded with url: /credit-cards-accounts/credit-cards/accounts/XqLuxBTABbIDvpw56ba34p2WV9JoWUSkPJ09hrBlWD8= (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",),))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/tsu892/Desktop/Office/Pythone-work/ASR-pythone/ASR-python3.6/Test-ASRreq.py", line 37, in <module>
    r = s.get(url)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/requests/sessions.py", line 521, in get
    return self.request('GET', url, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/requests/sessions.py", line 508, in request
    resp = self.send(prep, **send_kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/requests/sessions.py", line 618, in send
    r = adapter.send(request, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/requests/adapters.py", line 506, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='credit-cards-accounts-qa.kdc.capitalone.com', port=443): Max retries exceeded with url: /credit-cards-accounts/credit-cards/accounts/XqLuxBTABbIDvpw56ba34p2WV9JoWUSkPJ09hrBlWD8= (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",),))

@erikbern может быть проблема с настройкой на моем ноутбуке. Я использую Mac, Pythone3.6

6c40089ea258:~ tsu892$ pip3 show requests
Name: requests
Version: 2.18.4
Summary: Python HTTP for Humans.
Home-page: http://python-requests.org
Author: Kenneth Reitz
Author-email: [email protected]
License: Apache 2.0
Location: /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages
Requires: idna, certifi, chardet, urllib3
6c40089ea258:~ tsu892$ pip3 show certifi
Name: certifi
Version: 2017.7.27.1
Summary: Python package for providing Mozilla's CA Bundle.
Home-page: http://certifi.io/
Author: Kenneth Reitz
Author-email: [email protected]
License: MPL-2.0
Location: /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages
Requires: 

Как вы думаете, с сертификатом что-то не так?

Что дает python -m requests.help ?

@Lukasa Результат:

6c40089ea258:~ tsu892$ python3 -m requests.help?
/Library/Frameworks/Python.framework/Versions/3.6/bin/python3: No module named requests.help?

Удалите вопросительный знак из командной строки.

6c40089ea258:~ tsu892$ python3 -m requests.help
{
  "chardet": {
    "version": "3.0.4"
  },
  "cryptography": {
    "version": "2.0.3"
  },
  "idna": {
    "version": "2.6"
  },
  "implementation": {
    "name": "CPython",
    "version": "3.6.2"
  },
  "platform": {
    "release": "16.7.0",
    "system": "Darwin"
  },
  "pyOpenSSL": {
    "openssl_version": "1010006f",
    "version": "17.2.0"
  },
  "requests": {
    "version": "2.18.4"
  },
  "system_ssl": {
    "version": "100020bf"
  },
  "urllib3": {
    "version": "1.22"
  },
  "using_pyopenssl": true
}

Таким образом, ошибка, которую вы получаете, вызвана тем, что мы не можем проверить сертификат TLS сервера. certifi и OpenSSL кажутся правильными, поэтому я предполагаю, что сервер плохо себя ведет. К какому серверу вы пытаетесь подключиться?

Приложение развернуто на Cloud AWS. Но когда мы вызываем API, он сначала переходит к OSB, которая аутентифицирует сертификат, а затем направляет запрос в AWS.
Я использую тот же сертификат и использую почтальон или мой рубиновый код, API работает нормально

Вам нужен конкретный корневой сертификат? Можете ли вы предоставить мне имя хоста, с которым меня связывают?

URL-адрес хоста без пути: https://credit-cards-accounts-qa.kdc.capitalone.com
Это внутренняя конечная точка

Да, поэтому я не вижу, что там происходит. Можете ли вы запустить openssl s_client -showcerts -connect credit-cards-accounts-qa.kdc.capitalone.com:443 и предоставить полный вывод?

Удалено

Это очень похоже на то, что вы не используете глобально доверенный корневой сертификат. Где находится корневой сертификат для этой службы?

Никаких других сертификатов я не использую. Не уверен, что за сценой используется какой-либо корневой сертификат, есть ли способ узнать?

Да, инструменты разработчика Chrome расскажут вам полную цепочку сертификатов, которые он использует.

вы, вероятно, не хотите публиковать какой-то внутренний сертификат в Интернете, чтобы кто-то мог его увидеть ...

@erikbern Это общедоступная информация. Вы можете добиться того же результата, выполнив ту же команду.

@SethMichaelLarson Из профиля @erikbern на GitHub "Главный офицер троллей". Может, они просто троллили?

@erikbern @ sigmavirus24 А! Я не знал, с кем разговаривал. Продолжить! 🙇

Я не вижу ничего, кроме сертификата sha-1, когда бегу от почтальона
Может быть, мне нужно как-то добавить это в Pycharm

Если вы буквально переходите на веб-сайт в Chrome, этого должно быть достаточно.

Какую команду выполняет

@erikbern Это был всего лишь открытый ключ для сертификата ...

Сертификаты являются общедоступными данными; они передаются по сети открытым текстом при каждой попытке подключения.

Я пошел в цепочку сертификатов и нашел только сертификат Sha-1 и сертификат .Pem, которые я использую для доступа к API.

@AnoopPillai Я получил ваш пример кода от 1 сентября, работающий без проблем с использованием клиентского pem-файла с паролем. Кажется, что хост использует обычный сертификат. С @Lukasa большое спасибо!

К сожалению, у меня все еще возникают проблемы даже с методом временного файла. Я могу использовать .pfx в Google Postman, и у меня нет проблем с аутентификацией (так что я знаю, что мои учетные данные работают), но я все еще получаю 401 с Python. К сожалению, сотрудник службы поддержки компании, с которой я работаю, не очень помог - есть ли у кого-нибудь предложения по устранению неполадок?

На данном этапе я действительно не уверен, где даже искать проблему, поскольку другие люди сообщают об успешном использовании метода временного файла, а я до сих пор ничего не получил от их команды управления сертификатами.

Мы будем очень благодарны за любые советы - пожалуйста, дайте мне знать, могу ли я предоставить дополнительную информацию, чтобы упростить эту задачу.

Спасибо :)

Просто предложение, вы пробовали конвертировать PFX в PEM? Кроме того, если сервер также использует имя пользователя / пароль, вам необходимо добавить этот запрос на получение / публикацию с помощью auth = (). Я без проблем использую описанный выше подход class DESAdapter(HTTPAdapter) уже несколько недель, используя защищенный паролем файл PEM.

@ideasean По- прежнему

Поскольку .pfx работает с Postman, но не аутентифицируется здесь, может ли это означать, что что-то не так в процессе преобразования?

Я не использовал метод временного файла. Я использовал подход DESAdapter почти так же, как написано в сообщении AnoopPillai от 1 сентября выше, начиная с -

Я попытался изменить этот код (код вставлен ниже) и получил ту же ошибку, что и метод временного файла.

Я не могу говорить о процессе преобразования, но, возможно, хороший тест - попробовать использовать преобразованный файл pem с Postman?

Также обратите внимание, что я использовал вышеупомянутый подход, потому что мой файл pem был зашифрован / защищен паролем, а запросы Python в настоящее время не поддерживают это. Если ваш pem не защищен паролем, вы сможете использовать собственные запросы для каждой ссылки (но тогда у вас будет незащищенный сертификат в вашей файловой системе).

@ideasean Я разбил .pfx этим методом и получил файл .pem с атрибутами сумки и сертификатом, а также файл .pem с атрибутами сумки и зашифрованным закрытым ключом.

Все еще получаю недействительные учетные данные, я думаю, я попробую передать сертификаты в Postman и посмотреть, работают ли они, но я не могу понять, почему я, по-видимому, не могу правильно распаковать этот .pfx

Я также попробовал команду openssl openssl pkcs12 -in <my_pfx>.pfx -out certificate.cer -nodes , и она все еще дает мне ошибку 401, когда я меняю на нее так: context.load_cert_chain('certificate.cer')

Я установил вышеупомянутый .cer, и Postman даже не просит использовать его, когда я вызываю API (в отличие от всплывающего окна, когда он просит использовать .pfx), не знаю, как еще я могу заставить его использовать этот конкретный сертификат поскольку в настройках нет панели «Сертификаты», как говорится в документации.

Возможно, вы используете версию Postman для браузера, в которой нет панели сертификатов, отключения проверки SSL и т. Д. Попробуйте полную версию клиента, чтобы изменить настройки сертификата. Тогда вы можете продолжить это обсуждение в другой ветке, так как мы немного не по теме.

@ mkane848 видел ваш исходный комментарий, в котором вы получали ValueError: String expected . Возможно, вы захотите проверить https://github.com/pyca/pyopenssl/issues/701 и https://github.com/shazow/urllib3/issues/1275.

Я использую свой личный pem с паролем, используя это:

from requests.adapters import HTTPAdapter

from urllib3.util.ssl_ import create_urllib3_context

class SSLAdapter(HTTPAdapter):
    def __init__(self, certfile, keyfile, password=None, *args, **kwargs):
        self._certfile = certfile
        self._keyfile = keyfile
        self._password = password
        return super(self.__class__, self).__init__(*args, **kwargs)

    def init_poolmanager(self, *args, **kwargs):
        self._add_ssl_context(kwargs)
        return super(self.__class__, self).init_poolmanager(*args, **kwargs)

    def proxy_manager_for(self, *args, **kwargs):
        self._add_ssl_context(kwargs)
        return super(self.__class__, self).proxy_manager_for(*args, **kwargs)

    def _add_ssl_context(self, kwargs):
        context = create_urllib3_context()
        context.load_cert_chain(certfile=self._certfile,
                                keyfile=self._keyfile,
                                password=str(self._password))
        kwargs['ssl_context'] = context

К вашему сведению, я только что реализовал поддержку PKCS # 12 для requests как отдельную библиотеку:

Код представляет собой чистую реализацию : он не использует ни исправления обезьяны, ни временные файлы. Вместо этого используется настраиваемый TransportAdapter, который предоставляет настраиваемый SSLContext.

Любые отзывы и улучшения приветствуются!

Конечно, я бы хотел, чтобы requests предоставлял эту функциональность напрямую, но пока мы не будем там, эта библиотека облегчит боль.

Было бы очень хорошо, если бы мы могли просто сделать это:

~~~
cert = ("cert.pem", "key.pem", "somepassphrase") # отдельный сертификат / ключ

cert=("keycert.pem", None, "somepassphrase")    # combined cert/key

~~~

... даже если он работал только на python 3.3+. Это было бы лишь незначительным дополнением к поверхности API.

AFAICS, это будет означать небольшое изменение urllib3, чтобы HTTPSConnection принимал необязательный аргумент password ; это передается через ssl_wrap_socket , заканчиваясь:

~если файл сертификата:если пароль не None:context.load_cert_chain (файл сертификата, файл ключей, пароль)еще:context.load_cert_chain (файл сертификата, файл ключей)~

Тогда он будет обратно совместим, вызывая исключение только в том случае, если вы попытаетесь использовать парольную фразу закрытого ключа на более старой платформе, которая его не поддерживает.

Обратите внимание, что адаптер contrib/pyopenssl.py уже поддерживает этот дополнительный аргумент для load_cert_chain , как и python 2.7 .


Кроме того: я использую AWS KMS для управления «секретными» данными, поэтому я загружал ключевой пароль во время выполнения из KMS, а не жестко закодировал его в приложении.

Лично я не был бы против этого изменения, так как считаю, что оно значительно улучшит наш пользовательский интерфейс для многих пользователей по всем направлениям.

@ sigmavirus24 есть мысли?

@candlerb @kennethreitz Можно ли также включить в этот API случай PKCS # 12?

cert=('keycert.p12', None, 'somepassphrase')

Различие может быть либо расширением файла ( *.p12 против *.pem ), либо просмотром первых байтов этого файла.

У меня нет проблем с разрешением запросов на получение pkcs # 12, если это можно сделать безопасно - и, на мой взгляд, это препятствует записи извлеченного закрытого ключа во временный файл.

Погуглив для Python pkcs # 12, я нахожу:

  • Чей-то код, который записывает закрытый ключ
  • Другой код, который, как мне кажется, зависит от pyOpenSSL для чтения в pkcs # 12. Он возвращает сертификат и ключ как элементы данных.

Поэтому, делая это, я думаю, что было бы необходимо подключить вещи таким образом, чтобы сами ключи / сертификаты передавались в OpenSSL, а не имена файлов, содержащие эти вещи. Похоже, это гораздо большее изменение.

Если это слишком сложно, то это просто означает, что пользователь должен конвертировать pkcs # 12 в PEM в автономном режиме, что довольно просто (и может быть задокументировано).

@candlerb Как я писал в своем предыдущем комментарии (https://github.com/requests/requests/issues/1573#issuecomment-348968658), я уже создал чистую реализацию, которая хорошо интегрируется с requests .

Итак, проблемы, которые вы описываете, уже решены.

Прямо сейчас моя реализация добавляет новые аргументы ключевых слов pkcs12_* , чтобы как можно больше не мешать.

Но я думаю, что вместо этого его следует интегрировать в аргумент ключевого слова cert , и мой вопрос:

  • Будет ли это приемлемо в целом?
  • Будет ли приемлемо мое конкретное предложение cert=('keycert.p12', None, 'somepassphrase') ?
  • Как мы должны различать PKCS # 12 и PEM? (По суффиксу имени файла или по содержимому файла?)

(Более того, я бы предпочел видеть это в requests а не в моей отдельной библиотеке requests_pkcs12 . Но, учитывая возраст этой проблемы, у меня мало надежды на то, что она будет распространена в ближайшее время. Однако , если бы было конкретное заявление о том, какая именно реализация требуется, возможно, я мог бы соответствующим образом скорректировать свою реализацию и предложить запрос на перенос.)

Итак, несколько вещей:

  1. Я не думаю, что мы должны брать ключевое слово cert и расширять его вот так. Это неявно структурированные данные, и людей уже смущают кортежи в ключевом слове files . Я думаю, что глупо продолжать заведомо плохую схему.

  2. Я думаю, что во всяком случае, адаптер pkcs12 должен быть изменен и добавлен в список запросов. Я думаю, было бы лучше изменить его, чтобы создать ssl_context один раз, вместо того, чтобы хранить пароль pkcs12 в памяти этого объекта.

Я думаю, что есть еще одна работа, которую нужно проделать, прежде чем мы сможем справиться с этим в более общем случае, несмотря ни на что, и это включает определение правильного API для этого для Requests 3.0.

@ sigmavirus24 Спасибо за отзыв.

  1. Хорошо, давайте оставим отдельные ключевые слова pkcs12_* .
  2. Да, это определенно стоит улучшить. Я создал для этого запись в системе отслеживания проблем: https://github.com/m-click/requests_pkcs12/issues/2

Как класс PKCS # 12 TransportAdapter будет включен в requests ? Будет ли этот класс просто добавлен в requests , или есть другой способ включить его на «более глубокий» уровень, чтобы его можно было использовать без каких-либо оболочек request()/get()/... и без необходимости явно загружать это адаптер?

Моя организация нуждается в сертификатах PKCS12 и готова внести необходимые улучшения в вашу библиотеку для этого. Расшифровка файлов .p12 в файлы .pem считается слишком большим риском и требует дополнительных действий. Мы хотели бы добавить функциональность для создания и предоставления соответствующего ssl_context для данного сеанса. Будет ли ваша команда по-прежнему готова принять эту функциональность при условии, что она будет реализована должным образом?

Напоминаем: наша компания уже предоставила чистую реализацию, но в виде отдельного адаптера: https://github.com/m-click/requests_pkcs12

Не стесняйтесь переформатировать его в сам запрос на вытягивание.

Попутно вы можете исправить небольшую проблему: ssl_context должен храниться в памяти не в течение всего сеанса, а как можно скорее, только для одного данного соединения. Смотрите также:

Если вы исправите это в процессе, было бы неплохо, если бы вы могли предоставить его как небольшой запрос на перенос на

Таким образом, все люди, которые сейчас используют библиотеку requests_pkcs12 , автоматически получат выгоду от этого улучшения, без необходимости переключаться на (тогда улучшенный) новый API для самих запросов.

Да, https://github.com/m-click/requests_pkcs12 работал у меня и делал именно то, что я хотел. Большое спасибо @vog ! Я надеюсь, что запросы в конечном итоге смогут это поддержать.

Я также собираюсь поблагодарить @vog за его реализацию, работает так, как ожидалось, и решает проблему хранения сертификата / ключа в незащищенных хранилищах, таких как S3, в моем случае. Надеюсь, это может дойти до requests .

Была ли эта страница полезной?
0 / 5 - 0 рейтинги