python连接.net端遇到的一些问题回顾


最近用python作为客户端与.net下的web service进行数据交互,包括RSA密钥方面的交互遇到的一些问题的记录。

连接web service

在网上查看了一些方法后,最后使用suds这个库作为连接web service的工具,这个工具使用起来比较方便,算是比较小巧的,安装就直接在python shell下使用easy_install suds就可以了。

>>> import suds
>>> url='http://localhost:8000/Host/host?wsdl'
>>> client = suds.client.Client(url)
>>> print client

Suds ( https://fedorahosted.org/suds/ )  version: 0.4 GA  build: R699-20100913

Service ( Server ) tns="http://tempuri.org/"
    Prefixes (1)
    ns0 = "http://schemas.microsoft.com/2003/10/Serialization/"
    Ports (1):
        (BasicHttpBinding_Iserver)
            Methods (4):
                GetData(xs:base64Binary requestData, )
                GetDataCopy(xs:base64Binary requestData, )
                GetDataCopy2(xs:base64Binary requestData, )
                GetKey()
            Types (3):
                ns0:char
                ns0:duration
                ns0:guid


>>> server = client.service
>>> 

上面是我本地启动的一个服务,url就是服务发布的地址,在将地址输入到浏览器后可以看到发布的一些元数据。而在使用print client打印出来的一些信息主要是Method这栏,这里显示了四个方法,是我服务发布的四个对外接口,也是客户端调用的四个接口,包含有参数的一些基本信息。最后的server就可以调用服务接口的对象,例如value=server.GetKey(),这里的value就是服务返回的结果数据。对于suds的具体用法及详细的信息可以查看suds文档,文档写的非常详细。返回的数据如果是一堆的字母,试着用base64解密后看看。


RSA加密的交互

由于服务端是使用.net发布的,相应的rsa的表示格式为xml,而python下的我使用的是rsa这个库,格式为pem的,两者的格式不一致,若要交互就必须将格式统一后才行。rsa这个库有很多方法,具体可以使用help(rsa)查看,类PublicKey有个构造方法需要两个参数key=rsa.PublicKey(n,e),结果key就是构造出的公钥,由于我这里GetKey方法将服务端的公钥传递到了客户端:

>>> serverPubKey_CS = base64.b64decode(server.GetKey())
>>> print serverPubKey_CS
<RSAKeyValue><Modulus>2VCRn9IUE8ufRHIswpsTFiYr6sYT/SorKw/A1GTE4ux3oS12z4uRqrWGeGkXZ8q68HvmCFSn4h5xS+yvN17Fnw==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue> 
>>> serverPubKey_py = rsa.PublicKey(bytes2int(base64.b64decode('2VCRn9IUE8ufRHIswpsTFiYr6sYT/SorKw/A1GTE4ux3oS12z4uRqrWGeGkXZ8q68HvmCFSn4h5xS+yvN17Fnw==')),bytes2int(base64.b64decode('AQAB')))

这里serverPubKey_CS为解密后的服务端公钥,是个xml格式的,而上面key=rsa.PublicKey(n,e)构造方法中的n就是<Modulus>n</Modulus>中间的这部分数据,当然e就是<Exponent>e</Exponent>中间的数据了。两个数据全部获得,那么我们就可以转换成python下面的公钥了,当然这两部分数据都是经过base64加密的,所以我们首先要进行解密,由于python的密钥的en都是一串数据,整数类型,那么还需要将解密后的数据转换成int类型,这里有个方法bytes2int就是完成这部分功能的,上面代码中的serverPubKey_py就是最终的python下面的公钥格式了。

def bytes2int(bytes):
        """Converts a list of bytes or a string to an integer
    
       >>> (128*256 + 64)*256 + + 15
       8405007
       >>> l = [128, 64, 15]
       >>> bytes2int(l)
       8405007
       """
     
        if not (type(bytes) is types.ListType or type(bytes) is types.StringType):
            raise TypeError("You must pass a string or a list")
     
        # Convert byte stream to integer
        integer = 0
        for byte in bytes:
            integer *= 256
            if type(byte) is types.StringType: byte = ord(byte)
            integer += byte
     
        return integer

python的密钥格式转换成C#能看懂的xml格式就是上面的逆过程,我们可以获得公钥的ne,然后将它们都转换成byte并且加密后组合成xml的格式,下面的代码完成了这个过程的转换:

pubKey_CS = '<RSAKeyValue><Modulus>'+base64.b64encode(int2bytes(pubKey.n))+'</Modulus><Exponent>'+base64.b64encode(int2bytes(pubKey.e))+'</Exponent></RSAKeyValue>'

pubkey_CS就是C#能看懂的密钥格式。

def int2bytes(number):
        if not (type(number) is types.LongType or type(number) is types.IntType):
            raise TypeError("You MUST ENTER A LONG OR INT")
        string =""
        while number>0:
            string = "%s%s" % (chr(number & 0xff),string)
            number/=256
        return string

RSA在加密解密的时候注意事项

我们在使用rsa加密解密的时候最多,被加密解密的消息有个最大长度,由key.size决定,在消息长度超过最大值的时候我们需要对消息进行分段加密和解密。

def encodeSplit(message,clientPubKey):
        '''
       分段加密 由于我这边的密钥长度为512,所以下面的53为key.size/8-11得出
       '''
        count = len(message)
        if count>53:
            jiami=""
            index = 0
            while index<count:
                i = 53 if count-index>53 else count-index
                submsg = message[index:index+i]
                index+=i
                j = rsa.encrypt(submsg,clientPubKey)
                jiami+=j
            return jiami
        else:
            jiami = rsa.encrypt(message,clientPubKey)
            return jiami


    def decodeSplit(message,clientPriKey):
        """
       消息在传送过来之前是经过base64加密的,所以要先进行base64解密后才进行RSA分段解密
       64为key.size/8,密钥长度为512
       """
        message = base64.b64decode(base64.b64decode(message))
        count = len(message)
        if count>64:
                jiemi=""
                index=0
                while index<count:
                        i = 64 if count-index>64 else count-index
                        submsg = message[index:index+i]
                        index+=i
                        j = rsa.decrypt(submsg,clientPriKey)
                        jiemi+=j
                return jiemi
        else:
                jiemi = rsa.decrypt(message,clientPriKey)
                return jiemi

第一次写python程序,感觉和写伪代码似的,挺爽!


非专业娱乐型程序员。
Published under (CC) BY-NC-SA in categories 技术  tagged with python  rsa