1. 程式人生 > >RSA加密演算法及特定條件下的破解

RSA加密演算法及特定條件下的破解

在對稱加密演算法中,資訊的傳送方和接收方用同樣的金鑰對資訊進行加密和解密,而如何安全傳遞key本身成為了一個十分嚴重的問題,因此產生了公開金鑰密碼體制。

公開金鑰密碼體制使用不同的金鑰來進行加密和解密,加密金鑰公開(公鑰),而解密金鑰私有(私鑰),加密演算法和解密演算法都是公開的。解密金鑰由加密金鑰決定,但卻無法從加密金鑰運算得到。

由於公鑰和加密演算法的公開,理論上攻擊者可以嘗試所有可能的原文並對其加密來與密文進行對比從而找出原文,而實際上這樣的計算量過於龐大。所以公開金鑰密碼體制僅僅是計算上的安全,而非完美的安全。

RSA是目前最有影響力的公鑰加密演算法,其安全性基於對大素數相乘的結果沒有有效的因式分解方法。在實際操作上,只要RSA的金鑰足夠長的,其加密的資訊是無法被破解的。

RSA加密演算法的流程 

Alice向Bob傳送加密資訊:

1. Bob產生一對金鑰對,包含一個公鑰和一個私鑰。

2. Bob釋出他的公鑰

3. Alice用Bob的公鑰對資訊進行加密併發送給Bob

4. Bob用私鑰對收到的加密資訊進行解密

RSA數學原理

取兩個大素數pqm = pq,取d使de = 1 mod (p-1)(q-1)。 加密演算法:ek(x) = xemod m 解密演算法:dk(y) = ydmod m 公鑰:em 金鑰:dm 注:通常用快速冪取模演算法進行a^n mod bd的運算。
要證明該演算法的正確性,我們只需證明x = dk(ek(x))
,即對資訊加密並解密後,能否得到原始資訊。 證明: 要證明x等於dk(ek(x)) = (xe mod m)dmod m = xde mod pq 因為p和q都是素數,所以我們只需證明 xde = x mod p 且 xde x mod q 即可。

已知de = 1 mod (p-1)(q-1),所以存在h使得de - 1 = h (p -1)(q - 1) 

xde xde-1x = xh(p-1)(q-1)x= (xh(q-1))p-1x

由費馬小定理 = 1h(q- 1)x mod p = x 

(費馬小定理:當x為整數p為素數,xp-1 = 1 mod p

類似可得 x

de x mod q,得證。

金鑰對生成

1. 任選兩個大素數p和q(具體做法為隨機選擇數並驗證是否為素數,常用的檢驗方法有米勒-拉賓素性檢驗等) 2. 計算m = pq 3. 計算ψ = (p - 1)(q - 1) 4. 選擇一個整數e(1<e<ψ) 使 gcd(e,ψ) = 1。實際操作上,e通常在選擇p、q之前就確定,經常為65537 5. 計算d = e-1mod ψ 6. 釋出m 和 e 作為公鑰,d保密。

例子

----Bob產生金鑰對 1. Bob選擇 p = 5, q = 11 2. 計算m = pq = 55 3. ψ = (p - 1)(q - 1) = 40 4. 選擇e = 3,並用擴充套件歐幾里得演算法驗證gcd(e,ψ) = 1 5. 計算d =  e-1mod ψ = 27 6. 釋出m = 55和e = 3作為公鑰,私鑰d = 27 ----Alice想要傳送x = 14給Bob 7. Alice用Bob的公鑰計算ek(x) =143mod 55 = 49 ----Bob接收資訊並解密 8. Bob用私鑰解密:dk(x) =4927mod 55 = 14

RSA破解

當RSA沒有被正確使用時,它可以非常脆弱。例如對文字加密時,我們將L個字元放到一個整數中進行加密,當L很小時,破解是可行的。

破解的基本思路是用公開的公鑰和加密演算法對所有可能的值進行進行加密,建立原文和密文的對應表。

分解 c = m^e (mod n) = (m1 * m2)^e (mod n) = (m1^e)*(m2^e) (mod n) = c1*c2 (mod n) ,可以使所需的運算量減少。

破解方法:

已知密文c,公鑰(e,n)和引數r(通常r>=k/2),尋找合適的引數r也是破解工作的一部分。 r越大計算量越大破解也越精確,通常當r接近所需的大小時已經可以由人工猜測原文了。 1. 建立表格 for i = 1 to 2^r     table[i] = i^e mod  m 2. 找到 i,j < 2^r 使得c * inv(i^e mod n, n) mod m = x,x為j^e mod n (y = inv(x,n) 當 (x*y=1) mod n) 3. 如果能在表格中找到這樣的x,則原文 = i*j mod m 證明: 我們所尋找的 i,j滿足 c*(i^e)^-1 = j^e mod n (i*j)^e = (i^e)(j^e) = (i^e *c*(i^e)^-1) = c mod n 所以原文為i*j 

讀取文字檔案進行RSA加密的程式碼

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.math.BigInteger;
import java.util.Random;

public class RSA {
    final static int L = 3;                     //size of block - the encryption deals with L characters at a time
    final static int PQ_BITLENGTH = 32;         //size of p,q
    final static int KEY_BITLENGTH = 32;        //size of e
    
    static Random rnd = new Random();
    static BigInteger big_two = new BigInteger("2");
    
    public static void main(String[] args) {
            
        //System.out.println("test sqm: "+sqm(new BigInteger("3"),new BigInteger("5"),new BigInteger("7")).toString());
        //System.out.println("test generatePrime: "+generatePrime(PRIME_BITLENGTH));
        
        long startTime = System.nanoTime();
        
        BigInteger[] keyPair = generateKey();
        
        System.out.println("public key: (" + keyPair[0] + "," + keyPair[1] + ")");
        System.out.println("private key: (" + keyPair[2] + "," + keyPair[3] + ")");
        //System.out.println("test keyPair: "+sqm(sqm(big_two,keyPair[0],keyPair[1]),keyPair[2],keyPair[3]));
        
        BigInteger[] cipher = encrypt(readFile("rsa"+".plain"),L,keyPair[0],keyPair[1]);
        
        long endTime = System.nanoTime();
        System.out.println("Run time: "+(endTime-startTime)+"ns"); 
        
        writeFile(cipher,keyPair);
    }
	/*
	 * Write cipher into file "rsa.crypto"
	 * Write private key into file "rsa.key"
	 * Write public key into file "rsa.pub"
	 */
	static void writeFile(BigInteger[] cipher, BigInteger[] keyPair)
	{
	    try{
	        //crypto
	        File crypto = new File("rsa"+".crypto");
	        crypto.createNewFile();
	        FileWriter cryptoWriter = new FileWriter(crypto);
	        for(int i=0;cipher[i]!=null;i++)
	                cryptoWriter.write(cipher[i].toString()+"\n");
	        cryptoWriter.close();
	        
	        //private key
	        File key = new File("rsa"+".key");
	        key.createNewFile();
	        FileWriter keyWriter = new FileWriter(key);
	        keyWriter.write(keyPair[2].toString()+"\n");
	        keyWriter.write(keyPair[3].toString()+"\n");
	        keyWriter.close();
	        
	        //public key
	        File pub = new File("rsa"+".pub");
	        pub.createNewFile();
	        FileWriter pubWriter = new FileWriter(pub);
	        pubWriter.write(keyPair[0].toString()+"\n");
	        pubWriter.write(keyPair[1].toString()+"\n");
	        pubWriter.close();
	            
	    }catch(Exception e){
	        e.printStackTrace();
	    }
	}
	//e(k) = x^e mod n where x represent L characters
	static BigInteger[] encrypt(int[] plain, int L, BigInteger key_e, BigInteger key_n)
	{
	    BigInteger[] cipher= new BigInteger[100/L+1];
	    int temp = 0;
	    int index = 0;
	    for(int i=0,l=L;plain[i]!=-1;i++)
	    {
	        temp = (temp<<8) + plain[i];
	        if(l==1)
	        {
	            cipher[index] = sqm(new BigInteger(""+temp),key_e,key_n);//encrypt
	            l=L;
	            index++;
	            temp = 0;
	        }
	        else
	            l--;
	    }
	    return cipher;
	}
	/*
	 *  Read plain text into an integer array,
	 *  characters are encoded on 8 bits (typically Latin1 encoding)
	 */
	static int[] readFile(String fileName)
	{
	    int[] plain = new int[102];
	    for(int i=0;i<plain.length;i++)
	            plain[i]=-1;
	    int temp = 0;
	    int i = 0;
	    try {
	        FileReader reader = new FileReader(fileName);
	        while((temp=reader.read())!=-1)
	        {
	            plain[i] = temp;
	            i++;
	        }
	    } catch (Exception e) {
	            e.printStackTrace();
	    }
	    //when the number of characters to encode is not a multiple of L
	    while(i%L!=0)
	    {
	        plain[i]=0;
	        i++;
	    }
	    return plain;
	}
	/*
	 * Generate key pair for RSA
	 * @return 
	 *                 result[0]&result[1] - public key
	 *                 result[2]&result[3] - private key
	 */
	static BigInteger[] generateKey()
	{
	    BigInteger result[] = new BigInteger[4];
	    BigInteger p = generatePrime(PQ_BITLENGTH);
	    BigInteger q,phi,n,e,d;
	    do{
	        q = generatePrime(PQ_BITLENGTH);
	    }while (q.equals(p));
	    n = p.multiply(q);//n = p*q
	    phi = (p.subtract(BigInteger.ONE)).multiply((q.subtract(BigInteger.ONE)));//phi = (p-1)*(q-1)
	    do{
	        e = generatePrime(KEY_BITLENGTH);
	    }while(!gcd(e,phi).equals(BigInteger.ONE)||e.compareTo(phi)>=0);
	    d = inv(e,phi);
	    
	    result[0]=e;
	    result[1]=n;
	    result[2]=d;
	    result[3]=n;
	    
	    return result;
	}
	/*
	 * Use Extended Euclidean Algorithm to find y = inv(x,n) is such that (x*y=1) mod n
	 * ax+by=1 return x+b
	 */
	static BigInteger inv(BigInteger x, BigInteger n)
	{
	    return ext_gcd(x,n)[0].add(n);
	}
	static BigInteger[] ext_gcd(BigInteger a, BigInteger b)
	{
	    BigInteger [] tempxy = new BigInteger[2];
	    BigInteger temp = null;
	    if (b.equals(BigInteger.ZERO))
	    {
	        tempxy[0]=BigInteger.ONE;
	        tempxy[1]=BigInteger.ZERO;
	    }
	    else
	    {
	        tempxy = ext_gcd(b,a.mod(b));
	        temp = tempxy[0];
	        tempxy[0] = tempxy[1];
	        tempxy[1] = temp.subtract((tempxy[1].multiply(a.divide(b))));
	    }
	    return tempxy;                
	}
	
	//calculate the greatest common divisor of a and b
	static BigInteger gcd(BigInteger a, BigInteger b)
	{
	    BigInteger temp;
	    while(!b.equals(BigInteger.ZERO))
	    {
	            temp = b;
	            b = a.mod(b);
	            a = temp;
	    }
	    return a;
	}
	//generate a strong probable prime with the specified bitLength
	static BigInteger generatePrime(int bitLength){
	    BigInteger x = new BigInteger(bitLength,rnd);//[0,2^bitLength-1]
	    //change the most significant bit to 1 to make sure it meets the bitLength requirement
	    x = x.or(BigInteger.ONE.shiftLeft(bitLength-1));//[2^(bitLength-1),2^bitLength-1]
	    if(x.mod(big_two).equals(BigInteger.ZERO))//if n is even
	            x = x.add(BigInteger.ONE);
	    while(!miller_rabin(x,5))
	            x = x.add(big_two);
	    return x;
	}
	/**
	 * Use Miller-Rabin to test if n is a strong probable prime
	 * @param n - number to be tested from primality
	 * @param k - How many base a to be tested
	 * @return
	 *                  true means n has 1-4^(-k) probability to be prime
	 *                 false means n is composite
	 */
	static boolean miller_rabin(BigInteger n, int k)
	{
	    BigInteger a;
	    for (;k>0;k--) {
            //pick a random integer a in the range [1,N-1]
	        do{
	            a = new BigInteger(n.bitLength(), rnd);
	        }while(a.equals(BigInteger.ZERO)||a.compareTo(n)>=0);
	        if (!miller_rabin_test(a, n))
	            return false;
	    }
		return true;        
	}
	//Test if n is a strong probable prime to base a
	static boolean miller_rabin_test(BigInteger a, BigInteger n) {
	    BigInteger n_minus_one = n.subtract(BigInteger.ONE);
	    BigInteger d = n_minus_one;
	        int s = d.getLowestSetBit();
	        d = d.shiftRight(s);
	        BigInteger x = sqm(a,d,n);
	        if(x.equals(BigInteger.ONE)||x.equals(n_minus_one))
	            return true;
	        for(int i=0;i<s-1;i++)
	        {
	            x = x.multiply(x).mod(n);        
	            if(x.equals(BigInteger.ONE)) 
	                    return false;
	            if(x.equals(n_minus_one))
	                    return true;
	        }
	        return false;
	}
    //Square-and-Multiply algorithm to calculate (x^e) mod m
	static BigInteger sqm(BigInteger x, BigInteger e, BigInteger m){
	    BigInteger r = new BigInteger("1");
	    while(e.compareTo(BigInteger.ZERO)>0)
	    {
	        if(e.mod(big_two).equals(BigInteger.ONE))//if e is odd
	                r = r.multiply(x).mod(m);//(r*x) mod m
	        e = e.divide(big_two);
	        x = x.multiply(x).mod(m);
	    }        
	    return r;
	}
}

新址:http://www.limisky.com/106.html