1. 程式人生 > >PyCrypto密碼學庫原始碼解析(二)RSA引數生成

PyCrypto密碼學庫原始碼解析(二)RSA引數生成

Python Crypto庫原始碼解析(二) RSA引數生成


引用請註明出處,轉載請聯絡: [email protected]

本文主要講解pycrypto庫中RSA引數生成的實現方法。主要涉及的模組是PublicKey.RSA 和其繼承模組PublicKey._RSA。

0 本文需要的背景知識

0.1 RSA演算法的引數

選擇兩個大的質數p和q,p不等於q,計算N=pq
選擇一個整數e與(p-1)(q-1)互質,並且e小於(p-1)(q-1)。
計算d:d× e ≡ 1 (mod (p-1)(q-1))。
將p和q的記錄銷燬。
(N,e)是公鑰,(N,d)是私鑰。

0.2 RSA引數的要求

根據上文所述,可以根據引數生成過程來逐一解釋引數的要求。

選擇兩個大的質數p和q,p不等於q

現如今主流的RSA金鑰要求達到的位數是1024位元,例如在瀏覽器的公鑰證書中存放的公鑰就是1024位元的,而推薦使用的安全性較高的則是2048位元,也就是要求p*q的結果應該達到1024位元。

選擇一個整數e與(p-1)(q-1)互質,並且e小於(p-1)(q-1)

這一點是數論要求的,否則會造成演算法被攻擊,要保障gcd(e,(p-1)(q-1))=1也就是要保證gcd(e,p-1) = gcd(e,q-1) = 1,這一點實際上在上一篇文章中(2.6 獲取一個滿足RSA要求的強素數

)已經講解過:

if e and is_possible_prime:
            if e & 1:
                if GCD (e, X-1) != 1:
                    is_possible_prime = 0
            else:
                if GCD (e, divmod((X-1),2)[0]) != 1:
                    is_possible_prime = 0

getStrongPrime函式在生成素數的時候,就已經將e和p-1的關係做過判斷,才進行的輸出。

所以目前的問題只剩下如何選擇引數e,對於引數e,在庫的文件中有這樣一段說明:

Public RSA exponent. It must be an odd positive integer.
It is typically a small number with very few ones in its binary representation.
The default value 65537 (= 0b10000000000000001 ) is a safe choice: other common values are 5, 7, 17, and 257.

也就是說一般會選擇65537,當然也可以選擇其他給出的供選項,但是要保證一條就是一定要是奇數。

計算d:d× e ≡ 1 (mod (p-1)(q-1))

這一步只需要使用歐幾里得方法求逆就可以。

最後,為了演算法的安全需要將p和q的記錄銷燬,即使不銷燬也絕對不可以洩露。

1 RSAImplementation類

在PublicKey.RSA模組中,包含RSA實現類,按照說明文件所說,該類是一個與RSA金鑰相關的類,

class RSAImplementation(object):

包含有函式 _ init _, generate, construct, importKey,分別是構建函式、生成新的RSA金鑰、從RSA元件構造金鑰、從外部匯入金鑰。
值得一提的是外部匯入金鑰函式支援包括X.509、PKCS#1、OpenSSH等多種格式。

由於本文主要關注引數的生成模組,所以研究的重點放在generate函式上。

2 generate函式

generate(self, bits, randfunc=None, progress_func=None, e=65537)

這是函式的宣告,可以見得需要輸入的是bit位數,也就是生成金鑰的位數,randfunc和上篇文章中講的一樣,如果不指定就會使用Crypto.Random.new(),而progress_func用於等待金鑰生成時與使用者互動的用途。

接下來一步一步解析該函式:

 if bits < 1024 or (bits & 0xff) != 0:
            raise ValueError("RSA modulus length must be a multiple of 256 and >= 1024")

在上篇文章中提到,getStrongPrime函式對於輸入的位數是有限制的,這裡則是再次判斷,要求金鑰位數位 256的倍數而且不小於1024。

if e%2==0 or e<3:
            raise ValueError("RSA public exponent must be a positive, odd integer larger than 2.")

如果不使用預設的e=65537,那麼就一定要滿足非偶正奇數的條件。

rf = self._get_randfunc(randfunc)
obj = _RSA.generate_py(bits, rf, progress_func, e)    # TODO: Don't use legacy _RSA module
key = self._math.rsa_construct(obj.n, obj.e, obj.d, obj.p, obj.q, obj.u)

可以看到,在generate的部分,使用的是_RSA模組的generate_py函式,後面作者還留下了註釋,將會在之後的庫中不在此處使用legacy 的_RSA模組。
接下來,就來看看_RSA.generate_py裡是什麼樣的。

3 _RSA.generate_py函式

函式宣告如下:

def generate_py(bits, randfunc, progress_func=None, e=65537)

首先除開progress_func不說,其他部分的程式碼邏輯是這樣的:

    obj=RSAobj()
    obj.e = int(e)

    # Generate the prime factors of n
    p = q = 1
    while number.size(p*q) < bits:
        p = pubkey.getStrongPrime(bits>>1, obj.e, 1e-12, randfunc)
        q = pubkey.getStrongPrime(bits - (bits>>1), obj.e, 1e-12, randfunc)

    # It's OK for p to be larger than q, but let's be
    # kind to the function that will invert it for
    # the calculation of u.
    if p > q:
        (p, q)=(q, p)
    obj.p = p
    obj.q = q
    obj.u = pubkey.inverse(obj.p, obj.q)
    obj.n = obj.p*obj.q
    obj.d=pubkey.inverse(obj.e, (obj.p-1)*(obj.q-1))

    assert bits <= 1+obj.size(), "Generated key is too small"
    return obj

首先是轉化e引數為int型,然後使用迴圈生成大素數p和q,生成所使用的函式便是getStrongPrime,關於該函式的解析可以參見這裡,由於極其複雜的getStrongPrime考慮到了很多有關引數要求的限制,才讓這個部分較為簡潔。

生成了p和q之後,按照p小q大的順序交換位置,至此e、p和q這幾個最重要的引數已經確定,接下來就是

計算N=p*q
計算d:d× e ≡ 1 (mod (p-1)(q-1))。

至於這裡出現的u = p1modq參看_RSAobj的說明可以知道,意義是:

  • u, the CRT coefficient (1/p) mod q

至此,所有RSA演算法所需要的引數已經生成完畢。
下一篇文章((三)PublicKey模組概覽)將講解該庫中RSA演算法的實現過程。

附錄 庫中給出的另一種計算引數方法

在庫的PublicKey._slowmath模組中,還給出了另一種RSA引數的構造過程,z這個函式通過部分引數計算餘下的引數並且返回一個完整例項。這裡貼上全部的原始碼,不作解釋,有興趣的讀者可以自行了解。

def rsa_construct(n, e, d=None, p=None, q=None, u=None):
    """Construct an RSAKey object"""
    assert isinstance(n, int)
    assert isinstance(e, int)
    assert isinstance(d, (int, type(None)))
    assert isinstance(p, (int, type(None)))
    assert isinstance(q, (int, type(None)))
    assert isinstance(u, (int, type(None)))
    obj = _RSAKey()
    obj.n = n
    obj.e = e
    if d is None:
        return obj
    obj.d = d
    if p is not None and q is not None:
        obj.p = p
        obj.q = q
    else:
        # Compute factors p and q from the private exponent d.
        # We assume that n has no more than two factors.
        # See 8.2.2(i) in Handbook of Applied Cryptography.
        ktot = d*e-1
        # The quantity d*e-1 is a multiple of phi(n), even,
        # and can be represented as t*2^s.
        t = ktot
        while t%2==0:
            t=divmod(t,2)[0]
        # Cycle through all multiplicative inverses in Zn.
        # The algorithm is non-deterministic, but there is a 50% chance
        # any candidate a leads to successful factoring.
        # See "Digitalized Signatures and Public Key Functions as Intractable
        # as Factorization", M. Rabin, 1979
        spotted = 0
        a = 2
        while not spotted and a<100:
            k = t
            # Cycle through all values a^{t*2^i}=a^k
            while k<ktot:
                cand = pow(a,k,n)
                # Check if a^k is a non-trivial root of unity (mod n)
                if cand!=1 and cand!=(n-1) and pow(cand,2,n)==1:
                    # We have found a number such that (cand-1)(cand+1)=0 (mod n).
                    # Either of the terms divides n.
                    obj.p = GCD(cand+1,n)
                    spotted = 1
                    break
                k = k*2
            # This value was not any good... let's try another!
            a = a+2
        if not spotted:
            raise ValueError("Unable to compute factors p and q from exponent d.")
        # Found !
        assert ((n % obj.p)==0)
        obj.q = divmod(n,obj.p)[0]
    if u is not None:
        obj.u = u
    else:
        obj.u = inverse(obj.p, obj.q)
    return obj

相關推薦

PyCrypto密碼原始碼解析RSA引數生成

Python Crypto庫原始碼解析(二) RSA引數生成 引用請註明出處,轉載請聯絡: [email protected] 本文主要講解pycrypto庫中RSA引數生成的實現方法。主要涉及的模組是PublicKey.RSA 和其繼承模

PyCrypto密碼原始碼解析隨機數和大素數生成

Python Crypto庫原始碼解析(一) 隨機數和大素數的生成 引用請註明出處,轉載請聯絡: [email protected] 本系列文章(Python Crypto庫原始碼解析)主要講解Python的密碼學工具庫PyCrypto的原始碼

Spring原始碼解析——元件註冊2

    import com.ken.service.BookService; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.

認真的 Netty 原始碼解析

Channel 的 register 操作 經過前面的鋪墊,我們已經具備一定的基礎了,我們開始來把前面學到的內容揉在一起。這節,我們會介紹 register 操作,這一步其實是非常關鍵的,對於我們原始碼分析非常重要。 register 我們從 EchoClient 中的 connect() 方法出發,或者 E

jquery 1.7.2原始碼解析構造jquery物件

構造jquery物件 jQuery物件是一個類陣列物件。 一)建構函式jQuery() 建構函式的7種用法:   1.jQuery(selector [, context ]) 傳入字串引數:檢查該字串是選擇器表示式還是HTML程式碼。如果是選擇器表示式,則遍歷文件查詢匹配的DOM元

java集合原始碼解析--AbstractCollection

今天帶來的是java單列頂層介面的第一個輕量級實現:AbstractCollection 我們直接進入正題,先來看看它的宣告: package java.util; //可以從名字上同樣看到 AbstractCollection 是一個抽象類,所以並不能例項化, //這個類只是作

EventBus原始碼解析—釋出事件和登出流程

1.EventBus原始碼解析(一)—訂閱過程 2.EventBus原始碼解析(二)—釋出事件和登出流程 前言 上一篇部落格已經比較詳細的講解了EventBus的註冊過程,有了上一篇部落格的基礎,其實關於EventBus的原始碼中的其他流程就非常好理解了,尤其是我

Spring原始碼解析:obtainFreshBeanFactory

spring的ApplicationContext容器的初始化流程主要由AbstractApplicationContext類中的refresh方法實現。 而refresh()方法中獲取新工廠的主要是由obtainFreshBeanFactory()實現的,後續的操作均是beanFactoty的進一步處理。

Redis5.0原始碼解析----------連結串列

基於Redis5.0 連結串列提供了高效的節點重排能力, 以及順序性的節點訪問方式, 並且可以通過增刪節點來靈活地調整連結串列的長度 每個連結串列節點使用一個 adlist.h/listNode 結構來表示: //adlist.h - A generic do

ThreadPoolExecutor原始碼解析

1.ThreadPoolExcuter執行例項 首先我們先看如何新建一個ThreadPoolExecutor去執行執行緒。然後深入到原始碼中去看ThreadPoolExecutor裡面使如何運作的。 public class Test { public

redis原始碼解析動態字串sds基本功能函式

1. 簡介   本文繼上文基礎上,分析動態字串的功能函式,位於sds.c。由於函式較多,本篇介紹實現動態變化的基本增刪新建釋放函式。 2. 原始碼分析   sdsHdrSize()函式用於返回sdshdr的大小,主要使用sizeof()函式實現。 /*返回sds

OKHttp 3.10原始碼解析:攔截器鏈

本篇文章我們主要來講解OKhttp的攔截器鏈,攔截器是OKhttp的主要特色之一,通過攔截器鏈,我們可以對request或response資料進行相關處理,我們也可以自定義攔截器interceptor。 上一篇文章中我們講到,不管是OKhttp的同步請求還是非同步請求,都會呼叫RealCal

OkHttp原始碼解析

上一篇講到OkHttp的整體流程,但是裡面有一個很重要的方法getResponseWithInterceptorChain還沒有講到,這個方法裡面包括了整個okHttp最重要的攔截器鏈,所以我們今天來講解一下。 Response getResponseWithI

Java容器——HashMapJava8原始碼解析

在前文中介紹了HashMap中的重要元素,現在萬事俱備,需要刨根問底看看實現了。HashMap的增刪改查,都離不開元素查詢。查詢分兩部分,一是確定元素在table中的下標,二是確定以指定下標元素為首的具體位置。可以抽象理解為二維陣列,第一個通過雜湊函式得來,第二個下標則是連結串列或紅黑樹來得到,下面

Yolov2原始碼解析

一、資料集製作 首先是從官網上下載VOC2012資料集,這裡我個人得到是訓練集檔案:VOCtrainval_11-May-2012,為了減輕訓練開銷,我將驗證集作為測試集,通過將Main資料夾下的val.txt改名為test.txt檔案,將資料集製作成hdf5檔案的形式。 import os

Muduo網路原始碼分析 定時器TimeQueue,Timer,TimerId

首先,我們先要明白為什麼需要設計這樣一個定時器類? 在開發Linux網路程式時,通常需要維護多個定時器,如維護客戶端心跳時間、檢查多個數據包的超時重傳等。如果採用linux的SIGALARM訊號實現,則會帶來較大的系統開銷,且不便於管理。 Muduo 的 Timer

RxJava2 原始碼解析

概述 知道源頭(Observable)是如何將資料傳送出去的。 知道終點(Observer)是如何接收到資料的。 何時將源頭和終點關聯起來的 知道執行緒排程是怎麼實現的 知道操作符是怎麼實現的 本篇計劃講解一下4,5. RxJava最強大的莫過

Spark2.3.2原始碼解析: 8. RDD 原始碼解析 textFile 返回的RDD例項是什麼

  本文主要目標是分析RDD的例項物件,到底放了什麼。 從程式碼val textFile = sc.textFile(args(0)) 開始: 直接看textFile 原始碼: 你會發現呼叫的是hadoop的api,通過 hadoopFile 讀取資料,返回一個hadoop

DCGANs原始碼解析

model.py DCGANs大部分都在一個叫做 DCGAN 的 Python 類(class)中(model.py)。像這樣把所有東西都放在一個類中非常有用,因為訓練後中間狀態可以被儲存起來,以便後面使用。 首先讓我們定義生成器和鑑別器(上一篇已經介紹過了

mybatis通用mapper原始碼解析

1.javabean的屬性值生成sql /** * 獲取所有查詢列,如id,name,code... * * @param entityClass * @return */ public static String getAllColumns(C