web-dev-qa-db-ja.com

証明書付きのhttpsを介したSuds

私は、sslを使用するApacheの下でsoapサービスを使用しています。sdsは、sslがなくても機能します。
クライアント証明書があります(my.crtおよびuser.p12ファイル)。
Sudsクライアントを構成して、https経由のサービスで動作するようにするにはどうすればよいですか?

私が見る証明書なし

urllib2.URLError:<urlopenエラー[Errno 1] _ssl.c:499:エラー:14094410:SSLルーチン:SSL3_READ_BYTES:sslv3アラートハンドシェイクエラー>
30
Andrey Koltsov

一部のコメントで述べられているserver証明書ではなくclient証明書を使用して認証したいようです。同じ問題があり、SUDSのカスタム転送を作成できました。これが私のために働くコードです。

これを機能させるには、PEM形式の証明書が必要です。 OpenSSLはこの変換を簡単に実行できますが、正確な構文は覚えていません。

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

別の回避策は、リクエストライブラリをトランスポートとして使用することです。これにより、sslのサポートが向上します。これは私がsudsを使用してhttps経由でSOAPサービスにアクセスするために現在使用しているものです:-

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

そして、あなたは次のようにsudsクライアントをインスタンス化することができます:-

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

更新

現在、下にrequestsを使用する Zeep を使用しています。

11
k4ml

@ k4mlの回答に基づいて、証明書を使用してWSDLをフェッチできるopen()のみを追加しました。

このメソッドは、HTTPSサービスの背後にある(クライアントの作成時に)WSDLをフェッチしようとするときにsuds.transport.TransportError: HTTP Error 403: Forbiddenを修正する必要があります。

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

余談ですが、k4mlの回答を編集するよう提案しましたが、承認されるまでに時間がかかる場合があります。

6
Andre Miras

証明書とキーを使用して@ k4mlソリューションを拡張すると、次のような例外が解決されます。

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

解決:

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

SSLセキュリティ機能は自動で有効になっていますpython 2.7.9+これはsudsおよびその他のライブラリを破壊しますpythonライブラリです。これを修正できるパッチを共有しています:

Sudsライブラリを見つけて、suds/trasnport/http.pyファイルのu2handlers関数を次の行に置き換えます。

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 

注:これは推奨される方法ではありません。

3
Rishabh Tariyal