Requests: Passwort für SSL-Client-seitiges Zertifikat angeben

Erstellt am 3. Sept. 2013  ·  121Kommentare  ·  Quelle: psf/requests

Soweit ich weiß, ist es derzeit nicht möglich, das Kennwort für das clientseitige Zertifikat anzugeben, das Sie zur Authentifizierung verwenden.
Dies ist ein kleines Problem, da Sie normalerweise immer Ihre .pem-Datei, die den privaten Schlüssel enthält, mit einem Kennwort schützen möchten. openssl lässt Sie nicht einmal ohne Passwort einen erstellen.

Documentation Planned

Hilfreichster Kommentar

@botondus Ich glaube, ich habe einen einfacheren Weg gefunden, dies mit der Anforderungsbibliothek zu erreichen. Ich dokumentiere dies für andere Leute, die mit dem Problem konfrontiert sind.

Ich gehe davon aus, dass Sie ein .p12-Zertifikat und eine Passphrase für den Schlüssel haben.

Zertifikat und privaten Schlüssel generieren.

// 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

Nun, wir sind noch nicht fertig und müssen den Schlüssel generieren, der nicht jedes Mal das PEM-Passwort erfordert, wenn er mit dem Server kommunizieren muss.

Schlüssel ohne Passphrase generieren.

// 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

Jetzt haben Sie certificate.pem und plainkey.pem , beides Dateien, die erforderlich sind, um über Anfragen mit der API zu kommunizieren.

Hier ist eine Beispielanfrage, die dieses Zertifikat und diese Schlüssel verwendet.

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

Hoffe das hilft:

cc @kennethreitz @Lukasa @sigmavirus24

Alle 121 Kommentare

Etwas wie:

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

Ziemlich sicher, dass Sie dafür den Parameter cert sollten: cert=('server.pem', 'my_password')

@sigmavirus24
Das Tupel ist für (certificate, key) . Derzeit gibt es keine Unterstützung für verschlüsselte Schlüsseldateien.
Die stdlib wurde nur für die Version 3.3 unterstützt.

Heh, @t-8ch, du hast aus Versehen eine Datei auf deinem lokalen FS verlinkt. ;) Korrekter Link .

Ganz richtig @t-8ch. Aus diesem Grund sollte ich niemals Fragen aus dem Bus beantworten. :/

Der derzeitige Konsens ist also, dass wir dies nicht unterstützen. Wie viel Arbeit wird es wahrscheinlich sein, Unterstützung in Nicht-3.3-Versionen von Python hinzuzufügen?

Wie schwer wäre es, unter dieser Bedingung einen Fehler auszulösen? Ich bin gerade auf dieses dumme Problem gestoßen und es hat zwei Stunden gedauert, um es herauszufinden, es wäre schön, wenn es einen Fehler auslösen würde, es sitzt derzeit nur in einer Schleife da. Danke für die tolle Bibliothek!

Warten Sie, es sitzt, wo Looping? Wo scheitern wir in der Ausführung? Können Sie die Rückverfolgung von unserer Schleife aus drucken?

Hier scheint es zu hängen:

r = request.get(url,
auth=headeroauth,
cert=self.cert_tuple,
Überschriften = Überschriften,
Zeitüberschreitung=10,
verifizieren=wahr)

Ich habe vergeblich versucht, das Timeout nach oben oder unten zu drehen, aber ich kann mir vorstellen, dass es vor dem Timeout weiß, dass es das Zertifikat nicht verwenden kann. Vielen Dank!

Ah, tut mir leid, ich war nicht klar. Ich wollte es hängen lassen und dann mit Strg + C beenden, damit Python eine KeyboardInterrupt-Ausnahme auslöst, um dann zu sehen, wo wir uns im Traceback befinden. Ich möchte wissen, wo in Requests die Ausführung anhält.

Was passiert (oder zumindest das, was ich in vielen Fällen gesehen habe) ist, dass OpenSSL, nachdem es ein passwortgeschütztes Zertifikat erhalten hat, den Benutzer zur Eingabe eines Passworts auffordert. Es wird in keinen Protokollen angezeigt (da die Eingabeaufforderung direkt gedruckt wird) und es tritt keine Zeitüberschreitung auf, weil es darauf wartet, dass ein Benutzer die Eingabetaste drückt.

Es ist unnötig zu erwähnen, dass es ein umständliches und gefährliches Verhalten ist, wenn der Code auf einem Server ausgeführt wird (weil er Ihren Worker hängen lässt, der keine andere Möglichkeit zur Wiederherstellung hat, als den Prozess zu beenden).

Gibt es eine Möglichkeit, Anfragen in diesem Fall zu einer Ausnahme zu machen, anstatt nach einem Passwort zu fragen, oder liegt das völlig außerhalb Ihrer Kontrolle und in den Händen von OpenSSL?

@maxnoel Ich bin mir ziemlich sicher, dass dies in den Händen von OpenSSL liegt, aber wenn Sie die Frage von @Lukasa (der letzte Kommentar zu diesem Problem) beantworten

Sie können bestätigen, dass OpenSSL auf stdin für die Passphrase von der interaktiven Python-Eingabeaufforderung blockiert:

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

Wenn Sie einen Hintergrundprozess ausführen, gehe ich davon aus, dass OpenSSL das Warten auf diese Eingabe blockiert.

Das ist richtig. Gibt es etwas, was Anfragen tun können, um dies zu verhindern? Eine Ausnahme auszulösen, wenn kein Passwort angegeben wird, wäre weitaus nützlicher, als nach Dingen auf stdin zu fragen (insbesondere in einem nicht interaktiven Programm).

Ich fürchte, ich kenne keinen Weg. @reaperhulk?

Es gibt Möglichkeiten, OpenSSL daran zu hindern, aber ich bin mir nicht sicher, ob sie von pyOpenSSL verfügbar gemacht werden. Wo rufen Anforderungen pyopenssl auf, um das Client-Zertifikat zu laden? Ich kann ein bisschen graben.

@reaperhulk Es wird in urllib3 gemacht, hier .

Wir machen auch etwas sehr Ähnliches für die stdlib, was ein ganz separates Problem darstellen wird.

Wir können dies also mit PyOpenSSL tun, indem wir einen Patch wie diesen verwenden . In der stdlib-Version müssen wir load_cert_chain mit einem Passwort verwenden.

Wurde dieses Problem gelöst? Ich stoße derzeit darauf, während ich versuche, eine Verbindung zu einem Apache-Server herzustellen.

Es hat nicht.

Was ist mit PKCS#12-formatierten (und verschlüsselten) Containern, die ein Client-Zertifikat/-Schlüssel enthalten könnten? Fällt dies unter die gleiche Funktionsanforderung?

@mikelupo Ja .

@telam @mikelupo
Ich habe das gleiche Problem und habe viel gegoogelt, schließlich habe ich es mit pycurl gelöst.
In meiner Situation verwende ich openssl, um meine .pfx-Datei in eine .pem-Datei zu konvertieren, die sowohl das Zertifikat als auch den Schlüssel enthält (mit Passphrase verschlüsselt), und rufe dann den folgenden Code auf.

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

Übrigens, aus Sicherheitsgründen ist es besser, für pass phrase keinen Hardcode zu machen

Natürlich. Das Problem ist jedoch nicht wirklich, dass eine Passphrase erforderlich ist – es ist, dass OpenSSL Ihr Programm hängen lässt, während es darauf wartet, dass jemand eine Passphrase in stdin eingibt, selbst im Fall eines nicht interaktiven GUI- oder Remote-Programms.

Wenn eine Passphrase erforderlich ist und keine bereitgestellt wird, sollte stattdessen eine Ausnahme ausgelöst werden.

Wenn Sie eine Standard-Passphrase von '' für den Schlüssel verwenden, hängt openssl nicht.
es wird einen falschen Passworttext zurückgeben. Sie können Ihren Py-Flow sofort ändern
um den Benutzer dann ohne diesen scheinbaren Stand zu benachrichtigen

jeder Plan, diese Funktion hinzuzufügen

Wir möchten es hinzufügen, haben aber derzeit keinen Zeitplan, um es hinzuzufügen.

@botondus Ich glaube, ich habe einen einfacheren Weg gefunden, dies mit der Anforderungsbibliothek zu erreichen. Ich dokumentiere dies für andere Leute, die mit dem Problem konfrontiert sind.

Ich gehe davon aus, dass Sie ein .p12-Zertifikat und eine Passphrase für den Schlüssel haben.

Zertifikat und privaten Schlüssel generieren.

// 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

Nun, wir sind noch nicht fertig und müssen den Schlüssel generieren, der nicht jedes Mal das PEM-Passwort erfordert, wenn er mit dem Server kommunizieren muss.

Schlüssel ohne Passphrase generieren.

// 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

Jetzt haben Sie certificate.pem und plainkey.pem , beides Dateien, die erforderlich sind, um über Anfragen mit der API zu kommunizieren.

Hier ist eine Beispielanfrage, die dieses Zertifikat und diese Schlüssel verwendet.

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

Hoffe das hilft:

cc @kennethreitz @Lukasa @sigmavirus24

Ich habe durch die Weinrebe gehört, dass Amazon genau das intern tut.

Ich stehe auch vor diesem Problem. Meine Sorge ist, dass ich den einfachen privaten Schlüssel nicht im Dateisystem speichern möchte (kann von anderen gestohlen werden). Meiner Meinung nach ist die erweiterbare Möglichkeit, dies zu implementieren, die Verwendung von PEM encoded string of private key anstelle des Dateipfads zur Angabe des privaten Schlüssels zu unterstützen. Überlassen Sie einfach die Verschlüsselung / Entschlüsselung des privaten Schlüssels / Zertifikats den Entwicklern zu ihren Gunsten.
Nach dem Lesen des Quellcodes von Anfragen scheint es nicht ganz einfach zu sein, da Anfragen von der SSL-Bibliothek von Python abhängen, die nur Zertifikate / private Schlüsseldateien unterstützt. Ich frage mich nur, ob wir pyopenssl anstelle von python stdlib verwenden könnten? pyopenssl hat einen Wrapper der openssl-Verbindung, siehe: https://pyopenssl.readthedocs.io/en/latest/api/ssl.html#connection -objects . Daher können wir das 'pkey'-Objekt als privaten Schlüssel anstelle des Dateipfads verwenden.

Requests unterstützt bereits PyOpenSSL, sofern es und einige andere erforderliche Abhängigkeiten installiert sind. Das wird aber nie Pflicht: Uns ist es wichtig, dass wir gut mit der Standardbibliothek arbeiten.

In einer zukünftigen Version werden wir die Übergabe von SSLContext-Objekten an urllib3 unterstützen, um TLS zu handhaben: das wird diese Funktion aktivieren.

Für diejenigen, die mit diesem Problem konfrontiert sind, gibt es hier eine Problemumgehung, die tatsächlich die Verwendung verschlüsselter Zertifikate / Schlüsseldateien unterstützt (erfordert, dass PyOpenSSL installiert und anstelle der Standardbibliothek verwendet wird) ssl, was es sein sollte, wenn es installiert ist)

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 : Funktioniert dies auch für PKCS#12-Dateien oder ist dies nur PEM?

@Lukasa : Soll der PKCS#12-Fall wirklich hier behandelt werden oder sollte ich dafür ein separates Thema eröffnen?

PKCS#12 ist ein schwierigeres Problem, aber im Grunde müssen Sie alles tun, was erforderlich ist, um Ihren SSLContext anzupassen.

@Lukasa : Ich dachte eher daran, eine gute High-Level-API in Anfragen client_cert.p12 und das Kennwort über den Schlüsselwortparameter cert=... .

@vog Welcher Code wäre Ihrer Meinung nach erforderlich, damit das funktioniert?

@Lukasa Ich bin mir bei den Interna von requests nicht sicher, daher unterschätze ich vielleicht, was bereits vorhanden ist, aber ich denke, eines der folgenden Dinge muss getan werden:

  • Entweder haben wir eine Möglichkeit, einen PKCS#12-Dateinamen direkt an die unteren Ebenen (urllib3 usw.) bereitzustellen. Und vielleicht auch das Passwort. (Weil ich niemanden kenne, der möchte, dass eine URL-Bibliothek den Administrator interaktiv auffordert, sein PKCS#12-Passwort in einem serverseitig ausgeführten Tool einzugeben.)
  • Wenn dies nicht möglich ist, müssen wir PKCS#12 (+Passwort) in PEM konvertieren und diese dann den unteren Ebenen bereitstellen. Dies geschieht mit wenigen Aufrufen direkt an die OpenSSL Bindung. Das Ergebnis ist jedoch das PEM-Zertifikat als String, und ich habe noch keine Möglichkeit gefunden, das (unverschlüsselte) PEM als String an die unteren Schichten bereitzustellen (außer vielleicht durch Verwendung von OpenSSL / Python "ssl" "buffer" Wrapper, zB wrap_bio , aber dies ist nur in den neuesten Python 3-Versionen verfügbar, nicht in Python 2).
  • Wenn auch das nicht möglich ist, müssten wir nicht nur PKCS#12 in PEM konvertieren, sondern auch eine temporäre Datei mit den (unverschlüsselten) PEM-Daten erstellen.

Beachten Sie, dass der letzte Punkt das ist, was ich im Moment im Wesentlichen tue, aber das gefällt mir überhaupt nicht. Warum kann ich OpenSSL keinen einfachen String bereitstellen, der das Zertifikat enthält? Außerdem, warum kann ich den PKCS#12-Dateinamen und das Passwort nicht einfach an die unteren Schichten übergeben?

Ich werde @reaperhulk als OpenSSL-Experte markieren, aber ich verstehe, dass es keine APIs für OpenSSL gibt, um Zertifikate im PKCS#12-Format für Client-Zertifikate zu laden. Das bedeutet, dass wir unbedingt auf PEM umstellen müssen. Es ist sicherlich möglich, dies im Speicher zu tun, aber an einem bestimmten Punkt frage ich mich, ob wir diesen Experten nicht nur als genug betrachten wollen, um an jeden SSLContext zu delegieren, den Sie uns übergeben.

@Lukasa Danke, dass Sie dieses Problem ernst nehmen. Tut mir leid, wenn das zu technisch klingt, aber im Wesentlichen ist es nur Folgendes:

Sie möchten über ein Clientzertifikat auf einen Dienst zugreifen. Fast überall bekommt man dies als Datei und Passwort (wo die Datei PKCS#12 kodiert ist). In den meisten APIs, wie der Java-Standardbibliothek, geben Sie einfach den Dateinamen und das Kennwort ein und sind damit fertig.

In Python ist dies jedoch höllisch kompliziert.

Deshalb macht es fast niemand. Stattdessen konvertieren sie ihre Datei und ihr Passwort manuell über OpenSSL in eine PEM-Datei und verwenden diese Datei. Dies ist ein administrativer Aufwand für jeden Benutzer einer solchen Anwendung. Weil sie die Datei (PKCS#12) und das Kennwort nicht einfach benennen können.

Ich denke, die Bibliothek requests sollte es mindestens so einfach machen wie in Java.

requests leistet bereits hervorragende Arbeit bei der Vereinfachung dummer komplexer APIs, und der Anwendungsfall PKCS#12 ist nur ein weiteres Beispiel für eine dumme komplexe API.

der Anwendungsfall PKCS#12 ist nur ein weiteres Beispiel für eine dumme komplexe API.

Ja, dem widerspreche ich überhaupt nicht: Ich wäre total glücklich, irgendwo im Stack eine Lösung für die PKCS#12-Unterstützung zu haben.

Was ich versuche, ein Gefühl dafür zu bekommen, ist, welcher Code erforderlich ist, damit das funktioniert und wo dieser platziert werden sollte. Meine Überlegung ist wie folgt:

  1. Im Allgemeinen erweitert Requests seine API-Oberfläche nur dann, wenn dies von erheblichem Nutzen ist (d oder hat subtile Randfälle.
  2. Normalerweise würde die Unterstützung von PKCS#12 als Ergänzung zur API-Oberfläche gelten, aber wenn es die Syntax von cert= überhaupt nicht ändert (erweitert nur die Dinge, die es unterstützt) und das Verhalten nicht zurückgeht ( das heißt, wir können zuverlässig den Unterschied zwischen PKCS#12-Dateien und PEM-Dateien erkennen, oder wir können einfach beide Logikketten durchlaufen), würde ich sagen, dass es eine ausreichend kleine Änderung an der Oberfläche ist, die es wahrscheinlich wert ist .
  3. Es gibt jedoch andere Orte, an denen dies möglich ist. Zum Beispiel auf Transportadapter-Ebene oder als Helfer im Requests Toolbelt oder etwas anderes.

Das heißt, ich möchte abwägen, wie subtil dies ist, wie komplex der Code ist, ob zusätzliche Abhängigkeiten erforderlich sind, und dann anhand dieser Informationen herausfinden, wo der Code am besten platziert werden kann. Zum Beispiel habe ich gerade den _Verdacht_, dass die Standardbibliothek PKCS#12 nicht verarbeiten kann, was bedeuten würde, dass _bestenfalls_ Requests PKCS#12 nur mit installiertem [security] können. In einem noch schlimmeren Fall haben wir möglicherweise überhaupt nicht die Funktionen einer OpenSSL-Bindung zur Verfügung, in diesem Fall müssen wir einige wirklich verrückte Dinge tun, um sie zum Laufen zu bringen. Deshalb wollte ich, dass @reaperhulk abwägt : Er wird dies wahrscheinlich schneller für uns klären können, als ich die Recherche durchführen könnte.

Ich wünsche mir, dass diese Unterstützung hinzugefügt wird: Ich muss nur einige Leute dazu bringen, die den Umfang der Arbeit kennen, die sich hier äußern und mir sagen, wie groß der Berg, den wir bewegen müssen, tatsächlich ist.

Noch ein Detail für eine PKCS#12-Implementierung: Ältere Versionen der Python OpenSSL-Bindungen schlagen fehl, wenn das Passwort als unicode Objekt statt als Byte-String angegeben wird. Es sollte also konvertiert werden, bevor es wie load_pkcs12() an

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

Ein vollständiger Konverter könnte so aussehen, wobei erwartet wird, dass pkcs12_data ein Byte-String mit Binärdaten ist, während password ein Byte-String oder ein Unicode-String sein kann:

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

Die Diskussion über PKCS#12 scheint mir den Rahmen des ursprünglichen Problems zu sprengen, da es sich um die Frage handelt, ob Anfragen PKCS#12 nativ unterstützen sollten. Ich würde dafür stimmen, dass es sein eigenes Problem bekommt, aber das liegt natürlich an den Verantwortlichen.

Als Problemumgehung, die keine unverschlüsselten temporären Dateien erfordert, verfügt die Methode OpenSSL.crypto.dump_privatekey über einen optionalen Passphrase-Parameter, sodass Sie auf diese Weise eine Kopie des verschlüsselten privaten Schlüssels im PEM-Format erhalten können. Das würde dies auf das verschlüsselte PEM-Problem reduzieren, mit dem wir begonnen haben.

Alternativ könnten Sie wahrscheinlich auch einen Hack ähnlich dem von mir vorgeschlagenen schreiben, bevor Sie die use_privatekey Methode von OpenSSL.SSL.Context . Aus dem Kopf (ungetestet) sowas wie

# 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

Dann verwenden Sie einfach requests.get etc, ohne ein Zertifikat anzugeben, da es bereits im Konstruktor behandelt wird.

Überprüfe diesen Thread jetzt. Umformulierung des Originals:

Können Anfragen bei einem verschlüsselten PEM-formatierten Client-Zertifikat mit der Bereitstellung eines Kennworts umgehen?

Da dies in aktuellen Standardbibliotheken enthalten ist, wäre es großartig, diese Option zu integrieren. Dies wäre aus Gründen der Unternehmenssicherheit äußerst wertvoll (wo unsere Zertifikate verschlüsselt sind und dies auch bleiben sollen).

An dieser Stelle kann dies durch Übergeben eines benutzerdefinierten SSL-Kontexts an urllib3 mithilfe eines Transportadapters erfolgen. Dies kann alles tun, was der SSL-Kontext der Standardbibliothek zulässt. Ein Beispiel für die Übergabe eines benutzerdefinierten Kontexts finden Sie hier .

Ich konnte .pfx und .p12 mit Anfragen verwenden, indem ich sie mithilfe einer temporären Datei in eine .pem-Datei konvertierte. Siehe https://gist.github.com/erikbern/756b1d8df2d1487497d29b90e81f8068

Bei Interesse kann ich eine PR einreichen. Es wäre toll, die temporäre Datei und den Kontextmanager zu vermeiden. Gib mir Bescheid.

Dies wird leider nicht zusammengeführt, aber beachten Sie, dass Sie jetzt einen PyOpenSSL-Kontext über Transportadapter direkt an Requests übergeben können, sodass Sie dieses Problem möglicherweise umgehen können.

Dies wird leider nicht zusammengeführt, aber beachten Sie, dass Sie jetzt einen PyOpenSSL-Kontext über Transportadapter direkt an Requests übergeben können, sodass Sie dieses Problem möglicherweise umgehen können.

Entschuldigung für die Verwirrung, aber sagen Sie, dass die pfx/p12-Unterstützung im Allgemeinen wahrscheinlich nicht zusammengeführt wird? (Vorausgesetzt, es wurde richtig gemacht, durch den Kontext usw.). Ich freue mich, es auszuprobieren, aber meine Zeit ist es offensichtlich nicht wert, wenn es nicht zusammengeführt wird.

Ich glaube, dass es bei "nicht wahrscheinlich zusammengeführt" um die temporäre Dateilösung ging.

@erikbern Um es anzusprechen und zusammenzuführen, die einigermaßen konsistent funktioniert. Beispielsweise wäre eine Lösung für die Verwendung von PKCS#12 über das PyOpenSSL-Contrib-Modul in urllib3 akzeptabel.

Eine temporäre Dateilösung ist jedoch nicht akzeptabel (wie von @vog festgestellt). Dies bedeutet, dass die Unterstützung für PKCS#12 mit der Standardbibliothek wahrscheinlich nicht funktioniert, da das Modul ssl der Standardbibliothek keine Unterstützung dafür bereitstellt und daher nicht in allen Requests-Konfigurationen unterstützt wird.

Hört sich gut an. Ich stimme auch zu, dass die temporäre Datei fehlerhaft ist, da beim Speichern entschlüsselter Schlüssel auf der Festplatte ein Sicherheitsrisiko besteht. Werde es nächste Woche mal anschauen. Danke für die Hinweise zum ssl -Modul – wenn die Beschränkung außerhalb von requests wird es natürlich schwieriger

Ich habe es mir angesehen und das ssl Modul hat ein cadata Argument hinzugefügt, in dem Sie Pem-Daten als Rohstring übergeben können: https://docs.python.org/3/library/ssl.html #ssl.SSLContext.load_verify_locations

Wir müssten urllib3 an einer Reihe von Stellen patchen, damit dies funktioniert, also könnte ich dort anfangen

@erikbern Um es klar zu SSLContext Objekt an urllib3 mit einem TransportAdapter übergibt.

https://github.com/kennethreitz/requests/issues/2519 scheint mit diesem Problem identisch zu sein, daher sollten sie wahrscheinlich zusammengeführt werden

Irgendein Update zu diesem Problem Ich versuche, ein Client-Zertifikat zu verwenden, das mit einem Kennwort verschlüsselt ist und es nicht zum Laufen bringen kann. Sollte ich nach anderen Optionen als Anfragen suchen? Könnten Sie bitte so schnell wie möglich antworten.

Dokumentieren wir das? Ich habe das Gefühl, dass dies unsere am häufigsten nachgefragte Funktion ist.

Ich glaube, dieser Thread begann 2013 und ich habe bis zum Ende keine Lösung gefunden. Haben Sie eine Option für die Bereitstellung des Passworts bereitgestellt? oder ist das noch in Arbeit?

Ich versuche, Anfragen in einem von mir erstellten App-Sicherheitsprodukt zu verwenden. Also sind alle Hinweise hilfreich

@AnoopPillai Hast du diesen Kommentar überprüft? https://github.com/requests/requests/issues/1573#issuecomment -188125157

Ja, ich habe diesen Kommentar gelesen, dies ist ein Workaround, aber in meinem Fall möchte ich ihn nicht in 2 CERT-Dateien konvertieren, da dies außerhalb meiner Anwendung erfolgen muss. Darüber hinaus verwenden wir einen Tresor, um das Passwort für die verschlüsselte .pem-Datei zu speichern.

Dieses Passwort wird zur Laufzeit dynamisch von der App abgerufen, sodass keine harte Codierung erforderlich ist.

@AnoopPillai Okay.

@kennethreitz Nein, wir haben es nicht dokumentiert.

@AnoopPillai Ja, das funktioniert gut. Sie müssen nur einige untergeordnete Hooks verwenden. In diesem Fall erlauben wir Ihnen, SSLContext direkt auf Transportadapter-Ebene

@AnoopPillai Workaround mit einer temporären Datei, die ich für nützlich befunden habe: https://gist.github.com/erikbern/756b1d8df2d1487497d29b90e81f8068

Danke Lukasa, dass du mich wissen lässt, dass es eine Möglichkeit gibt.
Ich bin sehr neu in Python und verwende die Version 3.6. Könnten Sie mir helfen, wo ich Optionen wie Verschlüsselungen zum Übergeben des Passworts des Client-Zertifikats finden kann.
@Erikbern Ich habe die

@AnoopPillai Sie wollen load_cert_chain .

@Lukasa würde es dir etwas

Sorry Leute, meine mangelnde Erfahrung mit Python könnte der Grund sein, aber ich kann den oben erklärten Code Lukasa nicht ändern. Mein Code ist:

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

und ich bekomme einen fehler
TypeError: create_urllib3_context() hat ein unerwartetes Schlüsselwortargument 'load_cert_chain' erhalten

Das ist ein Fehler, ja. Sie möchten create_urllib3_context aufrufen und seinen Rückgabewert abrufen und dann load_cert_chain für das zurückgegebene Objekt aufrufen. Probieren Sie diese Funktionen im interaktiven Interpreter aus, um zu sehen, wie sie funktionieren.

Die auf meinem Mac installierte urllib3..util.ssl_.py verfügt nicht über die neueste Passwortoption.
das ist der Code

    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)

Die Passwortoption fehlt. Wie aktualisiere ich ssl_.py, um die neueste Version zu erhalten?

@AnoopPillai Das load_cert_chain für das zurückgegebene Objekt auf. Sie brauchen keine Änderungen an urllib3.

Um es klar zu sagen:

ctx = create_urllib3_context()
ctx.load_cert_chain(your_arguments_here)

Lass uns das dokumentieren :)

@erikbern Ich habe deine tempfile-Lösung ausprobiert, aber ich habe die folgende Fehlermeldung erhalten:

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')],)",),))

Unten ist mein Code:

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

Entschuldigung, schwer zu verstehen, warum es aus Ihrem Kommentar bricht. Ich habe es für eine Reihe von Anwendungen verwendet und hatte keine Probleme

@Lukasa Ich habe es mit dieser Codeänderung versucht (Code unten eingefügt) und bin mit dem gleichen Fehler aufgetreten, den ich mit der tempfile-Methode erhalten habe.

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 könnte es ein Setup-Problem auf meinem Laptop sein. Ich benutze einen 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: 

Glauben Sie, dass etwas mit dem Zertifikat nicht stimmt?

Was ist die Ausgabe von python -m requests.help ?

@Lukasa Die Ausgabe ist:

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

Bitte entfernen Sie das Fragezeichen aus Ihrer Befehlszeile.

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
}

Der Fehler, den Sie erhalten, wird also dadurch verursacht, dass wir das TLS-Zertifikat des Servers nicht validieren können. certifi und OpenSSL scheinen richtig zu sein, daher gehe ich davon aus, dass sich der Server schlecht benimmt. Welchen Server versuchst du zu erreichen?

Die Anwendung wird in Cloud AWS bereitgestellt. Aber wenn wir die API aufrufen, geht sie zuerst zu OSB, das das Zertifikat authentifiziert und dann die Anfrage an AWS weiterleitet.
Ich verwende das gleiche Zertifikat und mit Postbote oder meinem Ruby-Code funktioniert die API einwandfrei

Benötigen Sie ein bestimmtes Root-Zertifikat? Können Sie mir den zu erreichenden Hostnamen mitteilen?

Die Host-URL ohne den Pfad lautet https://credit-cards-accounts-qa.kdc.capitalone.com
Dies ist ein interner Endpunkt

Ja, also kann ich nicht sehen, was da los ist. Können Sie openssl s_client -showcerts -connect credit-cards-accounts-qa.kdc.capitalone.com:443 ausführen und die vollständige Ausgabe bereitstellen?

Gelöscht

Dies sieht sehr danach aus, als würden Sie kein global vertrauenswürdiges Root-Zertifikat verwenden. Wo ist das Root-Zertifikat für diesen Dienst?

Ich verwende keine anderen Zertifikate. Ich bin mir nicht sicher, ob hinter den Kulissen ein Root-Zertifikat verwendet wird. Gibt es eine Möglichkeit für mich, dies herauszufinden?

Ja, die Entwicklertools von Chrome teilen Ihnen die vollständige Zertifikatskette mit, die es verwendet.

Sie möchten wahrscheinlich kein internes Zertifikat online veröffentlichen, damit jeder es sehen kann ...

@erikbern Dies sind öffentliche Informationen. Sie können dasselbe Ergebnis erzielen, indem Sie denselben Befehl ausführen.

@SethMichaelLarson Aus @erikberns GitHub-Profil "Chief Troll Officer". Vielleicht waren sie nur Trolling?

@erikbern @sigmavirus24 Ah! Ich wusste nicht, mit wem ich sprach. Fortfahren! 🙇

Ich kann nichts sehen außer dem sha-1-Zertifikat, wenn ich vor dem Postboten renne
Vielleicht muss ich das irgendwie zu Pycharm hinzufügen

Wenn Sie buchstäblich in Chrome zur Website navigieren, sollte das ausreichen.

@SethMichaelLarson führt welchen Befehl aus? Zu Ihrer Information, der Kommentar wurde jetzt gelöscht, aber hier gab es früher einen ganzen BEGIN-ZERTIFIKAT-Blob ... afaik, Sie möchten das nicht online teilen

@erikbern Das war nur der öffentliche Schlüssel für das Zertifikat...

Zertifikate sind öffentliche Daten; sie werden bei jedem Verbindungsversuch im Klartext über das Netzwerk übertragen.

Ich ging zur Zertifikatskette und fand nur ein Sha-1-Zertifikat und das .Pem-Zertifikat, das ich verwende, um die API zu erreichen

@AnoopPillai Ich habe Ihren Beispielcode vom 1. September erhalten, der ohne Probleme mit einer clientseitigen Pem-Datei mit Passwort funktioniert. Es scheint, dass der Host ein normales Zertifikat verwendet. Mit @Lukasa vielen Dank!

Ich habe leider immer noch Probleme, selbst mit der Temp File-Methode. Ich kann die .pfx in Google Postman verwenden und habe keine Probleme bei der Authentifizierung (daher weiß ich, dass meine Anmeldeinformationen funktionieren), aber ich erhalte immer noch 401s mit Python. Leider hat mir der Support-Mitarbeiter der Firma, mit der ich zu tun habe, nicht viel geholfen - hat jemand Vorschläge zur Fehlerbehebung?

Zu diesem Zeitpunkt bin ich mir wirklich nicht sicher, wo ich überhaupt nach dem Problem suchen soll, da andere Leute von Erfolgen mit der Temp-Datei-Methode berichten und ich immer noch nichts von ihrem Cert-Management-Team gehört habe.

Jeder Rat wäre sehr dankbar - bitte lassen Sie es mich wissen, wenn ich zusätzliche Informationen zur Verfügung stellen kann, um dies zu vereinfachen.

Vielen Dank :)

Nur ein Vorschlag, haben Sie versucht, PFX in PEM zu konvertieren? Wenn der Server auch einen Benutzernamen/ein Passwort verwendet, müssen Sie dies der Get/Post-Anfrage mit auth=() hinzufügen. Ich verwende den obigen class DESAdapter(HTTPAdapter) Ansatz seit mehreren Wochen ohne Probleme mit einer passwortgeschützten PEM-Datei.

@ideasean Immer noch ungültige Anmeldeinformationen. Ich sollte die load_cert_chain auf eine .pem-Datei verweisen, die von der pfx_to_pem-Funktion generiert wurde, die für die Temp File-Methode geschrieben wurde, richtig? Es enthält den privaten Schlüssel und das Zertifikat.

Da die .pfx mit Postman funktioniert, aber hier nicht authentifiziert wird, kann das bedeuten, dass beim Konvertierungsprozess etwas schief läuft?

Ich habe nicht die temporäre Dateimethode verwendet. Ich habe den DESAdapter-Ansatz so ziemlich wie in AnoopPillais Beitrag vom 1. September oben beschrieben verwendet, beginnend mit -

Ich habe es mit dieser Codeänderung versucht (Code unten eingefügt) und bin mit dem gleichen Fehler aufgetreten, den ich mit der tempfile-Methode erhalten habe.

Ich kann nichts zum Konvertierungsprozess sagen, aber vielleicht ist es ein guter Test, die konvertierte Pem-Datei mit Postman zu verwenden?

Beachten Sie auch, dass ich den obigen Ansatz verwendet habe, da meine PEM-Datei verschlüsselt / passwortgeschützt war und Python-Anfragen dies derzeit nicht unterstützen. Wenn Ihr Pem nicht passwortgeschützt ist, sollten Sie in der Lage sein, native Anfragen per Link zu verwenden (aber dann haben Sie ein ungeschütztes Zertifikat in Ihrem Dateisystem).

@ideasean Ich habe die .pfx nach dieser Methode aufgebrochen und eine .pem-Datei mit Taschenattributen und Zertifikat sowie eine .pem-Datei mit Taschenattributen und einem verschlüsselten privaten Schlüssel erhalten.

Ich erhalte immer noch ungültige Anmeldeinformationen, ich denke, ich werde versuchen, die Zertifikate auf Postman zu übertragen und zu sehen, ob sie funktionieren, aber ich kann nicht herausfinden, warum ich diese .pfx anscheinend nicht richtig entpacken kann

Ich habe auch den openssl-Befehl openssl pkcs12 -in <my_pfx>.pfx -out certificate.cer -nodes ausprobiert, und es gibt immer noch einen 401-Fehler, wenn ich wie folgt zu ihm ändere: context.load_cert_chain('certificate.cer')

Ich habe das oben erwähnte .cer installiert und Postman fragt nicht einmal, ob ich es verwenden möchte, wenn ich den API-Aufruf mache (im Gegensatz zum Popup, wenn es darum bittet, das .pfx zu verwenden), ich bin mir nicht sicher, wie ich es sonst dazu bringen kann, dieses bestimmte Zertifikat zu verwenden da es in den Einstellungen kein "Zertifikate"-Panel gibt, wie es in den Dokumenten angegeben ist.

Möglicherweise verwenden Sie die Browserversion von Postman, die das Zertifikatsfenster, die SSL-Validierungsdeaktivierung usw. nicht enthält. Versuchen Sie den vollständigen Client, um die Zertifikatseinstellungen zu ändern. Vielleicht möchten Sie diese Diskussion dann in einem anderen Thread fortsetzen, da wir etwas vom Thema abschweifen.

@mkane848 hat Ihren ursprünglichen Kommentar gesehen, in dem Sie ein ValueError: String expected . Vielleicht möchten Sie https://github.com/pyca/pyopenssl/issues/701 und https://github.com/shazow/urllib3/issues/1275 überprüfen

Ich benutze mein privates Pem mit einem Passwort mit diesem:

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

Zu Ihrer Information habe ich gerade die PKCS#12-Unterstützung für requests als separate Bibliothek implementiert:

Der Code ist eine saubere Implementierung : Er verwendet weder Monkey-Patching noch temporäre Dateien. Stattdessen wird ein benutzerdefinierter TransportAdapter verwendet, der einen benutzerdefinierten SSLContext bereitstellt.

Jedes Feedback und Verbesserungen sind willkommen!

Natürlich würde ich mir wünschen, dass requests diese Funktionalität direkt zur Verfügung stellen würde, aber bis wir dort sind, wird diese Bibliothek den Schmerz lindern.

Es wäre sehr schön, wenn wir einfach folgendes machen könnten:

~~~
cert=("cert.pem", "key.pem", "somepassphrase") # separates Zertifikat/Schlüssel

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

~~~

...auch wenn es nur mit Python 3.3+ funktioniert hat. Dies wäre nur eine geringfügige Ergänzung der API-Oberfläche.

AFAICS würde dies eine kleine Änderung an urllib3 bedeuten, sodass HTTPSConnection ein optionales password Argument akzeptiert; dies wird durch ssl_wrap_socket und endet mit:

~if certfile:wenn das Passwort nicht None ist:context.load_cert_chain(Zertifikatdatei, Schlüsseldatei, Passwort)anders:context.load_cert_chain(certfile, keyfile)~

Dann wäre es abwärtskompatibel und löst nur dann eine Ausnahme aus, wenn Sie versuchen, eine private Schlüssel-Passphrase auf einer älteren Plattform zu verwenden, die sie nicht unterstützt.

Beachten Sie, dass der contrib/pyopenssl.py Adapter bereits dieses zusätzliche Argument für load_cert_chain , ebenso wie Python 2.7 .


Abgesehen davon: Ich verwende AWS KMS, um "geheime" Daten zu verwalten, daher würde ich das Schlüsselkennwort zur Laufzeit von KMS laden und nicht fest in die Anwendung codieren.

Ich persönlich wäre nicht gegen diese Änderung, da ich denke, dass sie unsere Benutzeroberfläche für viele Benutzer auf breiter Front erheblich verbessern würde.

@sigmavirus24 irgendwelche

@candlerb @kennethreitz Wäre es akzeptabel, den PKCS#12-Fall auch in diese API aufzunehmen?

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

Die Unterscheidung kann entweder durch die Dateierweiterung ( *.p12 gegenüber *.pem ) oder durch das Betrachten der ersten Bytes dieser Datei erfolgen.

Ich habe kein Problem damit, Anfragen zu erlauben, ein pkcs#12 zu nehmen, solange dies sicher durchgeführt werden kann - und meiner Meinung nach schließt dies das Schreiben des extrahierten privaten Schlüssels in eine temporäre Datei aus.

Beim Googeln nach Python pkcs#12 finde ich:

  • Jemandes Code, der den privaten Schlüssel ausschreibt
  • Ein anderer Code, der meiner Meinung nach von pyOpenSSL abhängt, um in pkcs#12. Es gibt das Zertifikat und den Schlüssel als Datenelemente zurück.

Um dies zu tun, denke ich, dass es notwendig wäre, die Dinge so zu verbinden, dass der Schlüssel/das Zertifikat selbst an OpenSSL übergeben wird, nicht die Dateinamen, die diese Dinge enthalten. Das klingt nach einer viel größeren Veränderung.

Wenn das zu schwer ist, bedeutet dies nur, dass der Benutzer pkcs#12 offline in PEM konvertieren muss, was ziemlich einfach ist (und dokumentiert werden kann).

@candlerb Wie ich in meinem vorherigen Kommentar (https://github.com/requests/requests/issues/1573#issuecomment-348968658) geschrieben habe, habe ich bereits eine saubere Implementierung erstellt, die sich gut in requests .

Die von dir beschriebenen Probleme sind also bereits gelöst.

Im Moment fügt meine Implementierung neue pkcs12_* Schlüsselwortargumente hinzu, um so weit wie möglich aus dem Weg zu gehen.

Aber ich denke, es sollte stattdessen in das Schlüsselwortargument cert werden, und meine Frage lautet:

  • Wäre das generell akzeptabel?
  • Wäre mein konkreter Vorschlag cert=('keycert.p12', None, 'somepassphrase') akzeptabel?
  • Wie sollten wir zwischen PKCS#12 und PEM unterscheiden? (Nach Dateinamensuffix oder nach Dateiinhalt?)

(Außerdem würde ich das lieber in requests als in meiner separaten requests_pkcs12 Bibliothek sehen. Aber angesichts des Alters dieser Ausgabe habe ich wenig Hoffnung, dass dies bald stromaufwärts gehen wird , wenn es eine konkrete Aussage dazu gibt, welche Art von Implementierung genau gewünscht wird, könnte ich vielleicht meine Implementierung entsprechend anpassen und einen Pull-Request vorschlagen.)

Also ein paar Dinge:

  1. Ich denke nicht, dass wir das Schlüsselwort cert nehmen und es so erweitern sollten. Es handelt sich um implizit strukturierte Daten, und die Leute sind bereits durch die Tupel im Schlüsselwort files verwirrt. Ich halte es für töricht, ein bekanntes schlechtes Muster fortzusetzen.

  2. Ich denke, wenn überhaupt, sollte der pkcs12-Adapter modifiziert und in den Request-Toolbelt eingebunden werden. Ich denke, es wäre besser, es zu ändern, um ssl_context einmal zu erstellen, anstatt das pkcs12-Passwort im Speicher dieses Objekts zu speichern.

Ich denke, es gibt noch andere Arbeit, die erledigt werden muss, bevor wir dies im allgemeineren Fall behandeln können, egal was passiert, und dazu gehört die Bestimmung der richtigen API dafür für Requests 3.0.

@sigmavirus24 Danke für das Feedback.

  1. Okay, lassen Sie uns die separaten pkcs12_* Schlüsselwörter beibehalten.
  2. Ja, das ist definitiv verbesserungswürdig. Dafür habe ich einen Issue-Tracker-Eintrag erstellt: https://github.com/m-click/requests_pkcs12/issues/2

Wie würde die Klasse PKCS#12 TransportAdapter in requests ? Würde diese Klasse einfach zu requests hinzugefügt werden, oder gibt es eine andere Möglichkeit, sie auf einer "tieferen" Ebene einzubinden, sodass sie ohne request()/get()/... Wrapper verwendet werden kann und ohne diese explizit laden zu müssen Adapter?

Meine Organisation muss PKCS12-Zertifikate verwenden und ist bereit, dafür die erforderlichen Verbesserungen an Ihrer Bibliothek vorzunehmen. Das Entschlüsseln der .p12-Dateien in .pem-Dateien wird als zu riskant angesehen und fügt einen zusätzlichen Schritt hinzu. Wir möchten Funktionen zum Generieren und Bereitstellen eines geeigneten ssl_context für eine bestimmte Sitzung hinzufügen. Ist diese Funktionalität noch immer von Ihrem Team akzeptiert, vorausgesetzt, sie wird ordnungsgemäß implementiert?

Nur kurz zur Erinnerung: Eine saubere Implementierung wurde von unserer Firma bereits bereitgestellt, jedoch als separater Adapter: https://github.com/m-click/requests_pkcs12

Fühlen Sie sich frei, es in einen Pull-Request für Anfragen selbst umzuformatieren.

Unterwegs möchten Sie vielleicht ein kleines Problem beheben: Der ssl_context sollte nicht für eine ganze Sitzung im Speicher gehalten werden, sondern so kurz wie möglich, nur für eine einzelne gegebene Verbindung. Siehe auch:

Falls Sie es unterwegs beheben, wäre es schön, wenn Sie es zusätzlich zu den Anfragen selbst als kleinen Pull-Request an https://github.com/m-click/requests_pkcs12 bereitstellen könnten.

Auf diese Weise würden alle Leute, die gerade die requests_pkcs12 Bibliothek verwenden, automatisch auch von dieser Verbesserung profitieren, ohne selbst auf die (damals verbesserte) neue API für Anfragen wechseln zu müssen.

Ja, https://github.com/m-click/requests_pkcs12 hat für mich funktioniert und genau das getan, was ich wollte. Vielen Dank @vog ! Ich hoffe, dass Anfragen das letztendlich unterstützen können.

Ich werde @vog auch für seine Implementierung danken, funktioniert wie erwartet und löst das Problem der Aufbewahrung von Zertifikaten / Schlüsseln in den nicht sicheren Speichern wie S3 in meinem Fall. Hoffentlich kann dies seinen Weg zu requests .

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen