web-dev-qa-db-fra.com

Mousse sur https avec cert

J'ai un service de savon sous Apache avec ssl, suds fonctionne greate sans ssl.
J'ai un certificat client (mes fichiers .crt et user.p12).
Comment dois-je configurer le client suds pour le faire fonctionner avec le service via https?

sans certificats je vois

urllib2.URLError: <erreur urlopen [Errno 1] _ssl.c: 499: erreur: 14094410: routines SSL: SSL3_READ_BYTES: échec de la négociation de l'alerte sslv3>
30
Andrey Koltsov

Il semble que vous souhaitiez vous authentifier à l'aide d'un certificat client, et non d'un certificat serveur comme cela a été indiqué dans certains commentaires. J'ai eu le même problème et j'ai pu écrire un transport personnalisé pour SUDS. Voici le code qui fonctionne pour moi.

Vous aurez besoin de vos certificats au format PEM pour que cela fonctionne; OpenSSL peut facilement effectuer cette conversion, bien que je ne me souvienne pas de la syntaxe exacte.

import urllib2, httplib, socket
from suds.client import Client
from suds.transport.http import HttpTransport, Reply, TransportError

class HTTPSClientAuthHandler(urllib2.HTTPSHandler):
    def __init__(self, key, cert):
        urllib2.HTTPSHandler.__init__(self)
        self.key = key
        self.cert = cert

    def https_open(self, req):
        #Rather than pass in a reference to a connection class, we pass in
        # a reference to a function which, for all intents and purposes,
        # will behave as a constructor
        return self.do_open(self.getConnection, req)

    def getConnection(self, Host, timeout=300):
        return httplib.HTTPSConnection(Host,
                                       key_file=self.key,
                                       cert_file=self.cert)

class HTTPSClientCertTransport(HttpTransport):
    def __init__(self, key, cert, *args, **kwargs):
        HttpTransport.__init__(self, *args, **kwargs)
        self.key = key
        self.cert = cert

    def u2open(self, u2request):
        """
        Open a connection.
        @param u2request: A urllib2 request.
        @type u2request: urllib2.Requet.
        @return: The opened file-like urllib2 object.
        @rtype: fp
        """
        tm = self.options.timeout
        url = urllib2.build_opener(HTTPSClientAuthHandler(self.key, self.cert))
        if self.u2ver() < 2.6:
            socket.setdefaulttimeout(tm)
            return url.open(u2request)
        else:
            return url.open(u2request, timeout=tm)

# These lines enable debug logging; remove them once everything works.
import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger('suds.client').setLevel(logging.DEBUG)
logging.getLogger('suds.transport').setLevel(logging.DEBUG)

c = Client('https://YOUR_URL_HERE',
    transport = HTTPSClientCertTransport('PRIVATE_KEY.pem',
                                         'CERTIFICATE_CHAIN.pem'))
print c
37
nitwit

Une autre solution consiste à utiliser la bibliothèque de demandes comme moyen de transport qui prend mieux en charge SSL. Voici ce que j'utilise maintenant pour accéder aux services SOAP via https à l'aide de mousse: -

import requests
from suds.transport.http import HttpAuthenticated
from suds.transport import Reply, TransportError

class RequestsTransport(HttpAuthenticated):
    def __init__(self, **kwargs):
        self.cert = kwargs.pop('cert', None)
        # super won't work because not using new style class
        HttpAuthenticated.__init__(self, **kwargs)

    def send(self, request):
        self.addcredentials(request)
        resp = requests.post(request.url, data=request.message,
                             headers=request.headers, cert=self.cert)
        result = Reply(resp.status_code, resp.headers, resp.content)
        return result

Et puis vous pouvez instancier le client de mousse comme: -

headers = {"Content-TYpe" : "text/xml;charset=UTF-8",
           "SOAPAction" : ""}
t = RequestsTransport(cert='/path/to/cert', **credentials)
client = Client(wsdl_uri, location=send_url, headers=headers,
                transport=t))

Mise à jour

Nous utilisons maintenant Zeep , qui utilise requests en dessous.

11
k4ml

Sur la base de la réponse @ k4ml, je n'ai ajouté que la open() qui permet de récupérer le WSDL à l'aide du certificat.

Cette méthode devrait corriger le suds.transport.TransportError: HTTP Error 403: Forbidden lors de la tentative de récupération d'un WSDL (lors de la création du client) servi derrière un service HTTPS.

import requests
from suds.transport.http import HttpAuthenticated
from suds.transport import Reply, TransportError

class RequestsTransport(HttpAuthenticated):
    def __init__(self, **kwargs):
        self.cert = kwargs.pop('cert', None)
        # super won't work because not using new style class
        HttpAuthenticated.__init__(self, **kwargs)

    def open(self, request):
        """
        Fetches the WSDL using cert.
        """
        self.addcredentials(request)
        resp = requests.get(request.url, data=request.message,
                             headers=request.headers, cert=self.cert)
        result = io.StringIO(resp.content.decode('utf-8'))
        return result

    def send(self, request):
        """
        Posts to service using cert.
        """
        self.addcredentials(request)
        resp = requests.post(request.url, data=request.message,
                             headers=request.headers, cert=self.cert)
        result = Reply(resp.status_code, resp.headers, resp.content)
        return result

Note latérale, j'ai également suggéré de modifier la réponse de k4ml, mais cela peut prendre un certain temps avant d'être approuvé.

6
Andre Miras

Étendre la solution @ k4ml, en utilisant la clé cert + Cela résoudra des exceptions comme:

requests.exceptions.SSLError: [SSL] PEM lib (_ssl.c:2599)

Solution:

import requests

from suds.client import Client
from suds.transport.http import HttpAuthenticated
from suds.transport import Reply, TransportError


class RequestsTransport(HttpAuthenticated):

    def __init__(self, **kwargs):
        self.cert = kwargs.pop('cert', None)
        HttpAuthenticated.__init__(self, **kwargs)

    def send(self, request):
        self.addcredentials(request)
        resp = requests.post(
            request.url,
            data=request.message,
            headers=request.headers,
            cert=self.cert,
            verify=True
        )
        result = Reply(resp.status_code, resp.headers, resp.content)
        return result



t = RequestsTransport(cert=('<your cert.pem path>', 'your key.pem path'))
headers = {"Content-Type": "text/xml;charset=UTF-8", "SOAPAction": ""}
client = Client(wsdl_url, headers=headers, transport=t)
4
Darkaico

La fonction de sécurité SSL est activée automatiquement python 2.7.9+ qui rompt la mousse et les autres bibliothèques python. Je partage un correctif qui peut résoudre ce problème:

Localisez votre bibliothèque suds et remplacez la fonction u2handlers dans le fichier suds/trasnport/http.py par la ligne suivante:

import ssl
def u2handlers(self):
        """
        Get a collection of urllib handlers.

        @return: A list of handlers to be installed in the opener.
        @rtype: [Handler,...]

        """
        handlers = []
        unverified_context = ssl.create_default_context()
        unverified_context.check_hostname = False
        unverified_context.verify_mode = ssl.CERT_NONE
        unverified_handler = urllib2.HTTPSHandler(context=unverified_context)
        handlers.append(unverified_handler)
        handlers.append(urllib2.ProxyHandler(self.proxy))
        #handlers.append(urllib2.ProxyHandler(self.proxy))
        return handlers 

Remarque: ce n'est pas une façon recommandée de le faire.

3
Rishabh Tariyal