Requests: SSL 클라이언트 측 인증서의 비밀번호 지정

에 만든 2013년 09월 03일  ·  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

이제 요청을 사용하여 API와 통신하는 데 필요한 두 파일 모두 certificate.pemplainkey.pem 가 있습니다.

다음은 이러한 인증서와 키를 사용하는 요청의 예입니다.

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. 이것이 내가 버스의 문제에 절대 대답해서는 안 되는 이유입니다. :/

따라서 현재 합의는 우리가 이것을 지원하지 않는다는 것입니다. 3.3 버전이 아닌 Python 버전에서 지원을 추가하려면 얼마나 많은 작업이 필요합니까?

이 조건에서 오류를 발생시키는 것이 얼마나 어렵습니까? 방금 이 어리석은 문제에 부딪쳤고 알아내는 데 2시간이 걸렸습니다. 오류가 발생하면 좋을 것입니다. 현재는 계속 반복되고 있습니다. 멋진 라이브러리에 감사드립니다!

잠깐, 루핑이 어디에 있습니까? 실행에서 우리는 어디에서 실패합니까? 우리가 반복하는 곳에서 역추적을 인쇄할 수 있습니까?

바로 여기에 매달려있는 것 같습니다.

r = 요청.get(URL,
인증 = 헤더 인증,
인증서 = self.cert_tuple,
헤더=헤더,
시간 초과 = 10,
확인 = 사실)

시간 초과를 위 또는 아래로 돌려 아무 소용이 없도록 시도했지만 시간 초과 전에 인증서를 사용할 수 없다는 것을 잘 알고 있다고 생각합니다. 감사 해요!

아, 죄송합니다. 명확하지 않았습니다. 나는 파이썬이 KeyboardInterrupt 예외를 던질 수 있도록 멈추게 한 다음 Ctrl + C로 죽이고 우리가 트레이스백에서 어디에 있는지 확인하려고 했습니다. 요청에서 실행이 중단되는 위치를 알고 싶습니다.

무슨 일이 일어나고 있는지 (또는 적어도 많은 경우에서 본 것)은 OpenSSL이 암호로 보호된 인증서를 받으면 사용자에게 암호를 묻는 메시지를 표시한다는 것입니다. 로그에 나타나지 않고(프롬프트가 직접 인쇄되기 때문에) 사용자가 Enter 키를 누르기를 기다리고 있기 때문에 시간 초과되지 않습니다.

말할 필요도 없이, 코드가 서버에서 실행될 때 번거롭고 위험한 동작입니다(프로세스를 종료하는 것 외에는 복구 옵션 없이 작업자를 중단시키기 때문입니다).

이 경우 암호를 묻는 대신 요청에 예외를 발생시키는 방법이 있습니까? 아니면 완전히 통제할 수 없고 OpenSSL의 손에 달려 있습니까?

@maxnoel 나는 이것이 OpenSSL의 손에 있다고 확신하지만 @Lukasa 의 질문(이 문제에 대한 마지막 의견)에 답할 수 있다면 우리가 도울 수 있는 일이 있는지에 관해 확실한 대답을 하는 데 매우 도움이 될 것입니다. .

대화형 python 프롬프트에서 암호 구문에 대한 stdin에서 OpenSSL이 차단되고 있는지 확인할 수 있습니다.

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

백그라운드 프로세스에서 실행 중인 경우 OpenSSL이 해당 입력을 기다리는 것을 차단할 것이라고 가정합니다.

맞습니다. 그런 일이 일어나지 않도록 요청이 할 수 있는 일이 있습니까? 암호가 제공되지 않을 때 예외를 발생시키는 것은 stdin(특히 비대화형 프로그램에서)에 대한 프롬프트를 표시하는 것보다 훨씬 더 유용합니다.

방법을 몰라서 두렵습니다. @리퍼헐크?

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()

BTW, 보안을 위해 pass phrase 대한 하드 코드를 수행하지 않는 것이 좋습니다.

당연하지. 즉, 문제는 암호 구문이 필요하다는 것이 아닙니다. 대화형이 아닌 GUI 또는 원격 프로그램의 경우에도 누군가가 stdin에 암호 구문을 입력하기를 기다리는 동안 OpenSSL이 프로그램을 중단시킨다는 것입니다.

암호가 필요하고 아무 것도 제공되지 않으면 대신 예외가 발생해야 합니다.

키에 기본 암호 ''를 사용하면 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

이제 요청을 사용하여 API와 통신하는 데 필요한 두 파일 모두 certificate.pemplainkey.pem 가 있습니다.

다음은 이러한 인증서와 키를 사용하는 요청의 예입니다.

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

아마존이 내부적으로 정확히 이 일을 하고 있다는 소문을 들었습니다.

나도이 문제에 직면 해있다. 내 관심사는 일반 개인 키를 파일 시스템에 저장하고 싶지 않다는 것입니다(다른 사람이 도난당할 위험이 있음). 그래서 제 생각에는 이것을 구현하는 더 확장 가능한 방법은 개인 키를 지정하기 위해 파일 경로 대신 PEM encoded string of private key 와 같은 것을 사용하는 것을 지원하는 것입니다. 개인 키/인증서의 암호화/복호화를 개발자에게 유리하게 맡기기만 하면 됩니다.
요청 소스 코드를 읽어보니 인증서/개인 키 파일만 지원하는 파이썬의 ssl 라이브러리에 따라 요청이 달라 구현이 쉽지 않은 것 같습니다. python stdlib 대신 pyopenssl을 사용할 수 있는지 궁금합니다. pyopenssl에는 openssl 연결 래퍼가 있습니다( https://pyopenssl.readthedocs.io/en/latest/api/ssl.html#connection -objects 참조). 따라서 'pkey' 객체를 파일 경로 대신 개인 키로 사용할 수 있습니다.

요청은 PyOpenSSL과 몇 가지 다른 필수 종속성이 설치되어 있는 한 이미 PyOpenSSL을 지원합니다. 그러나 이것이 의무화되지는 않을 것입니다. 우리가 표준 라이브러리와 잘 협력하는 것이 중요합니다.

향후 릴리스에서는 TLS를 처리하기 위해 SSLContext 객체를 urllib3에 전달하는 기능을 지원할 예정입니다. 그러면 이 기능이 활성화됩니다.

이 문제에 직면한 사람들을 위해 요청에 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를 제공하는 것을 더 생각했습니다. 예를 들어 cert=... 키워드 매개변수를 통해 client_cert.p12 파일 이름과 암호를 제공하기 cert=... 하면 됩니다.

@vog 그 작업을 수행하려면 어떤 코드가 필요하다고 생각하십니까?

@Lukasa requests 의 내부에 대해 잘 모르겠기 때문에 이미 있는 것을 과소평가할 수도 있지만 다음 중 하나를 수행해야 한다고 생각합니다.

  • PKCS#12 파일 이름을 하위 계층(urllib3 등)에 직접 제공하는 방법이 있습니다. 그리고 아마도 비밀번호도 마찬가지일 것입니다. (URL 라이브러리가 서버 측에서 실행되는 도구에 PKCS#12 암호를 입력하도록 대화식으로 관리자에게 요청하기를 원하는 사람이 없기 때문입니다.)
  • 이것이 불가능한 경우 PKCS#12(+password)를 PEM으로 변환한 다음 이를 하위 수준에 제공해야 합니다. 이것은 OpenSSL 바인딩에 대한 몇 번의 직접 호출로 수행됩니다. 그러나 결과는 문자열로 된 PEM 인증서이며 (암호화되지 않은) PEM을 하위 계층에 문자열로 제공하는 방법을 아직 찾지 못했습니다(OpenSSL/python "ssl" "buffer" 래퍼를 사용하는 경우 제외). 예: wrap_bio , 그러나 이것은 Python 2가 아닌 최신 Python 3 버전에서만 사용할 수 있습니다.
  • 따라서 이것이 불가능하다면 PKCS#12를 PEM으로 변환해야 할 뿐만 아니라 (암호화되지 않은) PEM 데이터를 포함하는 임시 파일도 생성해야 합니다.

마지막 요점은 내가 현재 본질적으로 하고 있는 일이지만 나는 이것을 전혀 좋아하지 않습니다. 인증서가 포함된 OpenSSL에 간단한 문자열을 제공할 수 없는 이유는 무엇입니까? 게다가 PKCS#12 파일 이름과 암호를 하위 계층에 전달할 수 없는 이유는 무엇입니까?

OpenSSL 전문가로 @reaperhulk 에 태그를 지정하려고 하지만 클라이언트 인증서용 PKCS#12 형식 인증서를 로드하기 위한 OpenSSL용 API가 없다는 점을 이해하고 있습니다. 이것은 우리가 절대적으로 PEM으로 변환해야 한다는 것을 의미합니다. 메모리에서 하는 것은 확실히 가능하지만 특정 시점에서 우리가 당신이 우리에게 전달하는 SSLContext에 위임할 만큼 충분히 이 전문가를 고려하고 싶지 않은지 궁금합니다.

@Lukasa 이 문제를 진지하게 받아주셔서 감사합니다. 너무 기술적으로 들리면 죄송하지만 본질적으로 다음과 같습니다.

클라이언트 인증서를 통해 서비스에 액세스하려고 합니다. 거의 모든 곳에서 이것을 파일과 암호로 얻을 수 있습니다(파일이 PKCS#12로 인코딩된 경우). Java 표준 라이브러리와 같은 대부분의 API에서는 파일 이름과 암호를 지정하기만 하면 됩니다.

그러나 Python에서는 지옥처럼 복잡합니다.

그렇기 때문에 거의 아무도 하지 않습니다. 대신 OpenSSL을 통해 파일과 비밀번호를 손으로 PEM 파일로 변환하고 해당 파일을 사용합니다. 이는 이러한 응용 프로그램의 모든 사용자에 대한 관리 오버헤드입니다. 단순히 (PKCS#12) 파일과 암호의 이름을 지정할 수 없기 때문입니다.

requests 라이브러리는 최소한 Java처럼 간단하게 만들어야 한다고 생각합니다.

requests 이미 어리석은 복잡한 API를 단순화하는 훌륭한 일을 하고 있으며 PKCS#12 사용 사례는 어리석은 복잡한 API의 또 다른 예일 뿐입니다.

PKCS#12 사용 사례는 어리석은 복잡한 API의 또 다른 예일 뿐입니다.

네, 저는 그것에 대해 전혀 동의하지 않습니다. 스택 어딘가에 PKCS#12 지원을 위한 일종의 솔루션이 있으면 매우 기쁩니다.

내가 느낌을 얻으려고 하는 것은 그 작업을 수행하는 데 필요한 코드와 결과적으로 이것이 배치되어야 하는 위치입니다. 내 추론은 이렇습니다.

  1. 일반적으로 요청은 그렇게 하는 데 상당한 유용성이 있고(즉, 많은 사람들이 사용하거나 일부가 매우 많이 사용하는 경우) 우리가 하고 있는 일이 제대로 하기 어려운 경우에만 API 표면에 추가합니다. 또는 미묘한 가장자리 경우가 있습니다.
  2. 일반적으로 PKCS#12를 지원하는 것은 API 표면에 추가된 것으로 간주되지만 cert= 구문을 전혀 변경하지 않고(지원할 항목을 확장함) 동작을 회귀하지 않는 경우( 즉, PKCS#12 파일과 PEM 파일의 차이점을 확실하게 알 수 있거나 두 논리 체인을 통해 쉽게 처리할 수 있습니다. .
  3. 그러나 이것이 갈 수있는 다른 장소가 있습니다. 예를 들어, Transport Adapter 수준에서, 또는 Requests Toolbelt의 도우미로, 또는 다른 것입니다.

즉, 이것이 얼마나 미묘한지, 코드가 얼마나 복잡한지, 추가 종속성이 필요한지 여부를 측정한 다음 해당 정보를 사용하여 코드를 배치하는 것이 가장 좋은 위치를 파악하고 싶습니다. 예를 들어, 지금 표준 라이브러리가 PKCS#12를 처리할 수 없다는 _의심_이 있습니다. 즉, _최상의_ 요청은 [security] 추가 기능이 설치된 PKCS#12만 사용할 수 있습니다. 더 나쁜 경우에는 OpenSSL 바인딩에서 사용할 수 있는 기능이 전혀 없을 수도 있습니다. 이 경우 작동하도록 하기 위해 진짜 골치 아픈 일을 해야 합니다. 그렇기 때문에 @reaperhulk 가 무게를

이 지원이 추가되는 것을 보고 싶습니다. 작업의 범위가 무엇인지 아는 사람들이 여기에 댓글을 달고 실제로 이동해야 하는 산이 얼마나 큰지 알려주면 됩니다.

PKCS#12 구현에 대한 세부 정보 하나 더: 암호가 바이트 문자열 대신 unicode 객체로 제공되면 이전 버전의 Python OpenSSL 바인딩이 실패합니다. 따라서 다음과 같이 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 문제로 줄일 수 있습니다.

또는 OpenSSL.SSL.Contextuse_privatekey 메서드를 사용하기 전에 제안한 것과 유사한 해킹을 작성할 수도 있습니다. 내 머리 꼭대기에서 (테스트되지 않은) 무언가

# 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 컨텍스트가 허용하는 모든 작업을 수행할 수 있습니다. 여기 에서 사용자 정의 컨텍스트를 전달하는 예를 볼 수 있습니다.

임시 파일을 사용하여 .pem으로 변환하여 요청과 함께 .pfx 및 .p12를 사용할 수 있었습니다. https://gist.github.com/erikbern/756b1d8df2d1487497d29b90e81f8068 참조

관심이 있으시면 PR을 제출할 수 있습니다. 임시 파일과 컨텍스트 관리자를 피하는 것이 좋습니다. 알려 줘요.

이것은 병합될 가능성이 없지만, 이제 전송 어댑터를 통해 PyOpenSSL 컨텍스트를 요청에 직접 전달할 수 있으므로 해당 문제를 우회할 수 있음을 알 수 있습니다.

이것은 병합될 가능성이 없지만, 이제 전송 어댑터를 통해 PyOpenSSL 컨텍스트를 요청에 직접 전달할 수 있으므로 해당 문제를 우회할 수 있음을 알 수 있습니다.

혼동을 드려 죄송하지만 일반적으로 pfx/p12 지원이 병합되지 않을 가능성이 있다는 말씀이신가요? (컨텍스트 등을 통해 올바른 방식으로 수행되었다고 가정). 한 번 해보게 되어 기쁩니다. 하지만 병합되지 않을 경우 시간을 할애할 가치가 없습니다.

나는 "병합될 가능성이 없다"는 것이 임시 파일 솔루션에 관한 것이라고 생각합니다.

@erikbern 분명히

그러나 임시 파일 솔루션은 허용되지 않습니다(@vog에서 언급함). 이것은 PKCS#12에 대한 지원이 표준 라이브러리에서 작동하지 않을 가능성이 있음을 의미합니다. 표준 라이브러리 ssl 모듈은 이에 대한 지원을 노출하지 않으므로 모든 요청 구성에서 지원되지 않을 것입니다.

잘 들린다. 나는 또한 디스크에 해독된 키를 저장하는 보안 위험이 있기 때문에 임시 파일이 나쁘다는 데 동의합니다. 다음 주에 살펴볼 수도 있습니다. ssl 모듈에 대해 알려주셔서 감사합니다. 제한이 requests 밖에 있으면 분명히 더 까다로워집니다.

나는 그것을 조사했고 ssl 모듈은 pem 데이터를 원시 문자열로 전달할 수 있는 cadata 인수를 추가했습니다. https://docs.python.org/3/library/ssl.html #ssl.SSLContext.load_verify_locations

이 작업을 수행하려면 여러 위치에서 urllib3을 패치해야 하므로 여기서 시작할 수 있습니다.

@erikbern 분명히 말해서, TransportAdapter 사용하여 적절하게 구성된 SSLContext 개체를 urllib3에 전달하면 이와 같은 거의 모든 솔루션이 더 잘 작동합니다.

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에 전달할 수 있습니다. 이를 통해 암호 또는 암호 기능을 제공할 수 있는 기본 기능에 액세스할 수 있습니다. 이를 지원하는 것이 좋습니다.

@AnoopPillai 유용한 임시 파일을 사용한 해결 방법: https://gist.github.com/erikbern/756b1d8df2d1487497d29b90e81f8068

방법이 있음을 알려주신 Lukasa에게 감사드립니다.
저는 파이썬을 처음 접했고 3.6 버전을 사용하고 있습니다. 클라이언트 인증서의 암호를 전달하기 위한 암호와 같은 옵션을 찾을 수 있는 곳을 안내해 주시겠습니까?
@Erikbern 나는 임시 파일 솔루션을 아직 거치지 않았지만 오늘 같은 것을 살펴볼 것입니다. 응답해 주셔서 감사합니다.

@AnoopPillai 당신은 load_cert_chain 원할 것입니다.

@Lukasa 문서화해 주시겠습니까? 몇 분 밖에 걸리지 않습니다(저는 고급 섹션 또는 새로운 고급 고급 섹션에서 생각하고 있습니다).

죄송합니다. 파이썬에 대한 경험이 부족한 것이 이유일 수 있지만 위에서 설명한 Lukasa 코드를 수정할 수 없습니다. 내 코드는 다음과 같습니다.

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 반환 된 객체에. 인터랙티브 인터프리터에서 해당 기능을 사용하여 작동 방식을 확인하십시오.

내 Mac에 설치된 urllib3..util.ssl_.py에는 최신 암호 옵션이 없습니다.
이것은 코드입니다

    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 해당 코드 변경(아래에 코드 붙여넣기)을 시도했지만 tempfile 메서드에서 발생한 것과 동일한 오류가 발생했습니다.

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 프로필 "Chief Troll Officer"에서. 아마도 그들은 단지 트롤링을 하고 있었을까요?

@erikbern @sigmavirus24 아! 나는 내가 누구에게 말을 하고 있는지 몰랐다. 진행하다! 🙇

우편 배달부에서 실행할 때 sha-1 인증서 외에는 아무것도 볼 수 없습니다.
어떻게 든 Pycharm에 이것을 추가해야 할 수도 있습니다.

말 그대로 Chrome에서 웹 사이트를 탐색하는 경우 충분해야 합니다.

@SethMichaelLarson이 어떤 명령을 실행하고 있습니까? 참고로 댓글은 지금 삭제되었지만 이전에 여기에 전체 BEGIN CERTIFICATE blob이 있었습니다... 아아악 온라인에서 공유하고 싶지 않습니다.

@erikbern 그것은 단지 인증서의 공개 키였습니다 ...

인증서는 공개 데이터입니다. 각 연결 시도에서 네트워크를 통해 일반 텍스트로 전송됩니다.

나는 인증서 체인에 가서 API를 적중하는 데 사용하는 Sha-1 인증서와 .Pem 인증서만 찾았습니다.

@AnoopPillai 암호가 있는 클라이언트 측 pem 파일을 사용하여 문제 없이 작동하는 9월 1일의 예제 코드를 얻었습니다. 호스트가 일반 인증서를 사용하고 있는 것 같습니다. @Lukasa 와 함께

불행히도 임시 파일 방법에도 여전히 문제가 있습니다. Google Postman에서 .pfx를 사용할 수 있고 인증에 문제가 없지만(그래서 내 자격 증명이 작동한다는 것을 알고 있음) Python에서는 여전히 401이 표시됩니다. 불행히도 내가 다루고 있는 회사의 지원 담당자는 별로 도움이 되지 않았습니다. 문제 해결에 대한 제안이 있는 사람이 있습니까?

이 단계에서 다른 사람들이 임시 파일 방법으로 성공을 보고하고 있고 아직 Cert Management 팀으로부터 아무 답변도 듣지 못했기 때문에 어디에서 문제를 찾아야 할지 확신이 서지 않습니다.

조언을 주시면 감사하겠습니다. 이 작업을 더 쉽게 하기 위해 추가 정보를 제공할 수 있으면 알려주세요.

감사 해요 :)

그냥 제안입니다. PFX를 PEM으로 변환해 보셨습니까? 또한 서버가 사용자 이름/비밀번호도 사용하는 경우 auth=()를 사용하여 get/post 요청을 추가해야 합니다. 암호로 보호된 PEM 파일을 사용하여 문제 없이 몇 주 동안 위의 class DESAdapter(HTTPAdapter) 접근 방식을 사용해 왔습니다.

@ideasean 유효하지 않은 자격 증명을 얻는 중입니다. 임시 파일 메서드에 대해 작성된 pfx_to_pem 함수에 의해 생성된 .pem 파일에서 load_cert_chain을 가리키고 있어야 합니다. 맞습니까? 개인 키와 인증서가 있습니다.

.pfx는 Postman과 함께 작동하지만 여기서 인증되지 않기 때문에 변환 프로세스에서 문제가 발생하고 있다는 의미일 수 있습니까?

임시 파일 방식을 사용하지 않았습니다. 나는 위의 9월 1일에 AnoopPillai의 게시물에서 다음과 같이 시작하여 DESAdapter 접근 방식을 거의 사용했습니다.

해당 코드 변경(아래에 코드 붙여넣기)을 시도했지만 tempfile 메서드에서 발생한 것과 동일한 오류가 발생했습니다.

변환 프로세스에 대해서는 말할 수 없지만 아마도 좋은 테스트는 변환된 pem 파일을 Postman과 함께 사용해 보는 것입니다.

또한 내 pem 파일이 암호화/비밀번호로 보호되어 있고 Python 요청이 현재 이를 지원하지 않기 때문에 위의 접근 방식을 사용했습니다. pem이 비밀번호로 보호되지 않으면 링크 당 기본 요청을 사용할 수 있어야 합니다(그러나 파일 시스템에 보호되지 않은 인증서가 있음).

@ideasean 이 방법에 따라 .pfx를

여전히 유효하지 않은 자격 증명이 표시되면 Postman에 인증서를 넣고 작동하는지 확인하려고 하지만 이 .pfx의 압축을 제대로 풀 수 없는 이유를 알 수 없습니다.

나는 또한 openssl 명령 openssl pkcs12 -in <my_pfx>.pfx -out certificate.cer -nodes 시도했는데 다음과 같이 변경하면 여전히 401 오류가 발생합니다. context.load_cert_chain('certificate.cer')

위에서 언급한 .cer을 설치했는데 Postman은 API 호출을 할 때 사용을 요청하지도 않습니다(.pfx를 사용하도록 요청할 때 팝업이 표시되는 것과는 달리). 특정 인증서를 사용하도록 다른 방법을 잘 모르겠습니다. 문서에서 말하는 것과 같은 설정에 "인증서" 패널이 없기 때문입니다.

인증서 패널, SSL 유효성 검사 비활성화 등이 포함되지 않은 브라우저 버전의 Postman을 사용 중일 수 있습니다. 전체 클라이언트에서 인증서 설정을 변경해 보십시오. 우리는 주제에서 약간 벗어난 것이므로 다른 스레드에서 이 토론을 계속할 수 있습니다.

@mkane848ValueError: String expected 을(를) 받고 있던 원래 댓글을 보았습니다. https://github.com/pyca/pyopenssl/issues/701https://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

참고로 requests 대한 PKCS#12 지원을 별도의 라이브러리로 구현했습니다.

코드는 깨끗한 구현입니다 . 원숭이 패치나 임시 파일을 사용하지 않습니다. 대신 사용자 지정 SSLContext를 제공하는 사용자 지정 TransportAdapter가 사용됩니다.

모든 피드백과 개선 사항을 환영합니다!

물론 requests 이 이 기능을 직접 제공하기를 바라지

다음과 같이 간단히 할 수 있다면 매우 좋을 것입니다.

~~~
cert=("cert.pem", "key.pem", "somepassphrase") # 별도의 인증서/키

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

~~~

... 파이썬 3.3 이상에서만 작동하더라도. 이것은 API 표면에 대한 사소한 추가일 뿐입니다.

AFAICS, 이것은 HTTPSConnection이 선택적 password 인수를 허용하도록 urllib3에 대한 약간의 변경을 의미합니다. 이것은 ssl_wrap_socket 통해 전달되어 다음과 같이 끝납니다.

~인증서 파일의 경우:암호가 없음이 아닌 경우:context.load_cert_chain(인증서 파일, 키 파일, 암호)또 다른:context.load_cert_chain(인증서 파일, 키 파일)~

그러면 이전 버전과 호환되므로 지원하지 않는 이전 플랫폼에서 개인 키 암호를 사용하려고 하는 경우에만 예외가 발생합니다.

contrib/pyopenssl.py 어댑터는 이미 load_cert_chain 대한 이 추가 인수를 지원하며 python 2.7도 마찬가지입니다.


제쳐두고: 저는 AWS KMS를 사용하여 "비밀" 데이터를 관리하고 있으므로 애플리케이션에 하드 코딩하지 않고 런타임 시 KMS에서 키 암호를 로드합니다.

저는 개인적으로 이 변경에 반대하지 않을 것입니다. 왜냐하면 이것이 전반적으로 많은 사용자를 위한 사용자 인터페이스를 크게 향상시킬 것이라고 생각하기 때문입니다.

@sigmavirus24 생각이 있으신가요?

@candlerb @kennethreitz PKCS#12 케이스도 해당 API에 포함해도 됩니까?

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

구분은 파일 확장자( *.p12*.pem ) 또는 해당 파일의 첫 번째 바이트를 확인하는 것일 수 있습니다.

요청이 안전하게 수행될 수 있는 한 pkcs#12를 사용하도록 허용하는 데 문제가 없습니다. 제 생각에는 추출된 개인 키를 임시 파일에 쓰는 것을 금지합니다.

Python pkcs#12에 대한 인터넷 검색에서 다음을 찾았습니다.

  • 개인 키를 작성하는 누군가의 코드
  • 내가 생각하는 다른 코드 는 pkcs#12에서 읽을 pyOpenSSL에 의존합니다. 인증서와 키를 데이터 항목으로 반환합니다.

그래서 이렇게 하면 키/인증서 자체가 포함된 파일 이름이 아니라 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_pkcs12 라이브러리보다 requests 것을 보고 싶습니다. 그러나 이 문제의 시대를 고려할 때 이것이 곧 업스트림으로 올라갈 것이라는 희망은 거의 없습니다. 그러나 , 정확히 어떤 종류의 구현이 필요한지에 대한 구체적인 설명이 있다면 그에 따라 구현을 조정하고 풀 리퀘스트를 제안할 수 있습니다.)

그래서, 몇 가지:

  1. 나는 우리가 cert 키워드를 가져 와서 이렇게 확장해서는 안된다고 생각합니다. 그것은 암시적으로 구조화된 데이터이고 사람들은 이미 files 키워드의 튜플에 혼란스러워합니다. 알려진 나쁜 패턴을 계속하는 것은 어리석은 일이라고 생각합니다.

  2. 제 생각에는 pkcs12 어댑터가 수정되어 requests-toolbelt로 업스트림되어야 한다고 생각합니다. 해당 개체의 메모리에 pkcs12 암호를 저장하는 대신 ssl_context 한 번 생성하도록 수정하는 것이 더 나을 것 같습니다.

요청 3.0에 적합한 API를 결정하는 것을 포함하여 더 일반적인 경우에 이를 처리하기 전에 수행해야 할 다른 작업이 아직 더 있다고 생각합니다.

@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는 전체 세션 동안 메모리에 보관되어서는 안 되며, 주어진 단일 연결에 대해서만 가능한 한 빨리 유지되어야 합니다. 또한보십시오:

도중에 고칠 경우 요청 자체와 함께 https://github.com/m-click/requests_pkcs12 에 작은 pull 요청으로 제공할 수 있다면 좋을 것입니다.

그렇게 하면 현재 requests_pkcs12 라이브러리를 사용하는 모든 사람들이 요청 자체에 대해 (향상된) 새 API로 전환하지 않고도 자동으로 해당 개선의 혜택을 볼 수 있습니다.

예, https://github.com/m-click/requests_pkcs12 가 저에게 도움이 되었고 제가 하고 싶은 일을 정확히 했습니다. @vog 정말 감사합니다! 나는 요청이 결국 그것을 지원할 수 있기를 바랍니다.

또한 구현에 대해 @vog 에게 감사를 표하고 예상대로 작동하며 제 경우 S3와 같은 비보안 저장소에 인증서/키를 유지하는 문제를 해결합니다. 바라건대 이것은 requests 있습니다.

이 페이지가 도움이 되었나요?
0 / 5 - 0 등급