如何設(shè)計(jì)一個(gè)安全對(duì)外的接口憔恳?加簽與驗(yàn)簽了解一下

前言

我們?cè)谇舐毭嬖囍腥坷螅?jīng)常會(huì)被問(wèn)到,如何設(shè)計(jì)一個(gè)安全對(duì)外的接口呢? 其實(shí)可以回答這一點(diǎn)钥组,加簽和驗(yàn)簽输硝,這將讓你的接口更加有安全扣典。接下來(lái)捞稿,本文將和大家一起來(lái)學(xué)習(xí)加簽和驗(yàn)簽。從理論到實(shí)戰(zhàn)幌衣,加油哦~

  • 密碼學(xué)相關(guān)概念
  • 加簽驗(yàn)簽概念
  • 為什么需要加簽屿附、驗(yàn)簽
  • 加密算法簡(jiǎn)介
  • 加簽驗(yàn)簽相關(guān)API
  • 加簽驗(yàn)簽代碼實(shí)現(xiàn)
  • 公眾號(hào):撿田螺的小男孩

本文已經(jīng)收錄到個(gè)人github郎逃,文章有用的話,可以給個(gè)star呀:

https://github.com/whx123/JavaHome

密碼學(xué)相關(guān)概念

明文挺份、密文褒翰、密鑰、加密匀泊、解密

  • 明文:指沒(méi)有經(jīng)過(guò)加密的信息/數(shù)據(jù)优训。
  • 密文:明文被加密算法加密之后,會(huì)變成密文各聘,以確保數(shù)據(jù)安全揣非。
  • 密鑰:是一種參數(shù),它是在明文轉(zhuǎn)換為密文或?qū)⒚芪霓D(zhuǎn)換為明文的算法中輸入的參數(shù)躲因。密鑰分為對(duì)稱密鑰與非對(duì)稱密鑰早敬。
  • 加密:將明文變成密文的過(guò)程。
  • 解密:將密文還原為明文的過(guò)程大脉。

對(duì)稱加密搞监、非對(duì)稱加密

  • 對(duì)稱加密:加密和解密使用相同密鑰的加密算法。


    image
  • 非對(duì)稱加密:非對(duì)稱加密算法需要兩個(gè)密鑰(公開(kāi)密鑰和私有密鑰)镰矿。公鑰與私鑰是成對(duì)存在的琐驴,如果用公鑰對(duì)數(shù)據(jù)進(jìn)行加密,只有對(duì)應(yīng)的私鑰才能解密衡怀。

image

什么是公鑰私鑰棍矛?

  • 公鑰與私鑰是成對(duì)存在的密鑰,如果用公鑰對(duì)數(shù)據(jù)進(jìn)行加密抛杨,只有用對(duì)應(yīng)的私鑰才能解密够委。
  • 其實(shí),公鑰就是公開(kāi)的秘鑰怖现,私鑰就是要你私自保存好的秘鑰茁帽。
  • 非對(duì)稱加密算法需要有一對(duì)公私鑰~

假設(shè)你有一個(gè)文件玉罐,你用字母a加密,只有字母b才能解密;或者你用b加密潘拨,只有a才能解密吊输,那么a和b就是一對(duì)公私鑰。如果密鑰a公開(kāi)铁追,密鑰b你就要私自保存好啦季蚂,這時(shí)候密鑰a就是公鑰,密鑰b就是私鑰琅束。相反扭屁,如果b公開(kāi),a就要保存好涩禀,這時(shí)候呢料滥,秘鑰b就是公鑰,秘鑰a就是私鑰艾船。

加簽驗(yàn)簽概念

  • 加簽:用Hash函數(shù)把原始報(bào)文生成報(bào)文摘要葵腹,然后用私鑰對(duì)這個(gè)摘要進(jìn)行加密,就得到這個(gè)報(bào)文對(duì)應(yīng)的數(shù)字簽名屿岂。通常來(lái)說(shuō)呢践宴,請(qǐng)求方會(huì)把數(shù)字簽名和報(bào)文原文一并發(fā)送給接收方。

    image

  • 驗(yàn)簽:接收方拿到原始報(bào)文和數(shù)字簽名后雁社,用同一個(gè)Hash函數(shù)從報(bào)文中生成摘要A浴井。另外晒骇,用對(duì)方提供的公鑰對(duì)數(shù)字簽名進(jìn)行解密霉撵,得到摘要B,對(duì)比A和B是否相同洪囤,就可以得知報(bào)文有沒(méi)有被篡改過(guò)徒坡。

image

為什么需要加簽驗(yàn)簽

上小節(jié)中,加簽和驗(yàn)簽我們已經(jīng)知道概念啦瘤缩,那么喇完,為什么需要加簽和驗(yàn)簽?zāi)兀坑行┡笥芽赡苡X(jué)得剥啤,我們不是用公鑰加密锦溪,私鑰解密就好了嘛?

接下來(lái)呢府怯,舉個(gè)demo吧刻诊。

假設(shè)現(xiàn)在有A公司,要接入C公司的轉(zhuǎn)賬系統(tǒng)牺丙。在一開(kāi)始呢则涯,C公司把自己的公鑰寄給A公司复局,自己收藏好私鑰。A公司這邊的商戶粟判,發(fā)起轉(zhuǎn)賬時(shí)亿昏,A公司先用C公司的公鑰,對(duì)請(qǐng)求報(bào)文加密档礁,加密報(bào)文到達(dá)C公司的轉(zhuǎn)賬系統(tǒng)時(shí)角钩,C公司就用自己的私鑰把報(bào)文揭開(kāi)。假設(shè)在加密的報(bào)文在傳輸過(guò)程中呻澜,被中間人Actor獲取了彤断,他也郁悶,因?yàn)樗麤](méi)有私鑰易迹,看著天鵝肉宰衙,又吃不了。本來(lái)想修改報(bào)文睹欲,給自己賬號(hào)轉(zhuǎn)一個(gè)億的供炼,哈哈。這個(gè)實(shí)現(xiàn)方式看起來(lái)是天衣無(wú)縫窘疮,穩(wěn)得一匹的袋哼。

image

但是呢,如果一開(kāi)始闸衫,C公司把公鑰發(fā)給公司A的時(shí)候涛贯,就被中間人Actor獲取到呢,醬紫就出問(wèn)題了蔚出。

中間人Actor截取了C的公鑰弟翘,他把自己的公鑰發(fā)給了A公司,A誤以為這就是C公司的公鑰骄酗。A在發(fā)起轉(zhuǎn)賬時(shí)稀余,用Actor的公鑰,對(duì)請(qǐng)求報(bào)文加密趋翻,加密報(bào)文到在傳輸過(guò)程睛琳,Actor又截取了,這時(shí)候踏烙,他用自己的私鑰解密师骗,然后修改了報(bào)文(給自己轉(zhuǎn)一個(gè)億),再用C的公鑰加密讨惩,發(fā)給C公司辟癌,C公司收到報(bào)文后,繼續(xù)用自己的私鑰解密步脓。最后是不是A公司的轉(zhuǎn)賬賬戶損失了一個(gè)億呢~

image

C公司是怎么區(qū)分報(bào)文是不是來(lái)自A呢愿待,還是被中間人修改過(guò)呢浩螺?為了表明身份和報(bào)文真實(shí)性,這就需要加簽驗(yàn)簽啦仍侥!

A公司把自己的公鑰也發(fā)送給C公司要出,私鑰自己保留著。在發(fā)起轉(zhuǎn)賬時(shí)农渊,先用自己的私鑰對(duì)請(qǐng)求報(bào)文加簽患蹂,于是得到自己的數(shù)字簽名。再把數(shù)字簽名和請(qǐng)求報(bào)文一起發(fā)送給C公司砸紊。C公司收到報(bào)文后传于,拿A的公鑰進(jìn)行驗(yàn)簽,如果原始報(bào)文和數(shù)字簽名的摘要內(nèi)容不一致醉顽,那就是報(bào)文被篡改啦~

image

有些朋友可能有疑問(wèn)沼溜,假設(shè)A在發(fā)自己的公鑰給C公司的時(shí)候,也被中間人Actor截取了呢游添。嗯嗯系草,我們來(lái)模擬一波Actor又截取了公鑰,看看Actor能干出什么事情來(lái)~哈哈

假設(shè)Actor截取到A的公鑰后唆涝,隨后也截取了到A發(fā)往C的報(bào)文找都。他截取到報(bào)文后,第一件想做的事肯定是修改報(bào)文內(nèi)容廊酣。但是如果單單修改原始報(bào)文是不可以的能耻,因?yàn)榘l(fā)過(guò)去C公司肯定驗(yàn)簽不過(guò)啦。但是呢亡驰,數(shù)字簽名似乎解不開(kāi)晓猛,因?yàn)橄⒄惴ǎ╤ash算法)無(wú)法逆向解開(kāi)的,只起驗(yàn)證的作用呢....

所以呢隐解,公鑰與私鑰是用來(lái)加密與加密的鞍帝,加簽與驗(yàn)簽是用來(lái)證明身份诫睬,以免被篡改的煞茫。

常見(jiàn)加密相關(guān)算法簡(jiǎn)介

  • 消息摘要算法
  • 對(duì)稱加密算法
  • 非對(duì)稱加密算法
  • 國(guó)密算法

消息摘要算法:

  • 相同的明文數(shù)據(jù)經(jīng)過(guò)相同的消息摘要算法會(huì)得到相同的密文結(jié)果值。
  • 數(shù)據(jù)經(jīng)過(guò)消息摘要算法處理摄凡,得到的摘要結(jié)果值续徽,是無(wú)法還原為處理前的數(shù)據(jù)的。
  • 數(shù)據(jù)摘要算法也被稱為哈希(Hash)算法或散列算法亲澡。
  • 消息摘要算法一般用于簽名驗(yàn)簽钦扭。

消息摘要算法主要分三類:MD(Message Digest,消息摘要算法)床绪、SHA(Secure Hash Algorithm客情,安全散列算法)和MAC(Message Authentication Code其弊,消息認(rèn)證碼算法)。

image

MD家族算法

MD(Message Digest膀斋,消息摘要算法)家族梭伐,包括MD2,MD4仰担,MD5糊识。

  • MD2,MD4摔蓝,MD5 計(jì)算的結(jié)果都是是一個(gè)128位(即16字節(jié))的散列值赂苗,用于確保信息傳輸完整一致。
  • MD2的算法較慢但相對(duì)安全贮尉,MD4速度很快拌滋,但安全性下降,MD5則比MD4更安全猜谚、速度更快鸠真。
  • MD5被廣泛應(yīng)用于數(shù)據(jù)完整性校驗(yàn)、數(shù)據(jù)(消息)摘要龄毡、數(shù)據(jù)加密等吠卷。
  • MD5,可以被破解沦零,對(duì)于需要高度安全性的數(shù)據(jù)祭隔,專家一般建議改用其他算法,如SHA-2路操。2004年疾渴,證實(shí)MD5算法無(wú)法防止碰撞攻擊,因此不適用于安全性認(rèn)證屯仗,如SSL公開(kāi)密鑰認(rèn)證或是數(shù)字簽名等用途搞坝。

舉個(gè)例子,看看如何獲取字符串的MD5值吧:

public class MD5Test {

    public static void main(String[] args) throws UnsupportedEncodingException {
        String s = "123";
        byte[] result = getMD5Bytes(s.getBytes());
        StringBuilder stringBuilder = new StringBuilder();
        for (byte temp : result) {
            if (temp >= 0 && temp < 16) {
                stringBuilder.append("0");
            }
            stringBuilder.append(Integer.toHexString(temp & 0xff));
        }
        System.out.println(s + ",MD5加密后:" + stringBuilder.toString());
    }

    private static byte[] getMD5Bytes(byte[] content) {
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            return md5.digest(content);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }
}


運(yùn)行結(jié)果:

123,MD5加密后:202cb962ac59075b964b07152d234b70

ShA家族算法

SHA(Secure Hash Algorithm魁袜,安全散列算法)桩撮,包括SHA-0、SHA-1峰弹、SHA-2(SHA-256,SHA-512,SHA-224,SHA-384等)店量、SHA-3。它是在MD算法基礎(chǔ)上實(shí)現(xiàn)的鞠呈,與MD算法區(qū)別在于摘要長(zhǎng)度融师,SHA 算法的摘要長(zhǎng)度更長(zhǎng),安全性更高蚁吝。

  • SHA-0發(fā)布之后很快就被NSA撤回旱爆,因?yàn)楹袝?huì)降低密碼安全性的錯(cuò)誤舀射,它是SHA-1的前身。
  • SHA-1在許多安全協(xié)議中廣為使用怀伦,包括TLS后控、GnuPG、SSH空镜、S/MIME和IPsec浩淘,是MD5的后繼者。
  • SHA-2包括SHA-224吴攒、SHA-256张抄、SHA-384、SHA-512洼怔、SHA-512/224署惯、SHA-512/256。它的算法跟SHA-1基本上相似镣隶,目前還沒(méi)有出現(xiàn)明顯弱點(diǎn)极谊。
  • SHA-3是2015年正式發(fā)布,由于對(duì)MD5出現(xiàn)成功的破解安岂,以及對(duì)SHA-0和SHA-1出現(xiàn)理論上破解的方法轻猖,SHA-3應(yīng)運(yùn)而生。它與之前算法不同的是域那,它是可替換的加密散列算法咙边。

SHA-1、SHA-2(SHA-256,SHA-512,SHA-224,SHA-384)等算法是比較常用的次员,我們來(lái)看看跟MD5的對(duì)比吧

image.png

MAC算法家族

MAC算法 MAC(Message Authentication Code败许,消息認(rèn)證碼算法),是帶密鑰的Hash函數(shù)淑蔚。輸入密鑰和消息市殷,輸出一個(gè)消息摘要。
它集合了MD和SHA兩大系列消息摘要算法刹衫。

  • MD 系列算法: HmacMD2醋寝、HmacMD4 和 HmacMD5 ;
  • SHA 系列算法:HmacSHA1绪妹、HmacSHA224甥桂、HmacSHA256、HmacSHA384 和 HmacSHA512 邮旷。

對(duì)稱加密算法

加密和解密使用相同密鑰的加密算法就是對(duì)稱加密算法。常見(jiàn)的對(duì)稱加密算法有AES蝇摸、3DES婶肩、DES办陷、RC5、RC6等律歼。

image

DES

數(shù)據(jù)加密標(biāo)準(zhǔn)(英語(yǔ):Data Encryption Standard民镜,縮寫為 DES)是一種對(duì)稱密鑰加密塊密碼算法。
DES算法的入口參數(shù)有三個(gè):Key险毁、Data制圈、Mode。

  • Key: 7個(gè)字節(jié)共56位畔况,是DES算法的工作密鑰鲸鹦;
  • Data: 8個(gè)字節(jié)64位,是要被加密或被解密的數(shù)據(jù)跷跪;
  • Mode: 加密或解密馋嗜。

3DES

三重?cái)?shù)據(jù)加密算法(英語(yǔ):Triple Data Encryption Algorithm,又稱3DES(Triple DES)吵瞻,是一種對(duì)稱密鑰加密塊密碼葛菇,相當(dāng)于是對(duì)每個(gè)數(shù)據(jù)塊應(yīng)用三次數(shù)據(jù)加密標(biāo)準(zhǔn)(DES)算法。

AES

AES橡羞,高級(jí)加密標(biāo)準(zhǔn)(英語(yǔ):Advanced Encryption Standard)眯停,在密碼學(xué)中又稱Rijndael加密法,是美國(guó)聯(lián)邦政府采用的一種區(qū)塊加密標(biāo)準(zhǔn)卿泽。

  • 采用對(duì)稱分組密碼體制庵朝,密鑰長(zhǎng)度為 128 位、 192 位又厉、256 位九府,分組長(zhǎng)度128位
  • 相對(duì)于DES ,AES具有更好的 安全性覆致、效率 和 靈活性侄旬。

非對(duì)稱加密算法

非對(duì)稱加密算法需要兩個(gè)密鑰:公鑰和私鑰。公鑰與私鑰是成對(duì)存在的煌妈,如果用公鑰對(duì)數(shù)據(jù)進(jìn)行加密儡羔,只有用對(duì)應(yīng)的私鑰才能解密。主要的非對(duì)稱加密算法有:RSA璧诵、Elgamal汰蜘、DSA、D-H之宿、ECC族操。

image

RSA算法

  • RSA加密算法是一種非對(duì)稱加密算法,廣泛應(yīng)用于加密和數(shù)字簽名
  • RSA算法原理:兩個(gè)大素?cái)?shù)的乘積進(jìn)行因式分解卻極其困難,因此可以將乘積公開(kāi)作為加密密鑰。
  • RSA是被研究得最廣泛的公鑰算法色难,從提出到現(xiàn)在泼舱,經(jīng)歷了各種攻擊的考驗(yàn),普遍認(rèn)為是目前最優(yōu)秀的公鑰方案之一枷莉。

DSA

  • DSA(Digital Signature Algorithm,數(shù)字簽名算法),也是一種非對(duì)稱加密算法娇昙。
  • DSA和RSA區(qū)別在,DSA僅用于數(shù)字簽名笤妙,不能用于數(shù)據(jù)加密解密冒掌。其安全性和RSA相當(dāng),但其性能要比RSA好蹲盘。

ECC 算法

  • ECC(Elliptic Curves Cryptography股毫,橢圓曲線密碼編碼學(xué)),基于橢圓曲線加密辜限。
  • Ecc主要優(yōu)勢(shì)是皇拣,在某些情況下,它比其他的方法使用更小的密鑰薄嫡,比如RSA加密算法氧急,提供相當(dāng)?shù)幕蚋叩燃?jí)的安全級(jí)別。
  • 它的一個(gè)缺點(diǎn)是毫深,加密和解密操作的實(shí)現(xiàn)比其他機(jī)制時(shí)間長(zhǎng) (相比RSA算法吩坝,該算法對(duì)CPU 消耗嚴(yán)重)。

國(guó)密算法

國(guó)密即國(guó)家密碼局認(rèn)定的國(guó)產(chǎn)密碼算法哑蔫。為了保障商用密碼的安全性钉寝,國(guó)家商用密碼管理辦公室制定了一系列密碼標(biāo)準(zhǔn),即SM1闸迷,SM2嵌纲,SM3,SM4等國(guó)密算法腥沽。

image

SM1

  • SM1逮走,為對(duì)稱加密算法,加密強(qiáng)度為128位今阳,基于硬件實(shí)現(xiàn)师溅。
  • SM1的加密強(qiáng)度和性能,與AES相當(dāng)盾舌。

SM2

  • SM2主要包括三部分:簽名算法墓臭、密鑰交換算法、加密算法
  • SM2用于替換RSA加密算法妖谴,基于ECC窿锉,效率較低。

SM3

  • SM3,即國(guó)產(chǎn)消息摘要算法榆综。
  • 適用于商用密碼應(yīng)用中的數(shù)字簽名和驗(yàn)證妙痹,消息認(rèn)證碼的生成與驗(yàn)證以及隨機(jī)數(shù)的生成铸史。

SM4

  • SM4是一個(gè)分組算法鼻疮,用于無(wú)線局域網(wǎng)產(chǎn)品。
  • 該算法的分組長(zhǎng)度為128比特琳轿,密鑰長(zhǎng)度為128比特判沟。
  • 加密算法與密鑰擴(kuò)展算法都采用32輪非線性迭代結(jié)構(gòu)。
  • 解密算法與加密算法的結(jié)構(gòu)相同崭篡,只是輪密鑰的使用順序相反挪哄,解密輪密鑰是加密輪密鑰的逆序。
  • 它的功能類似國(guó)際算法的DES琉闪。

加簽驗(yàn)簽相關(guān)Java的API

這個(gè)小節(jié)先介紹一下加簽驗(yàn)簽需要用到的API吧~

image

加簽相關(guān)API

- java.security.Signature.getInstance(String algorithm); //根據(jù)對(duì)應(yīng)算法迹炼,初始化簽名對(duì)象
- KeyFactory.getInstance(String algorithm);// 根據(jù)對(duì)應(yīng)算法,生成KeyFactory對(duì)象
- KeyFactory.generatePrivate(KeySpec keySpec); //生成私鑰
- java.security.Signature.initSign(PrivateKey privateKey) //由私鑰,初始化加簽對(duì)象
- java.security.Signature.update(byte[] data)  //把原始報(bào)文更新到加簽對(duì)象
- java.security.Signature.sign();//加簽

Signature.getInstance(String algorithm);

  • 根據(jù)對(duì)應(yīng)算法颠毙,初始化簽名對(duì)象
  • algorithm參數(shù)可以取SHA256WithRSA或者M(jìn)D5WithRSA等參數(shù)斯入,SHA256WithRSA表示生成摘要用的是SHA256算法,簽名加簽用的是RSA算法

KeyFactory.getInstance(String algorithm);

  • 根據(jù)對(duì)應(yīng)算法,生成KeyFactory對(duì)象蛀蜜,比如你的公私鑰用的是RSA算法刻两,那么就傳入RSA

KeyFactory.generatePrivate(KeySpec keySpec)

  • 生成私鑰,加簽用的是私鑰哈滴某,所以需要通過(guò)KeyFactory先構(gòu)造一個(gè)私鑰對(duì)象磅摹。

Signature.initSign(PrivateKey privateKey)

  • 加簽用的是私鑰,所以傳入私鑰霎奢,初始化加簽對(duì)象

Signature.update(byte[] data)

  • 把原始報(bào)文更新到加簽對(duì)象

java.security.Signature.sign();

  • 進(jìn)行加簽操作

驗(yàn)簽相關(guān)API

- java.security.Signature.getInstance(String algorithm); //根據(jù)對(duì)應(yīng)算法户誓,初始化簽名對(duì)象
- KeyFactory.getInstance(String algorithm);// 根據(jù)對(duì)應(yīng)算法,生成KeyFactory對(duì)象
- KeyFactory.generatePublic(KeySpec keySpec); //生成公鑰
- java.security.Signature.initVerify(publicKey); //由公鑰,初始化驗(yàn)簽對(duì)象
- java.security.Signature.update(byte[] data)  //把原始報(bào)文更新到驗(yàn)簽對(duì)象
- java.security.Signature.verify(byte[] signature);//驗(yàn)簽

Signature.getInstance(String algorithm)

  • 根據(jù)對(duì)應(yīng)算法幕侠,初始化簽名對(duì)象帝美,注意驗(yàn)簽和加簽是需要用相同的algorithm算法參數(shù)哦~

KeyFactory.getInstance(String algorithm);

  • 根據(jù)對(duì)應(yīng)算法,生成KeyFactory對(duì)象

KeyFactory.generatePublic(KeySpec keySpec);

  • 生成公鑰,驗(yàn)簽用的是公鑰橙依,通過(guò)KeyFactory先構(gòu)造一個(gè)公鑰對(duì)象

**Signature.initVerify(publicKey); **

  • 公鑰驗(yàn)簽证舟,所以傳入公鑰對(duì)象參數(shù),初始化驗(yàn)簽對(duì)象

Signature.update(byte[] data)

  • 把原始報(bào)文更新到加簽對(duì)象

Signature.verify(byte[] signature);

  • 進(jìn)行驗(yàn)簽操作

加簽驗(yàn)簽代碼實(shí)現(xiàn)

前幾個(gè)小節(jié)討論完概念窗骑,是時(shí)候上代碼實(shí)戰(zhàn)了女责,如下:

package pattern;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;

/**
 * 加簽驗(yàn)簽demo
 *  @Author 撿田螺的小男孩
 */
public class SignatureTest {
    //公鑰字符串
    private static final String PUBLIC_KEY_STR = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDaJzVjC5K6kbS2YE2fiDs6H8pB\n" +
            "JFDGEYqqJJC9I3E0Ebr5FsofdImV5eWdBSeADwcR9ppNbpORdZmcX6SipogKx9PX\n" +
            "5aAO4GPesroVeOs91xrLEGt/arteW8iSD+ZaGDUVV3+wcEdci/eCvFlc5PUuZJou\n" +
            "M2XZaDK4Fg2IRTfDXQIDAQAB";
    //私鑰字符串
    private static final String PRIVATE_KEY_STR = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANonNWMLkrqRtLZg\n" +
            "TZ+IOzofykEkUMYRiqokkL0jcTQRuvkWyh90iZXl5Z0FJ4APBxH2mk1uk5F1mZxf\n" +
            "pKKmiArH09floA7gY96yuhV46z3XGssQa39qu15byJIP5loYNRVXf7BwR1yL94K8\n" +
            "WVzk9S5kmi4zZdloMrgWDYhFN8NdAgMBAAECgYA9bz1Bn0i68b2KfqRdgOfs/nbe\n" +
            "0XNN1DLQp2t7WDfRCg01iI1zPkZgyFVZWtI85f5/uIrLs5ArLosL1oNuqqc0nNne\n" +
            "CvJK+ZxvA98Hx3ZqYTzDnleR054YhofL5awbhSciYVic204DOG1rhSsYWMqtX7J7\n" +
            "3geoWL7TYdMfYXcCAQJBAPMMKsz6ZJh98EeQ1tDG5gpAGWFQkYNrxZDelP/LjeO0\n" +
            "TP3XkQnIpcaZoCs7V/rRGRGMWwQ2BUdc/01in89ZZ5ECQQDlx2oBc1CtOAm2UAhN\n" +
            "1xWrPkZWENQ53wTrwXO4qbTGDfBKon0AehLlGCSqxQ71aufLkNO7ZlX0IHTAlnk1\n" +
            "TvENAkAGSEQ69CXxgx/Y2beTwfBkR2/gghKg0QJUUkyLqBlMz3ZGAXJwTE1sqr/n\n" +
            "HiuSAiGhwH0ByNuuEotO1sPGukrhAkAMK26a2w+nzPL+u+hkrwKPykGRZ1zGH+Cz\n" +
            "19AYNKzFXJGgclCqiMydY5T1knBDYUEbj/UW1Mmyn1FvrciHoUG1AkAEMEIuDauz\n" +
            "JabEAU08YmZw6OoDGsukRWaPfjOEiVhH88p00veM1R37nwhoDMGyEGXVeVzNPvk7\n" +
            "cELg28MSRzCK";


    public static void main(String[] args) throws SignatureException, NoSuchAlgorithmException, InvalidKeyException, IOException, InvalidKeySpecException {
        //原始報(bào)文
        String plain = "歡迎大家關(guān)注我的公眾號(hào),撿田螺的小男孩";
        //加簽
        byte[] signatureByte = sign(plain);
        System.out.println("原始報(bào)文是:" + plain);
        System.out.println("加簽結(jié)果:");
        System.out.println(new BASE64Encoder().encode(signatureByte));
        //驗(yàn)簽
        boolean verifyResult = verify(plain, signatureByte);
        System.out.println("驗(yàn)簽結(jié)果:" + verifyResult);
    }

    /**
     * 加簽方法
     * @param plain
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws UnsupportedEncodingException
     * @throws SignatureException
     */
    private static byte[] sign(String plain) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException, SignatureException {
        //根據(jù)對(duì)應(yīng)算法创译,獲取簽名對(duì)象實(shí)例
        Signature signature = Signature.getInstance("SHA256WithRSA");
        //獲取私鑰抵知,加簽用的是私鑰,私鑰一般是在配置文件里面讀的,這里為了演示方便刷喜,根據(jù)私鑰字符串生成私鑰對(duì)象
        PrivateKey privateKey = getPriveteKey(PRIVATE_KEY_STR);
        //初始化簽名對(duì)象
        signature.initSign(privateKey);
        //把原始報(bào)文更新到對(duì)象
        signature.update(plain.getBytes("UTF-8"));
        //加簽
        return signature.sign();
    }

    /**
     * 驗(yàn)簽方法
     * @param plain
     * @param signatureByte
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws IOException
     * @throws SignatureException
     * @throws InvalidKeySpecException
     */
    private static boolean verify(String plain, byte[] signatureByte) throws NoSuchAlgorithmException, InvalidKeyException, IOException, SignatureException, InvalidKeySpecException {
        //獲取公鑰
        PublicKey publicKey = getPublicKey(PUBLIC_KEY_STR);
        //根據(jù)對(duì)應(yīng)算法残制,獲取簽名對(duì)象實(shí)例
        Signature signature = Signature.getInstance("SHA256WithRSA");
        //初始化簽名對(duì)象
        signature.initVerify(publicKey);
        //把原始報(bào)文更新到簽名對(duì)象
        signature.update(plain.getBytes("UTF-8"));
        //進(jìn)行驗(yàn)簽
        return signature.verify(signatureByte);
    }

    private static PublicKey getPublicKey(String publicKeyStr) throws InvalidKeySpecException, IOException {
        PublicKey publicKey = null;
        try {
            java.security.spec.X509EncodedKeySpec bobPubKeySpec = new java.security.spec.X509EncodedKeySpec(
                    new BASE64Decoder().decodeBuffer(publicKeyStr));
            // RSA對(duì)稱加密算法
            java.security.KeyFactory keyFactory;
            keyFactory = java.security.KeyFactory.getInstance("RSA");
            // 生成公鑰對(duì)象
            publicKey = keyFactory.generatePublic(bobPubKeySpec);
           } catch (NoSuchAlgorithmException e) {
             e.printStackTrace();
            }
        return publicKey;
      }

    private static PrivateKey getPriveteKey(String privateKeyStr) {
        PrivateKey privateKey = null;
        PKCS8EncodedKeySpec priPKCS8;
        try {
            priPKCS8 = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(privateKeyStr));
            KeyFactory keyf = KeyFactory.getInstance("RSA");
            privateKey = keyf.generatePrivate(priPKCS8);
        } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) {
            e.printStackTrace();
        }
        return privateKey;
    }
}

運(yùn)行結(jié)果:

原始報(bào)文是:歡迎大家關(guān)注我的公眾號(hào),撿田螺的小男孩
加簽結(jié)果:
Oz15/aybGe42eGHbc+iMoSYHSCc8tfRskTVjjGSTPD4HjadL0CC5JUWNUW0WxHjUb4MvxWo2oeWE
Qw0+m61d+JgBMto/TWcVDcgwL/AbObsbWdQ6E/fVRqG13clkE8MyKsjt9Z7tcbwpycYTv0rUR4co
rndAVfBdtv5KeV+OXqM=
驗(yàn)簽結(jié)果:true

參考與感謝

微信公眾號(hào)

image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末搁宾,一起剝皮案震驚了整個(gè)濱河市折汞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌盖腿,老刑警劉巖爽待,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異翩腐,居然都是意外死亡鸟款,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門栗菜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)芬萍,“玉大人庇茫,你說(shuō)我怎么就攤上這事论熙÷铺猓” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵而咆,是天一觀的道長(zhǎng)霍比。 經(jīng)常有香客問(wèn)我,道長(zhǎng)暴备,這世上最難降的妖魔是什么悠瞬? 我笑而不...
    開(kāi)封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮涯捻,結(jié)果婚禮上浅妆,老公的妹妹穿的比我還像新娘。我一直安慰自己障癌,他們只是感情好凌外,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著涛浙,像睡著了一般康辑。 火紅的嫁衣襯著肌膚如雪摄欲。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天疮薇,我揣著相機(jī)與錄音胸墙,去河邊找鬼。 笑死按咒,一個(gè)胖子當(dāng)著我的面吹牛迟隅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播胖齐,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼玻淑,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼嗽冒!你這毒婦竟也來(lái)了呀伙?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤添坊,失蹤者是張志新(化名)和其女友劉穎剿另,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贬蛙,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡雨女,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了阳准。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氛堕。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖野蝇,靈堂內(nèi)的尸體忽然破棺而出讼稚,到底是詐尸還是另有隱情,我是刑警寧澤绕沈,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布锐想,位于F島的核電站,受9級(jí)特大地震影響乍狐,放射性物質(zhì)發(fā)生泄漏赠摇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一浅蚪、第九天 我趴在偏房一處隱蔽的房頂上張望藕帜。 院中可真熱鬧,春花似錦惜傲、人聲如沸洽故。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)收津。三九已至饿这,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間撞秋,已是汗流浹背长捧。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吻贿,地道東北人串结。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像舅列,于是被迫代替她去往敵國(guó)和親肌割。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容