首頁 >php教程 >PHP开发 >Golang加密解密之RSA(附php)

Golang加密解密之RSA(附php)

高洛峰
高洛峰原創
2016-12-30 09:08:021883瀏覽

RSA加密演算法簡史

  RSA是1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一起提出的。當時他們三人都在麻省理工學院工作。 RSA就是他們三人姓氏開頭字母拼在一起組成的。

RSA加密演算法原理

  學過演算法的朋友都知道,電腦中的演算法其實就是數學運算。所以,在先講解RSA加密演算法之前,有必要先了解一些必備的數學知識。我們就從數學知識開始講解。

必備數學知識

  RSA加密演算法中,只用到素數、互質數、指數運算、模運算等幾個簡單的數學知識。所以,我們也需要了解這幾個概念就好。

素數

  素數又稱質數,指在一個大於1的自然數中,除了1和此整數自身外,不能被其他自然數整除的數。這個概念,我們在上國中,甚至小學的時候都學過了,這裡就不再太多解釋了。

互質數

  百度百科上的解釋是:公因數只有1的兩個數,叫做互質數。 ;維基百科上的解釋是:互質,又稱互素。若N個整數的最大公因子是1,則稱這N個整數互質。

  常見的互質數判斷方法主要有以下幾種:

               1、兩個不同的質數一定是互質數。例如,2與7、13與19。

               2、一個質數,且另一個不為它的倍數,而兩個數為互質數。例如,3與10、5與 26。

               以兩個自然數為相鄰的自然數。如 15與 16。

               4、且鄰近的兩個奇數為互質數。如 49與 51。

               5且有兩個數字數量為質數的兩個數字。如97與88。

               6、小數是質數,且大數不是小數的倍數的兩個數是互質數。例如 7和 16。

               7、2、任何奇數是互質數。例如2和87。

               8、1不是質數也不是合成數,且它與任何自然數在一起都是互質數。如1和9908。

               9、輾轉相除法。

指數運算

  指數運算又稱乘方計算,計算結果稱為冪。 nm指將n自乘m次。把nm看作乘方的結果,叫做」n的m次方」或」n的m次方」。其中,n稱為“底數”,m稱為“指數”。

模運算

  模運算即求餘運算。 「模」是「Mod」的音譯。和模運算緊密相關的一個概念是「同餘」。數學上,當兩個整數除以同一個正整數,若得相同餘數,則二整數同餘。

  兩個整數a,b,若它們除以正整數m所得的餘數相等,則稱a,b對於模m同餘,記作: a ≡ b (mod m);讀作:a同餘於b模m,或者,a與b關於模m同餘。例如:26 ≡ 14 (mod 12)。

RSA加密演算法

公鑰與金鑰的產生

  假設Alice想要透過一個不可靠的媒體接收Bob的一條私人訊息。她可以用以下的方式來產生一個公鑰和一個私鑰:

               1、隨意選擇兩個大的質數p和q,而p不等於q,計算N=pq。

              的模反元素,命名為d。 (模反元素存在,當且僅當e與r互質)

               4、銷毀 p 與 q 的記錄。
         (N,e)是公鑰,(N,d)是私鑰。 Alice將她的公鑰(N,e)傳給Bob,而將她的私鑰(N,d)藏起來。

加密訊息

  假設Bob想送一個訊息m,他知道Alice產生的N和e。他使用起先與Alice約好的格式將m轉換為一個小於N的整數n,例如他可以將每一個字轉換為這個字的Unicode碼,然後將這些數字連在一起組成一個數字。如果他的訊息非常長的話,他可以將這個訊息分成幾段,然後將每一段轉換為n。用下面這個公式他可以將n加密為c:

  ne ≡ c (mod N)

計算c並不複雜。 Bob算出c後就可以將它傳給Alice。

解密訊息

Alice得到Bob的訊息c後就可以利用她的金鑰d來解碼。她可以用以下這個公式來將c轉換為n:

  cd ≡ n (mod N)

得到n後,她可以將原來的訊息m重新復原。

解碼的原理是:

  cd ≡ n e·d(mod N)

以及ed ≡ 1 (mod p-1)和ed ≡ 1 (mod q-1)。由費馬小定理可證明(因為p和q是質數)

  n e·d ≡ n (mod p)   和n e·d ≡ n (mod q)

這說明(因為p和q是不同的質數,q所以p和q互質)

  n e·d ≡ n (mod pq)

簽名訊息

  RSA也可以用來為一個訊息署名。假如甲想傳遞一個署名的訊息給乙的話,那麼她可以為她的訊息計算一個雜湊值(Message digest),然後用她的金鑰(private key)加密這個雜湊值並將這個“署名”加在訊息的後面。這個訊息只有用她的公鑰才能被解密。乙獲得這個訊息後可以用甲的公鑰解密這個雜湊值,然後將這個資料與他自己為這個訊息計算的雜湊值比較。假如兩者相符的話,那麼他就可以知道發信人持有甲的密鑰,以及這個消息在傳播路徑上沒有被篡改過。

Golang加密解密之RSA

在PHP中,很多功能經常是一個函數解決;而Go中的卻不是。本文會透過PHP加密,Go解密;Go加密,PHP解密來學習Go的RSA相關的API。

該文討論Go RSA加密解密。所有操作在linux下完成。

一、概要

這是一個非對稱加密演算法,一般透過公鑰加密,私鑰解密。

在加解密過程中,使用openssl生產金鑰。執行以下操作:

1)建立私鑰:

openssl genrsa -out private.pem 1024 //密钥长度,1024觉得不够安全的话可以用2048,但是代价也相应增大

   

2)建立公鑰:

openssl rsa -in private.pem -pubout -out public.pem
这样便生产了密钥。

   

一般地,個別語言也會提供API,用於產生API,用於產生API,用於產生API,用於產生API,用於產生API,用於產生API,用於產生API,用於產生API,用於產生API,用於產生API,用於產生API,用於產生API,用於產生API,用於產生API,用於產生API。在Go中,可以查看encoding/pem套件和crypto/x509套件。

加密解密這塊,牽涉到很多標準,個人建議需要的時候臨時學習一下。

二、Go RSA加密解密

1、rsa加解密,必然會去查crypto/ras這個包

Package rsa implements RSA encryption as specified in PKCS#1.

   

這是該包的說明:實現RSA加密技術,基於PKCS#1規範。

對於什麼是PKCS#1,可以查閱相關資料。 PKCS(公鑰密碼標準),而#1就是RSA的標準。可以查看:PKCS系列簡介

從該套件中函數的名稱,可以看到有兩對加解密的函數。

EncryptOAEP和DecryptOAEP
EncryptPKCS1v15和DecryptPKCS1v15

   

這稱為加密方案,詳細可以查看,PKCS #1 v2.1 RSA 演算法標準

可見,當與其他語言互動時,需要確定好使用哪種方案。

PublicKey和PrivateKey兩個類型分別代表公鑰和私鑰,關於這兩個類型中成員該怎麼設置,這涉及到RSA加密演算法,本文中,這兩個類型的實例透過解析文章開頭產生的密鑰得到。

2、解析金鑰得到PublicKey和PrivateKey的實例

這個過程,我也是花了好些時間(主要對各種加密的各種東東不熟):怎麼將openssl產生的金鑰檔案解析到公鑰和私鑰實例呢?

在encoding/pem套件中,看到了—–BEGIN Type—–這樣的字樣,這正好和openssl產生的金鑰形式差不多,那就試試看。

在該套件中,一個block代表的是PEM編碼的結構,關於PEM,請查閱相關資料。我們要解析密鑰,當然用Decode方法:

func Decode(data []byte) (p *Block, rest []byte)

   

這樣便得到了一個Block的實例(指標)。

解析來看crypto/x509。為什麼是x509呢?這又牽涉到一堆概念。先不管這些,我也是看encoding和crypto這兩個包的子包摸索出來的。

在x509包中,有一個函數:

func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error)

   

從該函數的說明:ParsePKIXPublicKey parses a DER encoded public key. These values are PUpically found in PjEG BL 」。可見這就是解析PublicKey的。另外,這裡說到了PEM,可以上面的encoding/pem對了。

而解析私鑰的,有好幾個方法,從上面的介紹,我們知道,RSA是PKCS#1,剛好有一個方法:

func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error)

  

返回的就是rsa.PrivateKey。

3、解密解密實作

透過上面的介紹,Go中RSA的解密解密實作就不難了。代碼如下:

// 加密

func RsaEncrypt(origData []byte) ([]byte, error) {
  block, _ := pem.Decode(publicKey)
  if block == nil {
    return nil, errors.New("public key error")
  }
  pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
  if err != nil {
    return nil, err
  }
  pub := pubInterface.(*rsa.PublicKey)
  return rsa.EncryptPKCS1v15(rand.Reader, pub, origData)
}

   

// 解密

func RsaDecrypt(ciphertext []byte) ([]byte, error) {
  block, _ := pem.Decode(privateKey)
  if block == nil {
    return nil, errors.New("private key error!")
  }
  priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
  if err != nil {
    return nil, err
  }
  return rsa.DecryptPKCS1v15(rand.Reader, priv, ciphertext)
}

   

其中,publicKey和privateKey是openssl生成的密钥,我生成的如下:

// 公钥和私钥可以从文件中读取

var privateKey = []byte(`
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDZsfv1qscqYdy4vY+P4e3cAtmvppXQcRvrF1cB4drkv0haU24Y
7m5qYtT52Kr539RdbKKdLAM6s20lWy7+5C0DgacdwYWd/7PeCELyEipZJL07Vro7
Ate8Bfjya+wltGK9+XNUIHiumUKULW4KDx21+1NLAUeJ6PeW+DAkmJWF6QIDAQAB
AoGBAJlNxenTQj6OfCl9FMR2jlMJjtMrtQT9InQEE7m3m7bLHeC+MCJOhmNVBjaM
ZpthDORdxIZ6oCuOf6Z2+Dl35lntGFh5J7S34UP2BWzF1IyyQfySCNexGNHKT1G1
XKQtHmtc2gWWthEg+S6ciIyw2IGrrP2Rke81vYHExPrexf0hAkEA9Izb0MiYsMCB
/jemLJB0Lb3Y/B8xjGjQFFBQT7bmwBVjvZWZVpnMnXi9sWGdgUpxsCuAIROXjZ40
IRZ2C9EouwJBAOPjPvV8Sgw4vaseOqlJvSq/C/pIFx6RVznDGlc8bRg7SgTPpjHG
4G+M3mVgpCX1a/EU1mB+fhiJ2LAZ/pTtY6sCQGaW9NwIWu3DRIVGCSMm0mYh/3X9
DAcwLSJoctiODQ1Fq9rreDE5QfpJnaJdJfsIJNtX1F+L3YceeBXtW0Ynz2MCQBI8
9KP274Is5FkWkUFNKnuKUK4WKOuEXEO+LpR+vIhs7k6WQ8nGDd4/mujoJBr5mkrw
DPwqA3N5TMNDQVGv8gMCQQCaKGJgWYgvo3/milFfImbp+m7/Y3vCptarldXrYQWO
AQjxwc71ZGBFDITYvdgJM1MTqc8xQek1FXn1vfpy2c6O
-----END RSA PRIVATE KEY-----
`)
  
var publicKey = []byte(`
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZsfv1qscqYdy4vY+P4e3cAtmv
ppXQcRvrF1cB4drkv0haU24Y7m5qYtT52Kr539RdbKKdLAM6s20lWy7+5C0Dgacd
wYWd/7PeCELyEipZJL07Vro7Ate8Bfjya+wltGK9+XNUIHiumUKULW4KDx21+1NL
AUeJ6PeW+DAkmJWF6QIDAQAB
-----END PUBLIC KEY-----
`)

   

4、使用例子

package main
  
import (
  "fmt"
)
 
func main() {
  data, err := RsaEncrypt([]byte("git@github.com/mrkt"))
  if err != nil {
    panic(err)
  }
  origData, err := RsaDecrypt(data)
  if err != nil {
    panic(err)
  }
  fmt.Println(string(origData))
}

  

该例子是加密完git@github.com/mrkt后立马解密

三、跨语言加解密

语言内部正常,还得看看和其他语言是否一致,即:其他语言加密,Go语言得正确解密;Go语言加密,其他语言正确解密

1、PHP RSA加解密

这里,我选择PHP,使用的是openssl扩展。PHP中加解密很简单,如下两个方法(这里只考虑用公钥加密,私钥解密):

bool openssl_public_encrypt ( string $data , string &$crypted , mixed
$key [, int $padding = OPENSSL_PKCS1_PADDING ] ) bool
openssl_private_decrypt ( string $data , string &$decrypted , mixed
$key [, int $padding = OPENSSL_PKCS1_PADDING ] )

   

最后一个参数是加密方案(补齐方式)。由于Go中使用的是PKCS1而不是OAEP,所以,使用默认值即可。

PHP代码如下:

$privateKey = '-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDZsfv1qscqYdy4vY+P4e3cAtmvppXQcRvrF1cB4drkv0haU24Y
7m5qYtT52Kr539RdbKKdLAM6s20lWy7+5C0DgacdwYWd/7PeCELyEipZJL07Vro7
Ate8Bfjya+wltGK9+XNUIHiumUKULW4KDx21+1NLAUeJ6PeW+DAkmJWF6QIDAQAB
AoGBAJlNxenTQj6OfCl9FMR2jlMJjtMrtQT9InQEE7m3m7bLHeC+MCJOhmNVBjaM
ZpthDORdxIZ6oCuOf6Z2+Dl35lntGFh5J7S34UP2BWzF1IyyQfySCNexGNHKT1G1
XKQtHmtc2gWWthEg+S6ciIyw2IGrrP2Rke81vYHExPrexf0hAkEA9Izb0MiYsMCB
/jemLJB0Lb3Y/B8xjGjQFFBQT7bmwBVjvZWZVpnMnXi9sWGdgUpxsCuAIROXjZ40
IRZ2C9EouwJBAOPjPvV8Sgw4vaseOqlJvSq/C/pIFx6RVznDGlc8bRg7SgTPpjHG
4G+M3mVgpCX1a/EU1mB+fhiJ2LAZ/pTtY6sCQGaW9NwIWu3DRIVGCSMm0mYh/3X9
DAcwLSJoctiODQ1Fq9rreDE5QfpJnaJdJfsIJNtX1F+L3YceeBXtW0Ynz2MCQBI8
9KP274Is5FkWkUFNKnuKUK4WKOuEXEO+LpR+vIhs7k6WQ8nGDd4/mujoJBr5mkrw
DPwqA3N5TMNDQVGv8gMCQQCaKGJgWYgvo3/milFfImbp+m7/Y3vCptarldXrYQWO
AQjxwc71ZGBFDITYvdgJM1MTqc8xQek1FXn1vfpy2c6O
-----END RSA PRIVATE KEY-----'; $publicKey = '-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZsfv1qscqYdy4vY+P4e3cAtmv
ppXQcRvrF1cB4drkv0haU24Y7m5qYtT52Kr539RdbKKdLAM6s20lWy7+5C0Dgacd
wYWd/7PeCELyEipZJL07Vro7Ate8Bfjya+wltGK9+XNUIHiumUKULW4KDx21+1NL
AUeJ6PeW+DAkmJWF6QIDAQAB
-----END PUBLIC KEY-----';
function rsaEncrypt($data)
{
  global $publicKey;
  openssl_public_encrypt($data, $crypted, $publicKey);
  return $crypted;
 
}
function rsaDecrypt($data)
{
  global $privateKey;
  openssl_private_decrypt($data, $decrypted, $privateKey);
  return $decrypted;
}
 
function main()
{
 
  $crypted = rsaEncrypt("git@github.com/mrk");
  $decrypted = rsaDecrypt($crypted);
  echo "encrypt and decrypt:" . $decrypted;
 
}

   


main();

这里也是用PHP加解密git@github.com/mrkt

2、Go和PHP一起工作

这里要注意的一点是,由于加密后是字节流,直接输出查看会乱码,因此,为了便于语言直接加解密,这里将加密之后的数据进行base64编码。

3、使用

示例中,php和Go版本都支持-d参数传入加密好的字符串,将其解密;不传时,会输出加密好并base64编码的串,可用于其他语言解密。

总结

以上就是用Go语言实现了RSA的加密解密的全部内容,文章很深入的讲解了RSA的加密解密过程,对学习相关知识的朋友很有帮助。如果有疑问欢迎留言讨论。

更多Golang加密解密之RSA(附带php)相关文章请关注PHP中文网!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn