PythonのRSA暗号化とPBE暗号化

11173 ワード

最近インタフェースを書く時、RSA暗号化とPBE暗号化を使う必要がある場合、相手の会社が提供するDEMOはすべてJAVAで、pythonで実現する必要があります.ネットで検索してみると、pythonのRSA暗号化という書き込みは多いですが、PBEは少ないです.だから私はRSA暗号化の上で出会った穴について話して、みんなは喜んでいます.PBEは中の塩、鍵を暗号化します.
RSA
RSA暗号化とは?
実はRSAは1種の非対称の暗号化で、それでは何が非対称の暗号化ですか?非対称暗号化は公開鍵暗号化とも呼ばれ、つまり私は公開鍵と秘密鍵に分かれている鍵を持っています.秘密鍵はこっそり残しておいて、他の人に見せない.そして公開鍵を他の人(誰であろうと)に渡します.他の人が公開鍵でデータを暗号化した後、この暗号化されたデータは私(秘密鍵を持っている人)が秘密鍵で解くことができ、残りの誰も解けません.これが非対称暗号化です.
これはただ一方的で、ただ私がデータを解いただけです.私は情報を取得します.
では、私はどのように他の人に情報を伝えますか?他の人はどのように私が伝えた情報が私が送ったことを保証しますか?この場合、秘密鍵が暗号化され、デジタル署名とも呼ばれます.私はデータ署名後のデータと未署名のデータを一斉に他の人に送り、他の人は公開鍵で暗号化されたデータを復号し、復号後のデータと未署名のデータを比較し、同じであればデータソースが正しいことを表します.
少し混乱しているかもしれませんが、私は前回非常にはっきりした例を見て、私は記憶で大体話しました.
社長は従業員の明ちゃんを地方に派遣してビジネスチャンスを考察した.
明ちゃんの任務はすばらしくて、すぐにビジネスチャンスを見つけました.この时、彼はボスに报告したいのですが、ネットは安全ではありません.ボスに情报メールを送ると、メールがライバルに手に入る可能性が高いです.今度の考察も失敗した.
そこで、明ちゃんは事前にボスからもらった公開鍵で情報を暗号化しました.
これにより、ボスは秘密鍵で情報を解読することができ、ライバルは文字化けしてぼんやりするしかない.
今回の情報は社長を満足させ、社長は明ちゃんに深く考察させることにした.
しかし、この深く考察し続ける命令がネット上で伝送されるのは安全ではありません.ライバルは情報を得られませんが、ハッカーを通じて命令を改ざんすることができます.明ちゃんを会社に帰らせたら、お得ではありません.時間も無駄になります.
このとき、ボスは秘密鍵で自分が下した命令に署名し、署名後のデータと明文の命令を一斉に送り、明ちゃんはメールを受け取った後、署名後のデータと命令を公開鍵で検証し、一致すれば、改ざんされていないことを代表し、安心して大胆な事実ボスの命令を表すことができる.
………………………………………分割線……………………………………………………………………
では、私が書いたインタフェースは、そうです.
我が社はインタフェースを通じて相手の会社のデータを取得して、データを取得してパラメータを伝達して、相手はパラメータによってそれから相応のデータを返します.
相手の会社は秘密鍵と公開鍵を生成し、当社は秘密鍵と公開鍵を生成し、双方は公開鍵を交換する.
1、相手会社の公開鍵を使用してすべてのパラメータを暗号化し、暗号化してbase 64符号化する.
2、弊社の秘密鍵を使用して暗号化されたデータに署名し、署名した後base 64符号化を行う.
3、それから暗号化したデータと署名したデータを一斉に相手に送る.
ピット1:RSAは最長117のデータの暗号化のみをサポートするため、セグメント暗号化が必要であり、さらに先につなぎ合わせてからbase 64符号化を行う必要があり、並べ替える前にずっと書いていたのはbase 64符号化してからつなぎ合わせることである.
ピット2:セグメント暗号化後に対応する署名を行うには、MD 5トランスコードが必要です.
talk is more, show your code.
Java:
暗号化:
private static final int MAX_ENCRYPT_BLOCK = 117;
public static final String KEY_ALGORITHM = "RSA"

/** *//**
     * 

* *

* * @param data * @param publicKey (BASE64 ) * @return * @throws Exception */ public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception { byte[] keyBytes = Base64.decode(publicKey); X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key publicK = keyFactory.generatePublic(x509KeySpec); // Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, publicK); int inputLen = data.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK); } else { cache = cipher.doFinal(data, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_ENCRYPT_BLOCK; } byte[] encryptedData = out.toByteArray(); out.close(); return encryptedData; }

このコードを使用すると、次のことに気づきます.
1、セグメント化して暗号化し、最後に暗号化された暗号文を直接結合する(out.write(cache,0,cache.length);)
2、直接returnデータ(反対側のプログラムでbase 64を行う)
署名:
public static final String SIGNATURE_ALGORITHM = "MD5withRSA";    
/** *//**
     * 

* *

* * @param data * @param privateKey (BASE64 ) * * @return * @throws Exception */ public static String sign(byte[] data, String privateKey) throws Exception { byte[] keyBytes = Base64.decode(privateKey); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec); Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initSign(privateK); signature.update(data); return Base64.encode(signature.sign()); }

このコードにより,カプセル化された暗号文を直接署名することが分かったが,セグメント署名を必要としないのは,暗号化後の暗号文の長さが117ビット未満であるためである.彼の暗号化方法はSIGNATURE_ALGORITHM = "MD5withRSA"なので、python署名もMD 5を行う必要があります.
ではpythonコードは
import base64
from Crypto.Hash import MD5
from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5
from Crypto.Signature import PKCS1_v1_5 as Signature_pkcs1_v1_5
from Crypto.PublicKey import RSA


def get_encrypt_data(params):
    """    """
    params = json.dumps(params)
    params = params.encode("utf-8")
    length = len(params)
    default_length = 117
    if length < default_length:
        return encrypt_data(params)
    offset = 0
    params_lst = []
    while length - offset > 0:
        if length - offset > default_length:
            params_lst.append(encrypt_data(params[offset:offset+default_length]))                               
        else:           
            params_lst.append(encrypt_data(params[offset:]))
        offset += default_length
    res = "".join(params_lst)
    return res, base64.b64encode(res)


def encrypt_data(params):
    """         """
    key = public_key
    rsakey = RSA.importKey(base64.b64decode(key))
    cipher = Cipher_pkcs1_v1_5.new(rsakey)
    text = cipher.encrypt(params)
    return text


def sign_data(params):
    """     """
    key = private_key
    rsakey = RSA.importKey(base64.b64decode(key))
    signer = Signature_pkcs1_v1_5.new(rsakey)
    digest = MD5.new(params)
    sign = signer.sign(digest)
    return base64.b64encode(sign)

パラメータをjson化しutf-8符号化し、117ビット毎に暗号化し、最後に暗号化暗号文を接続しbase 64符号化する.digest = MD5.new(params)を用いたことに注意してください.署名アルゴリズムもMD 5であることを示しています.
PBE
PBEアルゴリズム再JavaではMD 5とDESアルゴリズムで構築された対称暗号化である.すなわち、暗号解読は、一連の鍵を用いて行われる.
コードを見てみましょう.
Java:
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import org.apache.commons.codec.binary.Base64;

public class DesEncrypter {
    Cipher ecipher;
    Cipher dcipher;
    byte[] salt = { (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
            (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03 };

    /**
     *     
     * 
     * @param passPhrase
     *            apikey      
     * @throws Exception
     */
    public DesEncrypter(String passPhrase) throws Exception {
        int iterationCount = 2;
        KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt,
                iterationCount);
        SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES")
                .generateSecret(keySpec);
        ecipher = Cipher.getInstance(key.getAlgorithm());
        dcipher = Cipher.getInstance(key.getAlgorithm());
        AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt,
                iterationCount);
        ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
        dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
    }

    /**
     *   
     * 
     * @param str
     *                   
     * @return
     * @throws Exception
     */
    public String encrypt(String str) throws Exception {
        str = new String(str.getBytes(), "UTF-8");
        return Base64.encodeBase64String(ecipher.doFinal(str.getBytes()));
}

私たちは気づいた.1つの塩がある:対応するpython塩は:"\xA9\x9B\xC8\x32\x56\x35\xE3\x03"対応するpython 2である.7コード:
from Crypto.Hash import MD5
from Crypto.Cipher import DES


def get_encrypt_param(params):
    """         """    
    _salt = "\xA9\x9B\xC8\x32\x56\x35\xE3\x03"
    _iterations = 2
    data = []
    
    #        value  utf-8  
    for i in params:
        data.append("{}={}".format(i, params[i].encode("utf-8")))
    str_param = "&".join(data)
    padding = 8 - len(str_param) % 8
    str_param += chr(padding) * padding

    hasher = MD5.new()
    hasher.update(apikey)
    hasher.update(_salt)
    result = hasher.digest()

    #   hash   ,  java  iterationCount  
    for i in range(1, _iterations):
        hasher = MD5.new()
        hasher.update(result)
        result = hasher.digest()

    encoder = DES.new(result[:8], DES.MODE_CBC, result[8:16])
    encrypted = encoder.encrypt(str_param)
    return encrypted.encode("base64")

入力したパラメータをutf-8符号化しhashを行い,最後に暗号化した.
注意:javaコードのiterationCountはいくらですか.hashを何回ループしますか.
python 3のコードではstrは直接hashを行うことができないのでutf-8にキャプチャして暗号化し、最後のencryptedはencodeメソッドがなく、手動でBase 64符号化しかできない.
python 3コードは次のとおりです.
import base64
from Crypto.Hash import MD5
from Crypto.Cipher import DES


def get_encrypt_param(params):
"""         """

    #   _salt   ,     bytes
    _salt = b"\xA9\x9B\xC8\x32\x56\x35\xE3\x03"
    _iterations = 2
    data = []
for i in params:
        data.append("{}={}".format(i, params[i]))
    str_param = "&".join(data)
    padding = 8 - len(str_param) % 8
    str_param += chr(padding) * padding

    hasher = MD5.new()

    #  apikey  utf-8  
    hasher.update(apikey.encode())
    hasher.update(_salt)
    result = hasher.digest()
for i in range(1, _iterations):
        hasher = MD5.new()
        hasher.update(result)
        result = hasher.digest()
    encoder = DES.new(result[:8], DES.MODE_CBC, result[8:16])
    encrypted = encoder.encrypt(str_param)
    #   base64  
    return base64.b64encode(encrypted)

しかし、パラメータに中国語がある場合、彼は間違いを報告します.
ValueError: Input strings must be a multiple of 8 in length

検査コードによりパラメータをutf-8符号化していないことが分かった.
しかし、私たちがコードした後:
for i in params:
    data.append("{}={}".format(i, params[i].encode("utf-8")))

python 3のメカニズムのため、符号化後に中国語がbytesになり、相手が復号して認識できないため、私たちは別の道を切り開くしかない.
研究の結果、別のライブラリ、pyDesを使用することにしました.
コードは次のとおりです.
import pyDes


def get_encrypt_param(params):
    """         """
    _salt = b"\xA9\x9B\xC8\x32\x56\x35\xE3\x03"
    _iterations = 2
    data = []
    for i in params:
        data.append("{}={}".format(i, params[i]))
    str_param = "&".join(data)

    hasher = MD5.new()
    hasher.update(apikey.encode())
    hasher.update(_salt)
    result = hasher.digest()
    for i in range(1, _iterations):
        hasher = MD5.new()
        hasher.update(result)
        result = hasher.digest()

    despy = pyDes.des(result[:8], pyDes.CBC, padmode=pyDes.PAD_PKCS5, IV=result[8:16])
    encrypt_data = despy.encrypt(str_param.encode())
    return base64.b64encode(encrypt_data)