初等数论四大定理:

1.欧拉定理:

n和a为正整数,且 n,a互素,即 gcd(a,n) = 1 ,则:

​  7E4V00.png

证明省略。。。。。

2.威尔逊定理:

主要针对大数阶乘

初等数论中,威尔逊定理给出了判定一个自然数是否为素数充分必要条件

即:当且仅当p为素数时:( p -1 )! ≡ -1 ( mod p )

因为很少涉及,此处证明省略。。。。。。

例题:求value_p

1
2
3
4
x = 11124440021748127159092076861405454814981575144744508857178576572929321435002942998531420985771090167262256877805902135304112271641074498386662361391760451
y = 11124440021748127159092076861405454814981575144744508857178576572929321435002942998531420985771090167262256877805902135304112271641074498386662361391661439
value_p = sympy.nextprime((math.factorial(y)) % x) # Hint:这里直接计算会溢出,请你仔细观察 x 和 y 的特征
return value_p

求法:

HVMqC6.png

1
2
3
4
5
6
7
8
9
10
11
12
x = 11124440021748127159092076861405454814981575144744508857178576572929321435002942998531420985771090167262256877805902135304112271641074498386662361391760451
y = 11124440021748127159092076861405454814981575144744508857178576572929321435002942998531420985771090167262256877805902135304112271641074498386662361391661439
# cal (y! % x)
prd = 1
for i in range(y + 1,x - 1):
prd *= i
prd %= x

p = inverse(prd, x)
p = sympy.nextprime(p)

得到 `p = 10569944080090591401315432556965818857327680380269154543273468441025963038065648915158194147019839932524599260058098616377893091051396090650574162446875263`

给出个例题:

原题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import sympy
import random

def myGetPrime():
A= getPrime(513)
print(A)
B=A-random.randint(1e3,1e5)
print(B)
return sympy.nextPrime((B!)%A)
p=myGetPrime()
#A1=21856963452461630437348278434191434000066076750419027493852463513469865262064340836613831066602300959772632397773487317560339056658299954464169264467234407
#B1=21856963452461630437348278434191434000066076750419027493852463513469865262064340836613831066602300959772632397773487317560339056658299954464169264467140596

q=myGetPrime()
#A2=16466113115839228119767887899308820025749260933863446888224167169857612178664139545726340867406790754560227516013796269941438076818194617030304851858418927
#B2=16466113115839228119767887899308820025749260933863446888224167169857612178664139545726340867406790754560227516013796269941438076818194617030304851858351026

r=myGetPrime()

n=p*q*r
#n=85492663786275292159831603391083876175149354309327673008716627650718160585639723100793347534649628330416631255660901307533909900431413447524262332232659153047067908693481947121069070451562822417357656432171870951184673132554213690123308042697361969986360375060954702920656364144154145812838558365334172935931441424096270206140691814662318562696925767991937369782627908408239087358033165410020690152067715711112732252038588432896758405898709010342467882264362733
c=pow(flag,e,n)
#e=0x1001
#c=75700883021669577739329316795450706204502635802310731477156998834710820770245219468703245302009998932067080383977560299708060476222089630209972629755965140317526034680452483360917378812244365884527186056341888615564335560765053550155758362271622330017433403027261127561225585912484777829588501213961110690451987625502701331485141639684356427316905122995759825241133872734362716041819819948645662803292418802204430874521342108413623635150475963121220095236776428
#so,what is the flag?

这道题求解的重点在sympy.nextPrime((B!)%A)

因为A和B题目都给我们了,所以难点就在于求B的阶乘的模,而B又很大,计算阶乘不大现实

好在有一个定理可以解决这个问题,即威尔逊定理

当且仅当p为素数时:( p -1 )! ≡ -1 ( mod p )

借助此定理,各个参数都可以计算出来

例如,p = sympy.nextPrime((B1!)%A1)

由威尔逊定理,( A1 -1 )! ≡ -1 ( mod A1 )

即B1! * k = ≡ -1 ( mod A1 ),其中k = (A1-1)! / (B1)!,也就是B1之后的数字之积

两边同时乘上k的逆元,B1! = -k-1 (mod A1)

从而可计算p = sympy.nextPrime((B1!)%A1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import gmpy2
import sympy
import binascii
#p = sympy.nextPrime((B!)%A)
def getPrime(A, B):
k = 1
for i in range(B+1, A):
k = (k*i) % A
res = (-gmpy2.invert(k, A)) % A
return sympy.nextprime(res)

if __name__ == '__main__':
A1=21856963452461630437348278434191434000066076750419027493852463513469865262064340836613831066602300959772632397773487317560339056658299954464169264467234407
B1=21856963452461630437348278434191434000066076750419027493852463513469865262064340836613831066602300959772632397773487317560339056658299954464169264467140596
A2=16466113115839228119767887899308820025749260933863446888224167169857612178664139545726340867406790754560227516013796269941438076818194617030304851858418927
B2=16466113115839228119767887899308820025749260933863446888224167169857612178664139545726340867406790754560227516013796269941438076818194617030304851858351026
n=85492663786275292159831603391083876175149354309327673008716627650718160585639723100793347534649628330416631255660901307533909900431413447524262332232659153047067908693481947121069070451562822417357656432171870951184673132554213690123308042697361969986360375060954702920656364144154145812838558365334172935931441424096270206140691814662318562696925767991937369782627908408239087358033165410020690152067715711112732252038588432896758405898709010342467882264362733
e=0x1001
c=75700883021669577739329316795450706204502635802310731477156998834710820770245219468703245302009998932067080383977560299708060476222089630209972629755965140317526034680452483360917378812244365884527186056341888615564335560765053550155758362271622330017433403027261127561225585912484777829588501213961110690451987625502701331485141639684356427316905122995759825241133872734362716041819819948645662803292418802204430874521342108413623635150475963121220095236776428
p = getPrime(A1, B1)
q = getPrime(A2, B2)
r = n//(p*q)
d = gmpy2.invert(e,(p-1)*(q-1)*(r-1))
m = gmpy2.powmod(c, d, n)
flag = binascii.unhexlify(hex(m)[2:])
print(flag)

3.费马小定理:

费马小定理(Fermat’s little theorem)是数论中的一个重要定理,在1636年提出。如果p是一个质数,而整数a不是p的倍数,则有a^(p-1)≡1(mod p)。

实际上,它是欧拉定理的一个特殊情况,即:

HVMH4x.png

脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> n =221
>>>a = 38
>>>pow(a ,n -1,n)
1
"""221 may be a prime number."""
import random
def isprime(n,k=128):
if n<2:
return False
for _ in range(k):
a = random.randrange(1,n)
if pow(a,n-1,n)!=1:
return False
return True

给出个例题:知道p,q的生成方式

原题如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from random import choice
from Crypto.Util.number import isPrime, sieve_base as primes
from flag import flag


def getPrime(bits):
while True:
n = 2
while n.bit_length() < bits:
n *= choice(primes)
if isPrime(n + 1):
return n + 1

e = 0x10001
m = int.from_bytes(flag.encode(), 'big')
p, q = [getPrime(2048) for _ in range(2)]
n = p * q
c = pow(m, e, n)

# n = 32849718197337581823002243717057659218502519004386996660885100592872201948834155543125924395614928962750579667346279456710633774501407292473006312537723894221717638059058796679686953564471994009285384798450493756900459225040360430847240975678450171551048783818642467506711424027848778367427338647282428667393241157151675410661015044633282064056800913282016363415202171926089293431012379261585078566301060173689328363696699811123592090204578098276704877408688525618732848817623879899628629300385790344366046641825507767709276622692835393219811283244303899850483748651722336996164724553364097066493953127153066970594638491950199605713033004684970381605908909693802373826516622872100822213645899846325022476318425889580091613323747640467299866189070780620292627043349618839126919699862580579994887507733838561768581933029077488033326056066378869170169389819542928899483936705521710423905128732013121538495096959944889076705471928490092476616709838980562233255542325528398956185421193665359897664110835645928646616337700617883946369110702443135980068553511927115723157704586595844927607636003501038871748639417378062348085980873502535098755568810971926925447913858894180171498580131088992227637341857123607600275137768132347158657063692388249513
# c = 26308018356739853895382240109968894175166731283702927002165268998773708335216338997058314157717147131083296551313334042509806229853341488461087009955203854253313827608275460592785607739091992591431080342664081962030557042784864074533380701014585315663218783130162376176094773010478159362434331787279303302718098735574605469803801873109982473258207444342330633191849040553550708886593340770753064322410889048135425025715982196600650740987076486540674090923181664281515197679745907830107684777248532278645343716263686014941081417914622724906314960249945105011301731247324601620886782967217339340393853616450077105125391982689986178342417223392217085276465471102737594719932347242482670320801063191869471318313514407997326350065187904154229557706351355052446027159972546737213451422978211055778164578782156428466626894026103053360431281644645515155471301826844754338802352846095293421718249819728205538534652212984831283642472071669494851823123552827380737798609829706225744376667082534026874483482483127491533474306552210039386256062116345785870668331513725792053302188276682550672663353937781055621860101624242216671635824311412793495965628876036344731733142759495348248970313655381407241457118743532311394697763283681852908564387282605279108

分析可知,p-1和q-1前一万个素数中的若干数的乘积,可以使用费马小定理

根据费马小定理,有a(b-1) - 1是b的倍数

拓展一下,又有ak*(b-1) - 1是b的倍数,系数k为正整数

回到题目中来,根据p和q的生成方式,我们知道(p-1)和(q-1)由前10000个素数中的若干个素数相乘得到

因此,前10000个素数的乘积记为∏,则∏肯定为(p-1)和(q-1)的倍数,令∏ = k*(p-1)

由费马小定理,有2∏-1 = ak*(p-1)-1是p的倍数
gcd(2∏-1, n) = p,得到p,但是直接计算2∏计算量会很大,所以再进一步优化

2∏ = 1 (mod p),即2∏ = 1 + k1*p

而2∏ % n = 2∏ - k2n = 2∏ - k2pq

两边同时% p,有2∏ % n = 2∏ (mod p)

所以同样有2∏ % n = 1 (mod p)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import gmpy2
import binascii
from Crypto.Util.number import isPrime, sieve_base as primes

e = 0x10001
n = 32849718197337581823002243717057659218502519004386996660885100592872201948834155543125924395614928962750579667346279456710633774501407292473006312537723894221717638059058796679686953564471994009285384798450493756900459225040360430847240975678450171551048783818642467506711424027848778367427338647282428667393241157151675410661015044633282064056800913282016363415202171926089293431012379261585078566301060173689328363696699811123592090204578098276704877408688525618732848817623879899628629300385790344366046641825507767709276622692835393219811283244303899850483748651722336996164724553364097066493953127153066970594638491950199605713033004684970381605908909693802373826516622872100822213645899846325022476318425889580091613323747640467299866189070780620292627043349618839126919699862580579994887507733838561768581933029077488033326056066378869170169389819542928899483936705521710423905128732013121538495096959944889076705471928490092476616709838980562233255542325528398956185421193665359897664110835645928646616337700617883946369110702443135980068553511927115723157704586595844927607636003501038871748639417378062348085980873502535098755568810971926925447913858894180171498580131088992227637341857123607600275137768132347158657063692388249513
c = 26308018356739853895382240109968894175166731283702927002165268998773708335216338997058314157717147131083296551313334042509806229853341488461087009955203854253313827608275460592785607739091992591431080342664081962030557042784864074533380701014585315663218783130162376176094773010478159362434331787279303302718098735574605469803801873109982473258207444342330633191849040553550708886593340770753064322410889048135425025715982196600650740987076486540674090923181664281515197679745907830107684777248532278645343716263686014941081417914622724906314960249945105011301731247324601620886782967217339340393853616450077105125391982689986178342417223392217085276465471102737594719932347242482670320801063191869471318313514407997326350065187904154229557706351355052446027159972546737213451422978211055778164578782156428466626894026103053360431281644645515155471301826844754338802352846095293421718249819728205538534652212984831283642472071669494851823123552827380737798609829706225744376667082534026874483482483127491533474306552210039386256062116345785870668331513725792053302188276682550672663353937781055621860101624242216671635824311412793495965628876036344731733142759495348248970313655381407241457118743532311394697763283681852908564387282605279108
#primes为前10000个素数的列表
#计算prd = ∏ primes
prd = 1
for i in primes:
prd *= i
#p为(2^prd-1)和n的公约数
p = gmpy2.gcd(gmpy2.powmod(2,prd,n)-1,n)
q = n // p
d = gmpy2.invert(e,(p-1)*(q-1)) #计算私钥d
m = gmpy2.powmod(c, d, n) #解密

flag = binascii.unhexlify(hex(m)[2:])
print(flag)

4.中国剩余定理

《孙子算经》其卷下的第26题为:

今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二,问物几何?
答曰:‘二十三’。
术曰:三三数之剩二,置一百四十;五五数之剩三,置六十三,七七数之剩二,置三十,并之。得二百三十三,以二百一十减之,即得。凡三三数之剩一,则置七十;五五数之剩一,则置二十一;七七数之剩一,则置十五;一百六以上以一百五减之即得。

(即——

一个整数除以3余2、除以5余3、除以7余2,求这个整数。
答案:23
具体解法分三步:
找出三个数:从3和5的公倍数中找出被7除余1的最小数15,从3和7的公倍数中找出被5除余1 的最小数21,最后从5和7的公倍数中找出除3余1的最小数70。
用15乘以2(2为最终结果除以7的余数),用21乘以3(3为最终结果除以5的余数),同理,用70乘以2(2为最终结果除以3的余数),然后把三个乘积相加15∗2+21∗3+70∗2得到和233。
用233除以3,5,7三个数的最小公倍数105,得到余数23,即233%105=23。这个余数23就是符合条件的最小数。

直接给出:

中国剩余定理的公式:

HVML8K.png

中国剩余定理扩展——求解模数不互质情况下的线性方程组:

HVM7U1.png

脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import math
import gmpy2

def merge(a1,n1,a2,n2):
d = math.gcd(n1,n2)
c = a2-a1
if c%d!=0:
return 0
c = (c%n2+n2)%n2
c = c//d
n1 = n1//d
n2 = n2//d
c *= gmpy2.invert(n1,n2)
c %= n2
c *= n1*d
c += a1
global n3
global a3
n3 = n1*n2*d
a3 = (c%n3+n3)%n3
return 1
def exCRT(a,n):
a1=a[0]
n1=n[0]
le= len(a)
for i in range(1,le):
a2 = a[i]
n2=n[i]
if not merge(a1,n1,a2,n2):
return -1
a1 = a3
n1 = n3
global mod
mod=n1
return (a1%n1+n1)%n1
a = []
n = []
_m = exCRT(a,n)

//或
from xenny.ctf.crypto.modern.asymmetric.rsa.lowe_broadcast import attack
print(long_to_bytes(attack(n_list=n_list, c_list=c_list, e=9)[0])) //开9次方

其余小定理:

1.对于任意 r,k1,k2,当 k2 为 k1 因子时,r mod k2=(r mod k1) mod k2

2.如果有a%b=c,则有(a+k∗b)%b=c(k为非零整数)

3.如果a%b=c,那么(a∗k)%b=a%b+a%b+…+a%b=c+c+…+c=k∗c(k>0)