RSA算法原理與加密解密 求私鑰等價求求模反元素 等價於分解出2個質數 (r*X+1)%[(p-1)(q-1)]=0

Rsapaper.pdf http://people.csail.mit.edu/rivest/Rsapaper.pdfjavascript

【概述Abstractphp

一、將字符串按照雙方約定的規則轉化爲小於n的正整數m,可能分爲多段,這不是關鍵;html

二、加密過程同解密過程,都是取明/密文的public/private次方,而後對公共的n取餘數;java

三、整數轉化爲字符串git

算法

A message is encrypted by representing it as a number M, raising M to a publicly specified power e, and then taking the remainder when the result is divided by the publicly specified product, n, of two large secret prime numbers p and q. Decryption is similar; only a different, secret, power d is used, where e · d ≡ 1 (mod (p − 1) · (q − 1)). The security of the system rests in part on the difficulty of factoring the published divisor, n.express

 

【破解密鑰思路】apache

When the user reveals E he reveals a very inefficient method of computing D(C): testing all possible messages M until one such that E(M) = C is found. If property (c) is satisfied the number of such messages to test will be so large that this approach is impractical.app

 

【單向門  郵局信箱  A function E satisfying (a)-(c) is a 「trap-door one-way function;」 if it also satisfies (d) it is a 「trap-door one-way permutation.」】less

 These functions are called 「one-way」 because they are easy to compute in one direction but (apparently) very difficult to compute in the other direction.

V Our Encryption and Decryption Methods

 【維基百科和論文結合起來看】

注意取e d 順序,傾向維基百科觀點

Note: The authors of the original RSA paper carry out the key generation by choosing d and then computing e as the modular multiplicative inverse of d modulo φ(n). Since it is beneficial to use a small value for e (e.g., 65,537) in order to speed up the encryption function, current implementations of RSA, such as those following PKCS#1, choose e and compute d instead.[2][18]

RSA (cryptosystem) - Wikipedia https://en.wikipedia.org/wiki/RSA_(cryptosystem)

 【

歐拉函數定義

注意不是整除,是存在只有爲1的公約數

Euler's totient function - Wikipedia https://en.wikipedia.org/wiki/Euler%27s_totient_function

In number theoryEuler's totient function counts the positive integers up to a given integer n that are relatively prime to n. It is written using the Greek letter phi as φ(n) or ϕ(n), and may also be called Euler's phi function. It can be defined more formally as the number of integers k in the range 1 ≤ k ≤ n for which the greatest common divisor gcd(nk) is equal to 1.[2][3] The integers k of this form are sometimes referred to as totatives of n.

大於等於1但小於等於(此處包含與否不影響結果)n的正整數中與n的最大公約數爲1的正整數的個數

注意,結果中包含1;

【1既不是質數(素數)可是1與全部正整數互質】

【最大公約數   Greatest common divisor  Greatest common divisor - Wikipedia https://en.wikipedia.org/wiki/Greatest_common_divisor 】

 

【小於等於p^k的數中,與p^k有非1公約數的正整數的公約數爲且僅爲p的若干次方,從0到k次方,最小的數爲p,下一個爲p*2,下一個爲p*三、、、至p*p^(k-1),即p*i,i=1,2,3...,p^(k-1),故

總數p^k-扣除的個數p^(k-1)】

 

若p1和p2互質,n=p1*p2,則ψ(n)= ψ(p1*p2)= ψ(p1) ψ(p2)

【gcd(p1,j1i)=1 gcd(p2,j2i)=1  gcd(p1,p2)=1 

gcd(p1,j1i*j2i)=1 gcd(p2,j1i*j2i)=1

與p1互質的數與p2也互質,反之亦然

a b 均與p互質,則a*b也與p互質

故與p1*p2互質的數 爲 (1,j1i)* (1,j2i) 

個數爲個數的積】

 

 任意一個大於1的正整數均可以寫成一系列質數的積

【算術基本定理 區別於 代數基本定理 見維基百科英文版】

 

由於

         n = (p1^k1)* (p2^k2)*……(pr^kr)   (p1~pr都是質數)

因此

         ψ(n)= ψ((p1^k1)) ψ(p2^k2) ……ψ(pr^kr)   定理4

         ψ(n)= (p1^k1)*(1-1/p1) * (p2^k2)(1-1/p2)……(pr^kr)*(1-1/pr)   定理3

         ψ(n)= (p1^k1)* (p2^k2)*……(pr^kr) * (1-1/p1) (1-1/p2)…… (1-1/pr)

         ψ(n)=n (1-1/p1) (1-1/p2)…… (1-1/pr)  

    【<n 且爲正整數  

=[p1^(k1-1)*(p1-1)]*[p2^(k2-1)*(p2-1)]*...*[pr^(kr-1)*(pr-1)]

待證實

(三)  歐拉定理:

正整數a與n互質,則下式恆成立

a^ψ(n) ≡1(mod n)

 

Operation

The RSA algorithm involves four steps: key generation, key distribution, encryption and decryption.

A basic principle behind RSA is the observation that it is practical to find three very large positive integers e, d and n such that with modular exponentiation for all integers m (with 0 ≤ m < n):

【m^(ed)%n=m
m^(ed-1)%n=1
m、n互質
根據歐拉定理
ed-1能夠至少能夠取n的歐拉數

and that even knowing e and n or even m it can be extremely difficult to find d.

In addition, for some operations it is convenient that the order of the two exponentiations can be changed and that this relation also implies:

RSA involves a public key and a private key. The public key can be known by everyone, and it is used for encrypting messages. The intention is that messages encrypted with the public key can only be decrypted in a reasonable amount of time by using the private key. The public key is represented by the integers n and e; and, the private key, by the integer d (although n is also used during the decryption process. Thus, it might be considered to be a part of the private key, too). m represents the message (previously prepared with a certain technique explained below).

Key generation

The keys for the RSA algorithm are generated the following way:

  1. Choose two distinct prime numbers p and q.
    • For security purposes, the integers p and q should be chosen at random, and should be similar in magnitude but differ in length by a few digits to make factoring harder.[2] Prime integers can be efficiently found using a primality test.
  2. Compute n = pq.
    • n is used as the modulus for both the public and private keys. Its length, usually expressed in bits, is the key length.
  3. Compute λ(n) = lcm(λ(p), λ(q)) = lcm(p − 1, q − 1), where λ is Carmichael's totient function. This value is kept private.
  4. Choose an integer e such that 1 < e < λ(n) and gcd(eλ(n)) = 1; i.e., e and λ(n) are coprime.
  5. Determine d as d ≡ e−1 (mod λ(n)); i.e., d is the modular multiplicative inverse of e modulo λ(n).
  • This means: solve for d the equation de ≡ 1 (mod λ(n)).
  • e having a short bit-length and small Hamming weight results in more efficient encryption – most commonly e = 216 + 1 = 65,537. However, much smaller values of e (such as 3) have been shown to be less secure in some settings.[14]
  • e is released as the public key exponent.
  • d is kept as the private key exponent.

The public key consists of the modulus n and the public (or encryption) exponent e. The private key consists of the private (or decryption) exponent d, which must be kept secret. pq, and λ(n) must also be kept secret because they can be used to calculate d.

In the original RSA paper,[2] the Euler totient function φ(n) = (p − 1)(q − 1) is used instead of λ(n) for calculating the private exponent d. Since φ(n) is always divisible by λ(n) the algorithm works as well. That the Euler totient function can be used can also be seen as a consequence of the Lagrange's theorem applied to the multiplicative group of integers modulo pq. Thus any d satisfying de ≡ 1 (mod φ(n)) also satisfies de ≡ 1 (mod λ(n)). However, computing d modulo φ(n) will sometimes yield a result that is larger than necessary (i.e. d > λ(n)). Most of the implementations of RSA will accept exponents generated using either method (if they use the private exponent d at all, rather than using the optimized decryption method based on the Chinese remainder theorem described below), but some standards like FIPS 186-4 may require that d < λ(n). Any "oversized" private exponents not meeting that criterion may always be reduced modulo λ(n) to obtain a smaller equivalent exponent.

Since any common factors of (p − 1) and (q − 1) are present in the factorisation of n − 1 = pq − 1 = (p − 1)(q − 1) + (p − 1) + (q − 1),[15] it is recommended that (p − 1) and (q − 1) have only very small common factors, if any besides the necessary 2.[2][16][17]

Note: The authors of the original RSA paper carry out the key generation by choosing d and then computing e as the modular multiplicative inverse of d modulo φ(n). Since it is beneficial to use a small value for e (e.g., 65,537) in order to speed up the encryption function, current implementations of RSA, such as those following PKCS#1, choose e and compute d instead.[2][18]

Key distribution

Suppose that Bob wants to send information to Alice. If they decide to use RSA, Bob must know Alice's public key to encrypt the message and Alice must use her private key to decrypt the message. To enable Bob to send his encrypted messages, Alice transmits her public key (ne) to Bob via a reliable, but not necessarily secret, route. Alice's private key (d) is never distributed.

Encryption

After Bob obtains Alice's public key, he can send a message M to Alice.

【將明文轉化爲一個整數,可能對明文作截斷處理,生成多個正整數,分段加密 string msg--->int m

by using an agreed-upon reversible protocol known as a padding scheme  經過雙方約定好的字符串轉整數算法

To do it, he first turns M (strictly speaking, the un-padded plaintext) into an integer m (strictly speaking, the padded plaintext), such that 0 ≤ m < n by using an agreed-upon reversible protocol known as a padding scheme. He then computes the ciphertext c, using Alice's public key e, corresponding to

This can be done reasonably quickly, even for 500-bit numbers, using modular exponentiation. Bob then transmits c to Alice.

Decryption

Alice can recover m from c by using her private key exponent d by computing

Given m, she can recover the original message M by reversing the padding scheme.

 

 

 

 

【由於求私鑰,即求eKEY+1能被(p-1)(q-1)的整除的KEY,而e、p*q已被公開,故求私鑰等價於分解p*q】

【e 與 n的歐拉數互質,必定存在 e對於n的歐拉數的模範元素,此處不要求e次小於n的歐拉數,

e 與 (p-1)(q-1)互質,必定存在 e對於 (p-1)(q-1)的模反元素,此處不要求e次小於 (p-1)(q-1),

記這個模反元素爲d

即ed-1能夠被(p-1)(q-1)整除,記商爲ratio

則d=[(p-1)(q-1)*ratio+1]/e, 根據模反元素定義,ratio必定存在

 

(e*KEY-1)%[(p-1)(q-1)]=0

公鑰(p-1)(q-1),e

私鑰(p-1)(q-1),d

 

(r*X+1)%[(p-1)(q-1)]=0

 

【取2個不一樣的質數 p ,q(若是相同呢?),取小於p*q的質數r

根據歐拉定理

存在質數d使得e*d-1(或者d爲r的歐拉數減小1次方)能夠被p*q的歐拉數整除

存在質數d使得e*d-1能夠被(p-1)(q-1)整除

 

【歐拉定理

Euler's theorem - Wikipedia https://en.wikipedia.org/wiki/Euler%27s_theorem

In number theoryEuler's theorem (also known as the Fermat–Euler theorem or Euler's totient theorem) states that if n and a are coprime positive integers, then

 

 

設正整數k, e*d = kψ(n)+1;

 

則ed-kψ(n)=1

  d = (kψ(n)+1) / e;

對於不定方程ax+by=c,設gcd(a,b)=d,若是ax+by=c有解,則d|c----->也就是說若是ed-kψ(n)=1 有解,則gcd(d,-k)可以整除1,而1顯然能夠被任何整數整除,因此該二元一次方程一定有解(d,k)

 

 (歐幾里得定理和擴展歐幾里得定理計算二元一次方程)

2)        將n和d封裝成私鑰

 

 

5、RSA算法可靠性論證

從上文能夠統計出整個算法涉及到的量有6個,其中三個爲由私鑰持有者生成,三個是私鑰持有者推導出來的

生成量:p,q,e

推導量:n, ψ(n),d

 

密鑰中只有公鑰被髮布,全部人均可以獲取。而公鑰由n和e封裝起來,所以,若是要破解一份RSA加密過的密文,咱們必須使用私鑰(私鑰由n和d封裝而成)

n能夠從公鑰獲取。

 

(假設mc爲明文,c爲密文,則公鑰由n和e封裝則意味着求取密文的運算中,n,e和mc是已知數,只有c是未知數;私鑰由n和d封裝,同上,解密密文的運算中,n,d和c是已知的,只有mc是未知數。)

 

所以,破解私鑰的關鍵就是破解e對於ψ(n)的模反元素d。

         其數學關係是:  e*d=1(modψ(n));

所以需須要先求出ψ(n),而求出ψ(n)須要知道ψ(p)和ψ(q)(由於ψ(n)= ψ(p* ψ(q))

 

而p和q只能經過分解n的質因數得到。因此,整個RSA算法都基於n這個大數不能分解質因數這個基礎上。

        

所以,只要n夠大,私鑰就不會被破解

 

 

6、加解密過程:假設明文是m,c是密文

(一)  加密:使用公鑰(n,e)

先將其換算成asc碼或者unicode等其餘數值。且m必須小於n

則加密算法是

         m^e=c(mod n)

推出

         m^e / n = k ……c這裏c就是密文,k咱們不關心

(二)  解密:使用私鑰(n,d)

1.        簡單的說解密就是經過下式求m。(必定能夠求解出m)

c^d = m(mod n)

推出
c^d / n = k … … m    m就是明文編碼,不關心k

 

查表得出明文

 

JAVA的非對稱加密算法RSA——加密和解密 - 牽着妞去散步 - 博客園 https://www.cnblogs.com/OnlyCT/p/6586856.html

 

 

下面簡單總結加密和解密的完整過程。

 

l  簽名過程:

1.        A提取消息m的消息摘要h(m),並使用本身的私鑰對摘要h(m)進行加密,生成簽名s

2.        A將簽名s和消息m一塊兒,使用B的公鑰進行加密,生成密文c,發送給B。

l  驗證過程:

1.        B接收到密文c,使用本身的私鑰解密c獲得明文m和數字簽名s

2.        B使用A的公鑰解密數字簽名s解密獲得H(m).

3.        B使用相同的方法提取消息m的消息摘要h(m)

4.        B比較兩個消息摘要。相同則驗證成功;不一樣則驗證失敗。

複製代碼
package com.joe.main;  
  
import java.io.*;  
import java.math.BigInteger;  
import java.util.ArrayList;  
  
/**
 * @Description Demo說明: 
 *              一、按照加密解密和簽名驗籤的邏輯,編寫簡單的demo,不涉及java中繼承的RSA相關類和Sigesture簽名類 
 *              二、只能對數字和字母進行加密, 不涉及編碼和解碼問題 。 三、不作數字簽名和驗證了,涉及到提取信息摘要。 
 */  
public class EnAndDe {  
    private long p = 0;  
    private long q = 0;  
    private long n = 0;  
    private long t = 0; // 歐拉函數  
  
    private long e = 0; // 公匙  
    private long d = 0; // 密匙  
  
    private String mc; // 明文  
    private long c = 0; // 密文  
    private long word = 0; // 解密後明文  
  
    // 判斷是一個數 x 否爲素數素數就是判斷在 (2,√x)範圍內有沒有除1外的因數,若是沒有則x數素數  
    public boolean isPrime(long t) {  
        long k = 0;  
        k = (long) Math.sqrt((double) t);  
        for (int i = 2; i <= k; i++) {  
            if ((t % i) == 0) {  
                return false;  
            }  
        }  
        return true;  
    }  
  
    // 隨機產生大素數(1e6數量級,注意,太大了要超出範圍)  
    public void bigprimeRandom() {  
        do {  
            p = (long) (Math.random() * 1000000);  
        } while (!this.isPrime(p));  
        do {  
            q = (long) (Math.random() * 1000000);  
        } while (p == q || !this.isPrime(q));  
    }  
  
    // 輸入PQ  
    public void inputPQ() throws Exception {  
  
        this.bigprimeRandom();  
        System.out.println("自動生成兩個大素數p,q分別爲:" + this.p + " " + this.q);  
  
        this.n = (long) p * q;  
        this.t = (long) (p - 1) * (q - 1);  
  
        System.out.println("這兩個素數的乘積爲p*q:" + this.n);  
        System.out.println("所得的t=(p-1)(q-1):" + this.t);  
    }  
  
    // 求最大公約數  
    public long gcd(long a, long b) {  
        long gcd;  
        if (b == 0)  
            gcd = a;  
        else  
            gcd = gcd(b, a % b);  
        return gcd;  
  
    }  
  
    // 生成公匙  
    public void getPublic_key() throws Exception {  
        do {  
  
            this.e = (long) (Math.random() * 100000);  
            // e知足 e∈(1, ψ(n))且e與ψ(n)最大公約數爲1,即 e與t互質  
        } while ((this.e >= this.t) || (this.gcd(this.t, this.e) != 1));  
        System.out.println("生成的公鑰爲:" + "(" + this.n + "," + this.e + ")");  
    }  
  
    // 生成私鑰 e*d=1(modψ(n))==> d = (kψ(n)+1) / e  
    public void getPrivate_key() {  
        long value = 1; // value 是e和d的乘積  
        outer: for (long k = 1;; k++) {  
            value = k * this.t + 1;  
            if ((value % this.e == 0)) {  
                this.d = value / this.e;  
                break outer;  
            }  
        }  
        System.out.println("產生的一個私鑰爲:" + "(" + this.n + "," + this.d + ")");  
    }  
  
    // 輸入明文  
    public void getText() throws Exception {  
        System.out.println("請輸入明文:");  
        BufferedReader stdin = new BufferedReader(new InputStreamReader(  
                System.in));  
        mc = stdin.readLine();  
  
    }  
  
    // 解密密文  
    public void pascolum() throws Exception {  
        this.getText();  
        System.out.println("輸入明文爲: " + this.mc);  
        // 加密  
        ArrayList cestr = new ArrayList();  
        for (int i = 0; i < mc.length(); i++) {  
            this.c = this.colum((long) mc.charAt(i), this.n, this.e);  
            cestr.add(c);  
        }  
        System.out.println("加密後所得的密文爲:" + cestr);  
        // 解密  
        StringBuffer destr = new StringBuffer();  
        for (int j = 0; j < cestr.size(); j++) {  
            this.word = this.colum(Long.parseLong(cestr.get(j).toString()),  
                    this.n, this.d);  
            destr.append((char) word);  
        }  
        System.out.println("解密後所得的明文爲:" + destr);  
  
    }  
  
    // 加密、解密計算  
    public long colum(long mc, long n, long key) {  
        BigInteger bigy = new BigInteger(String.valueOf(mc));  
        BigInteger bign = new BigInteger(String.valueOf(n));  
        BigInteger bigkey = new BigInteger(String.valueOf(key));  
        return Long.parseLong(bigy.modPow(bigkey, bign).toString());// 備註1  
    }  
  
    public static void main(String[] args) {  
        try {  
            EnAndDe t = new EnAndDe();  
            t.inputPQ();  
            t.getPublic_key();  
            t.getPrivate_key();  
            t.pascolum();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
  
}  
複製代碼

備註1:modPow(a,b)是java類BigInteger中的一個方法,返回結果是:調用該方法的對象的a次冪,模b的結果

What a meaningless sense if losing myself,though owning all of the world.
 
 

自動生成兩個大素數p,q分別爲:192817 637309
這兩個素數的乘積爲p*q:122884009453
所得的t=(p-1)(q-1):122883179328
生成的公鑰爲:(122884009453,56699)
產生的一個私鑰爲:(122884009453,84333590963)
請輸入明文:
343i5435dsfs
輸入明文爲: 343i5435dsfs
加密後所得的密文爲:[104962964505, 53575721421, 104962964505, 73241667689, 115165799367, 53575721421, 104962964505, 115165799367, 58398500538, 71277672989, 121734470069, 71277672989]
解密後所得的明文爲:343i5435dsfs

 

 

方要咱們自行生成一對公私鑰後將其中的公/私鑰指數和模數發給他們

 php實現rsa簽名和驗籤 - 雲+社區 - 騰訊雲 https://cloud.tencent.com/developer/article/1153658

這兩天在弄某支付接口相關的東西,之前沒作過這塊,在簽名和驗籤的過程當中遇到了一些問題,記下來.

首先生成一個1024位的私鑰:

openssl genrsa -out private.pem 1024

而後根據私鑰導出公鑰

openssl rsa -in private.pem -pubout -out public.pem

php的openssl擴展裏已經封裝好了簽名和驗籤的方法,分別是openssl_sign和openssl_verify.

function sign($data){ $p = openssl_pkey_get_private(file_get_contents('private.pem')); openssl_sign($data, $signature, $p); openssl_free_key($p); return bin2hex($signature); } function verify($data, $sign){ $p = openssl_pkey_get_public(file_get_contents('public.pem')); $verify = openssl_verify($data, hex2bin($sign), $p); openssl_free_key($p); return $verify > 0; }

實際狀況是測試接口並無提供公私鑰,而是提供了公/私鑰指數,模數.經過java的RSAPrivateKeySpec和RSAPublicKeySpec來實現簽名和驗籤,遂寫了一個jar用命令行來調用:

package org; import java.math.BigInteger; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.Signature; import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; /** * @author eslizn * */ public class SignVerify { /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { if(args.length == 4 && args[0].equals("sign")){ System.out.println(Sign(args[1], new BigInteger(args[2], 16), new BigInteger(args[3], 16))); System.exit(0); } if(args.length == 5 && args[0].equals("verify")){ System.out.println(Verify(args[1], args[2], new BigInteger(args[3], 16), new BigInteger(args[4], 16)) ? "1" : "0"); System.exit(0); } } /** * Sign * * @param data * @param mod * @param exp * @return * @throws Exception */ public static String Sign(String data, BigInteger mod, BigInteger exp) throws Exception{ RSAPrivateKeySpec spec = new RSAPrivateKeySpec(mod, exp); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey signKey = keyFactory.generatePrivate(spec); Signature signature = Signature.getInstance("SHA1withRSA"); signature.initSign(signKey); signature.update(data.getBytes()); return byteArray2HexString(signature.sign()); } /** * Verify * * @param data * @param sign * @param mod * @param exp * @return * @throws Exception */ public static boolean Verify(String data, String sign, BigInteger mod, BigInteger exp) throws Exception{ RSAPublicKeySpec spec = new RSAPublicKeySpec(mod, exp); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); java.security.PublicKey verifyKey = keyFactory.generatePublic(spec); Signature verifier = Signature.getInstance("SHA1withRSA"); verifier.initVerify(verifyKey); verifier.update(data.getBytes()); return verifier.verify(hexString2ByteArray(sign)); } public static byte[] hexString2ByteArray(String hexStr){ if (hexStr == null) return null; if (hexStr.length() % 2 != 0) return null; byte data[] = new byte[hexStr.length() / 2]; for (int i = 0; i < hexStr.length() / 2; i++){ char hc = hexStr.charAt(2 * i); char lc = hexStr.charAt(2 * i + 1); byte hb = hexChar2Byte(hc); byte lb = hexChar2Byte(lc); if (hb < 0 || lb < 0) return null; int n = hb << 4; data[i] = (byte)(n + lb); } return data; } public static byte hexChar2Byte(char c){ if (c >= '0' && c <= '9') return (byte)(c - 48); if (c >= 'a' && c <= 'f') return (byte)((c - 97) + 10); if (c >= 'A' && c <= 'F') return (byte)((c - 65) + 10); else return -1; } public static String byteArray2HexString(byte arr[]){ StringBuilder sbd = new StringBuilder(); byte arr$[] = arr; int len$ = arr$.length; for (int i$ = 0; i$ < len$; i$++){ byte b = arr$[i$]; String tmp = Integer.toHexString(0xff & b); if (tmp.length() < 2) tmp = (new StringBuilder()).append("0").append(tmp).toString(); sbd.append(tmp); } return sbd.toString(); } }

巧合的是寫完後經過溝通,對方要咱們自行生成一對公私鑰後將其中的公/私鑰指數和模數發給他們.有的時候可能有的辦法比技術實現更方便.附上取公/私鑰指數和模數的代碼:

function getPrivate($file){ $p = openssl_pkey_get_private(file_get_contents($file)); $res = openssl_pkey_get_details($p); var_dump($res); openssl_free_key($p); return array( 'n' => bin2hex($res['rsa']['n']),#模數 'e' => bin2hex($res['rsa']['e']),#公鑰指數 'd' => bin2hex($res['rsa']['d']),#私鑰指數 ); }



非對稱加密算法-RSA算法 - The Last Song 的專欄 - CSDN博客 https://blog.csdn.net/kongqz/article/details/6302980


package com.neo.RSA;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;


/**
 * 非對稱加密算法RSA算法組件
 * 非對稱算法通常是用來傳送對稱加密算法的密鑰來使用的,相對於DH算法,RSA算法只須要一方構造密鑰,不須要
 * 大費周章的構造各自本地的密鑰對了。DH算法只能算法非對稱算法的底層實現。而RSA算法算法實現起來較爲簡單
 *
 * @author kongqz
 */
public class JavaRsa {
    //非對稱密鑰算法
    public static final String KEY_ALGORITHM = "RSA";
    /**
     * 密鑰長度,DH算法的默認密鑰長度是1024
     * 密鑰長度必須是64的倍數,在512到65536位之間
     */
    private static final int KEY_SIZE = 512;
    //公鑰
    private static final String PUBLIC_KEY = "RSAPublicKey";
    //私鑰
    private static final String PRIVATE_KEY = "RSAPrivateKey";

    /**
     * 初始化密鑰對
     *
     * @return Map 甲方密鑰的Map
     */
    public static Map<String, Object> initKey() throws Exception {
        //實例化密鑰生成器
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        //初始化密鑰生成器
        keyPairGenerator.initialize(KEY_SIZE);
        //生成密鑰對
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        //甲方公鑰
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        //甲方私鑰
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        //將密鑰存儲在map中
        Map<String, Object> keyMap = new HashMap<String, Object>();
        System.out.println("keyMap:" + keyMap);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        System.out.println("keyMap:" + keyMap);
        return keyMap;
    }


    /**
     * 私鑰加密
     *
     * @param data 待加密數據
     * @param key  密鑰
     * @return byte[] 加密數據
     */
    public static byte[] encryptByPrivateKey(byte[] data, byte[] key) throws Exception {
        //取得私鑰
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
        System.out.println("pkcs8KeySpec:" + pkcs8KeySpec);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //生成私鑰
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        System.out.println("privateKey:" + privateKey);

        //數據加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        System.out.println("cipher:" + cipher);

        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        System.out.println("cipher.doFinal(data):" + cipher.doFinal(data));
        return cipher.doFinal(data);
    }

    /**
     * 公鑰加密
     *
     * @param data 待加密數據
     * @param key  密鑰
     * @return byte[] 加密數據
     */
    public static byte[] encryptByPublicKey(byte[] data, byte[] key) throws Exception {

        //實例化密鑰工廠
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //初始化公鑰
        //密鑰材料轉換
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
        System.out.println("x509KeySpec:" + x509KeySpec);
        //產生公鑰
        PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
        System.out.println("pubKey:" + pubKey);
        //數據加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        System.out.println("cipher:" + cipher);
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        System.out.println("cipher.init(Cipher.ENCRYPT_MODE, pubKey):" + cipher.doFinal(data));
        return cipher.doFinal(data);
    }

    /**
     * 私鑰解密
     *
     * @param data 待解密數據
     * @param key  密鑰
     * @return byte[] 解密數據
     */
    public static byte[] decryptByPrivateKey(byte[] data, byte[] key) throws Exception {
        //取得私鑰
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //生成私鑰
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        System.out.println("decryptByPrivateKey-privateKey:" + privateKey);

        //數據解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        System.out.println("decryptByPrivateKey-cipher.doFinal(data):" + cipher.doFinal(data));
        return cipher.doFinal(data);
    }

    /**
     * 公鑰解密
     *
     * @param data 待解密數據
     * @param key  密鑰
     * @return byte[] 解密數據
     */
    public static byte[] decryptByPublicKey(byte[] data, byte[] key) throws Exception {

        //實例化密鑰工廠
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //初始化公鑰
        //密鑰材料轉換
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
        //產生公鑰
        PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
        //數據解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, pubKey);
        return cipher.doFinal(data);
    }

    /**
     * 取得私鑰
     *
     * @param keyMap 密鑰map
     * @return byte[] 私鑰
     */
    public static byte[] getPrivateKey(Map<String, Object> keyMap) {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return key.getEncoded();
    }

    /**
     * 取得公鑰
     *
     * @param keyMap 密鑰map
     * @return byte[] 公鑰
     */
    public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return key.getEncoded();
    }

    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        //初始化密鑰
        //生成密鑰對
        Map<String, Object> keyMap = JavaRsa.initKey();
        //公鑰
        byte[] publicKey = JavaRsa.getPublicKey(keyMap);

        //私鑰
        byte[] privateKey = JavaRsa.getPrivateKey(keyMap);
        System.out.println("公鑰:\n"+Base64.encodeBase64String(publicKey));
        System.out.println("私鑰:\n" + Base64.encodeBase64String(privateKey));

        System.out.println("================密鑰對構造完畢,甲方將公鑰公佈給乙方,開始進行加密數據的傳輸=============");
        String str = "RSA密碼交換算法";
        System.out.println("\n===========甲方向乙方發送加密數據==============");
        System.out.println("原文:" + str);
        //甲方進行數據的加密
        byte[] code1 = JavaRsa.encryptByPrivateKey(str.getBytes(), privateKey);
        System.out.println("加密後的數據:" + Base64.encodeBase64String(code1));
        System.out.println("===========乙方使用甲方提供的公鑰對數據進行解密==============");
        //乙方進行數據的解密
        byte[] decode1 = JavaRsa.decryptByPublicKey(code1, publicKey);
        System.out.println("乙方解密後的數據:" + new String(decode1) + "\n\n");

        System.out.println("===========反向進行操做,乙方向甲方發送數據==============\n\n");

        str = "乙方向甲方發送數據RSA算法";

        System.out.println("原文:" + str);

        //乙方使用公鑰對數據進行加密
        byte[] code2 = JavaRsa.encryptByPublicKey(str.getBytes(), publicKey);
        System.out.println("===========乙方使用公鑰對數據進行加密==============");
        System.out.println("加密後的數據:" + Base64.encodeBase64String(code2));

        System.out.println("=============乙方將數據傳送給甲方======================");
        System.out.println("===========甲方使用私鑰對數據進行解密==============");

        //甲方使用私鑰對數據進行解密
        byte[] decode2 = JavaRsa.decryptByPrivateKey(code2, privateKey);

        System.out.println("甲方解密後的數據:" + new String(decode2));
    }
}

  

keyMap:{}
keyMap:{RSAPublicKey=Sun RSA public key, 512 bits
  modulus: 9240044710867494912585831069170888399022790165196118946275326140848554643574034384515939267781952577892601470596983513190437403733818259515255163304444331
  public exponent: 65537, RSAPrivateKey=sun.security.rsa.RSAPrivateCrtKeyImpl@40a9f}
公鑰:
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALBsZdJMV7owsqz9hXcQ5Bzlvu9l8UX1BMBhSuUzxx3Ri9B+6cjbMXa7//hkzvHXbbysi2qUkLmFQR1y/XvegasCAwEAAQ==
私鑰:
MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAsGxl0kxXujCyrP2FdxDkHOW+72XxRfUEwGFK5TPHHdGL0H7pyNsxdrv/+GTO8ddtvKyLapSQuYVBHXL9e96BqwIDAQABAkEAruI1KPsed2XwqIUDeVzYE0E3PU67lhJlLGMFgCEMf6PfLnzc5oPMFsMZpvx6d5OnUSDJdz90dkGpOocfi/CWKQIhAPjmkQ49v8wDassy/OZ52wnMWPIlS3mKRkVde6CkCnEXAiEAtXSdJSDbts43xJ4qQH5ER2PeNNY8ele0GX0b4zyGiI0CIA2pMfrzHqzG3zyp3sGTnJcMFTGtJFhWY4b5G4j0l/IfAiAqLHBZsMlgM43CpQAs/Svha9S5cxGbXcb+JZXVuruKAQIgJpEedrg+u1J/OHsrQCF1vogDUuwJBfXHf+9CryR6Ems=
================密鑰對構造完畢,甲方將公鑰公佈給乙方,開始進行加密數據的傳輸=============

===========甲方向乙方發送加密數據==============
原文:RSA密碼交換算法
pkcs8KeySpec:java.security.spec.PKCS8EncodedKeySpec@42110406
privateKey:sun.security.rsa.RSAPrivateCrtKeyImpl@40a9f
cipher:javax.crypto.Cipher@366e2eef
cipher.doFinal(data):[B@35fb3008
加密後的數據:Ys5HQsCNZEJVyZk6rhIaQ23Za8UYwWk4pHbpsruAD5dBBUygXrPfvylIOXRStuznS5c4jR1qm4RIepR1za5wcQ==
===========乙方使用甲方提供的公鑰對數據進行解密==============
乙方解密後的數據:RSA密碼交換算法


===========反向進行操做,乙方向甲方發送數據==============


原文:乙方向甲方發送數據RSA算法
x509KeySpec:java.security.spec.X509EncodedKeySpec@7225790e
pubKey:Sun RSA public key, 512 bits
  modulus: 9240044710867494912585831069170888399022790165196118946275326140848554643574034384515939267781952577892601470596983513190437403733818259515255163304444331
  public exponent: 65537
cipher:javax.crypto.Cipher@54a097cc
cipher.init(Cipher.ENCRYPT_MODE, pubKey):[B@36f6e879
===========乙方使用公鑰對數據進行加密==============
加密後的數據:O6cqf/XRiRkWSbVfXm5gRnJFOMDnnLZ8zSKTl/A9H+LVtFa7h3+/GLndRTCpTB4unNfToLKkQOWcYUP3S13G4w==
=============乙方將數據傳送給甲方======================
===========甲方使用私鑰對數據進行解密==============
decryptByPrivateKey-privateKey:sun.security.rsa.RSAPrivateCrtKeyImpl@40a9f
decryptByPrivateKey-cipher.doFinal(data):[B@5a61f5df
甲方解密後的數據:乙方向甲方發送數據RSA算法

 注意:

 

keyMap:{RSAPublicKey=Sun RSA public key, 512 bits modulus: 9240044710867494912585831069170888399022790165196118946275326140848554643574034384515939267781952577892601470596983513190437403733818259515255163304444331 public exponent: 65537, RSAPrivateKey=sun.security.rsa.RSAPrivateCrtKeyImpl@40a9f}

相關文章
相關標籤/搜索