有關(guān)數(shù)字簽名CMS簽名和PKCS7

概念

純技術(shù)约计,無(wú)任何業(yè)務(wù)信息

CMS --- Cryptographic Message Syntax
PKCS7 --- Public Key Cryptography Standards #7

PKCS是一整套的嫌拣,比如PKCS#1是講RSA算法杨刨, PKCS#8是講私鑰保密的,而PKCS#7是講通過(guò)公私鑰的加密解密實(shí)現(xiàn)的身份驗(yàn)證和數(shù)據(jù)完整性驗(yàn)證浪南。

相關(guān)的幾個(gè)標(biāo)準(zhǔn):
rfc2315 PKCS #7: Cryptographic Message Syntax Version 1.5
rfc2630 Cryptographic Message Syntax
rfc5652 Cryptographic Message Syntax (CMS)

按照以上信息父款,PKCS#7也就是CMS 1.5, 最新的CMS rfc5652是對(duì)之前得到PKCS#7等的升級(jí)演進(jìn)斩松。是兼容PKCS#7的伶唯。
網(wǎng)上搜索用BouncyCastle做pkcs7簽名, 實(shí)際代碼中類名都有很多處CMSxxx
因此現(xiàn)在來(lái)說(shuō)惧盹, PKCS7的簽名和CMS簽名實(shí)際上是一回事乳幸。

關(guān)于簽名文件內(nèi)容和后綴

通常簽名后的文件有兩種格式

二進(jìn)制格式(一般后綴p7s, 也叫DER格式,用notepad++打開(kāi)看不懂)和PEM格式(base64的文本)钧椰。

(類似的粹断,證書文件也是這兩種情況。)

PEM的格式簽名通常有兩種:

-----BEGIN PKCS7-----
MIIGWgYJKoZIhvcNAxxxxxxxxxxxxxxxxxxxxxxxx
-----END PKCS7-----
-----BEGIN CMS-----
MIIGWgYJKoZIhvcNAxxxxxxxxxxxxxxxxxxxxxxxx
-----BEGIN CMS-----

二進(jìn)制格式的可以通過(guò)命令轉(zhuǎn)換成PEM格式:

openssl pkcs7 -in UDM20.5.1.25_22.UpgradeTool.zip.p7s  -inform der  -out signed.p7s.pem

這幾種格式實(shí)質(zhì)上是一樣的嫡霞,至少用openssl命令瓶埋,或者自己寫的調(diào)用BouncyCastle的代碼,或者用公司開(kāi)發(fā)的CMS校驗(yàn)庫(kù)都一樣處理秒际。

只不過(guò)有的時(shí)候調(diào)用代碼庫(kù)需要輸入String的時(shí)候悬赏,只能用文本格式(PEM)而已。

PKCS#7簽名和驗(yàn)證過(guò)程

簽名目的是確保簽名者身份娄徊,以及簽名后的信息不能被篡改

簽名的步驟包括:

  1. 計(jì)算原始信息的摘要值(根據(jù)指定的摘要算法)

  2. 用RSA(或DSA等其他非對(duì)稱算法)的私鑰加密這個(gè)摘要闽颇。

簽名驗(yàn)證的步驟:

  1. 用RSA公鑰解密得到一個(gè)摘要值
  2. 用相同的摘要算法,對(duì)原始信息計(jì)算也得到一個(gè)摘要值
  3. 對(duì)比1和2的結(jié)果寄锐,如果相同兵多,驗(yàn)證通過(guò)尖啡。

簽名和驗(yàn)簽操作:

Openssl中的cms簽名和校驗(yàn)

#自己寫的一對(duì)一簽名和校驗(yàn)。
openssl cms -sign -nosmimecap -signer sig.crt -inkey sig.pem -binary -in source.txt -out dest.txt
openssl cms -verify -in dest.txt -signer sig.crt -CAfile root.crt -out signedtext.txt

驗(yàn)證已經(jīng)簽發(fā)好的軟件包

目前來(lái)看剩膘,openssl命令衅斩、自己寫的代碼和我們調(diào)用公司CMS簽名庫(kù)的方法,這幾種都是一致的怠褐,只要一個(gè)能校驗(yàn)通過(guò)畏梆,其他也都可以。

openssl cms命令驗(yàn)證

openssl cms -verify -binary -in signed_file.p7s  -signer "ca.der"  -inform der   -noverify  -content content.zip    -certsout mycerts.pem > /dev/null
  • binary參數(shù)表示對(duì)輸入的內(nèi)容直接使用奈懒,不轉(zhuǎn)換處理奠涌。(否則的話,除非你不提供content磷杏,如果提供了溜畅,就會(huì)處理成smime什么的內(nèi)容格式,驗(yàn)證就會(huì)有問(wèn)題)
  • in表示輸入的簽名文件极祸。
  • inform指定輸入的簽名文件的格式慈格。我們這里常用也就是DER或PEM。
  • signer是直接簽署這個(gè)簽名用的證書遥金。也可以是個(gè)CA證書浴捆,但是簽名文件內(nèi)必須包含由該證書簽發(fā)的“直接簽名用證書”。 也就是說(shuō)必須能夠鏈起來(lái)汰规,知道最后一個(gè)證書是用來(lái)簽名的汤功。
  • noverify 不對(duì)證書本身的有效性做檢查。(但還是必須鏈起來(lái))
  • content 有時(shí)候簽名文件中會(huì)包含原文溜哮。但不包含的時(shí)候需要用這個(gè)參數(shù)指定原文件。
  • certsout 輸出簽名用的證書色解。當(dāng)然也可以不需要茂嗓。
  • nointern 不加這個(gè)參數(shù)時(shí),提供的證書和簽名文件中證書同時(shí)參與驗(yàn)簽(看能不能鏈起來(lái))科阎。加了這個(gè)參數(shù)述吸, 簽名文件中的證書會(huì)被忽略。所以一般不加比較好锣笨。
  • out textdata 輸出簽名原文蝌矛, 一般也不需要
  • 最后的> /dev/null, 把控制臺(tái)輸出隱藏错英。因?yàn)槿绻辛?content參數(shù)入撒, 會(huì)把原文打出來(lái),內(nèi)容太多椭岩。

舉例常見(jiàn)錯(cuò):
openssl校驗(yàn)失敗茅逮,報(bào)錯(cuò)如下, 同時(shí)在java中讀取的CMSSignedData對(duì)象.getSignedContent()為空璃赡,是因?yàn)槿鄙僭膬?nèi)容,需加參數(shù)-content献雅。

[root@dggphisprd47503 package_ok]# openssl cms -verify  -in signed.cms  -signer public.pem -inform PEM   -noverify

Verification failure
139927364507536:error:2E06307F:CMS routines:CHECK_CONTENT:no content:cms_smime.c:120:

其他

  從p7s文件中分離出簽名所用的證書(可能只支持p7s的二進(jìn)制和pem碉考,不支持cms,待驗(yàn)證)
  ```shell
  openssl pkcs7 -in signed.p7s  -inform pem -print_certs  


###  BouncyCastle代碼驗(yàn)證


```java
public PKCS7SignTool() {
        if (Security.getProvider("BC") == null) {
            Security.addProvider(new BouncyCastleProvider());
        }
    }

    //只驗(yàn)證一個(gè)簽名挺身, 
    public static boolean verify(String certFile, byte[] originMessage, byte[] signedMessage)
        throws FileNotFoundException, CertificateException, CMSException, OperatorCreationException {
        Certificate certificate = loadCert(certFile);
    
        CMSSignedData sign = new CMSSignedData(new CMSProcessableByteArray(originMessage), Base64.decode(signedMessage));
    
        CollectionStore<X509CertificateHolder> certificateHolderStore = (CollectionStore)sign.getCertificates();
        for (Iterator i = certificateHolderStore.iterator(); i.hasNext(); ) {
            X509Certificate x509Cert = new JcaX509CertificateConverter().getCertificate((X509CertificateHolder) i.next());
            //System.out.println("cert in signedMsg: "+x509Cert.getSubjectDN()+x509Cert.getSerialNumber());
        }
    
        SignerInformationStore signers = sign.getSignerInfos();
        SignerInformation signerInfo = signers.getSigners().iterator().next();
        //這里證書使用了傳入的證書侯谁,沒(méi)有用簽名文件中的證書。實(shí)際正常都要用到的章钾。
        PublicKey publicKey = certificate.getPublicKey();
        return signerInfo.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(publicKey));
    }
    
    public static byte[] sign(String certFile, String keyFile, byte[] srcMessage,boolean containCert)
        throws IOException, CertificateException, CMSException, NoSuchAlgorithmException, InvalidKeySpecException,
               OperatorCreationException {
        Certificate certificate = loadCert(certFile);
    
        byte[] encodedKey = Files.readAllBytes(Paths.get(keyFile));
        String keyStr = new String(encodedKey).replace("-----BEGIN RSA PRIVATE KEY-----", "")
            .replace("-----END RSA PRIVATE KEY-----", "");
    
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.decode(keyStr.getBytes()));
        KeyFactory kf = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = kf.generatePrivate(spec);


        ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").setProvider("BC").build(privateKey);
    
        CMSSignedDataGenerator generator  = new CMSSignedDataGenerator();
        generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(
            new JcaDigestCalculatorProviderBuilder().setProvider("BC")
                .build()).build(signer,  (X509Certificate)certificate));
    
        //add cert to generated sign data;
        if(containCert){
            generator.addCertificates(new JcaCertStore(Arrays.asList(certificate)));
        }
    
        CMSSignedData signedData = generator.generate(new CMSProcessableByteArray(srcMessage), false);
        return Base64.encode(signedData.getEncoded());
    }
    
    private static Certificate loadCert(String certFile) throws FileNotFoundException, CertificateException {
        InputStream inStream = new FileInputStream(certFile);
        BufferedInputStream bis = new BufferedInputStream(inStream);
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        Certificate certificate = cf.generateCertificate(bis);
        return certificate;
    }

注意證書的key usage

上述操作中墙贱,openssl驗(yàn)證簽名時(shí)同時(shí)要驗(yàn)證證書(除非增加參數(shù) -noverify),
BouncyCastle的接口中不會(huì)驗(yàn)證證書伍玖,對(duì)證書的驗(yàn)證要自行另外寫代碼嫩痰。
通常證書驗(yàn)證僅包含有效期,簽發(fā)者是否可信等等等窍箍,
如果證書做簽名和驗(yàn)證簽名串纺,實(shí)際上還有一個(gè)點(diǎn),很容易遺漏的椰棘,就是證書的key usage纺棺。
通過(guò)命令可以查看證書用途。

openssl x509  -in  xxx.crt  -text

根據(jù)https://tools.ietf.org/html/rfc5280#section-4.2.1.3的說(shuō)明邪狞,如果要做書簽簽名祷蝌,keyusage要包含digitalSignature (0)

如果沒(méi)有包含的話, openssl驗(yàn)證簽名時(shí)不通過(guò)的帆卓, 會(huì)報(bào)錯(cuò)Verify error:unsupported certificate purpose巨朦。

之前給友商技術(shù)人員解釋,類似于你有一個(gè)正規(guī)的駕照剑令,只規(guī)定了能開(kāi)C1汽車糊啡, 如果用來(lái)開(kāi)摩托車是不行的。
這一點(diǎn)嚴(yán)格的說(shuō)要校驗(yàn)出來(lái)吁津,但是大部分時(shí)候都被忽略了棚蓄, 以后需關(guān)注。

當(dāng)然自己寫校驗(yàn)代碼或者有openssl時(shí)通常都忽略掉了這個(gè)校驗(yàn)碍脏。




# 簽名不通過(guò)問(wèn)題的校驗(yàn)步驟

1. 首先確認(rèn)是不是真的校驗(yàn)不通過(guò)梭依。可以要求問(wèn)題提出方用openssl命令校驗(yàn)一下典尾。

 校驗(yàn)時(shí)可以先不考慮證書鏈(輸入?yún)?shù)noverify)

 - openssl命令目前看是完全一致的役拴,只要是我們系統(tǒng)應(yīng)當(dāng)通過(guò)的, openssl都可以通過(guò)急黎。

 - 我們也可以協(xié)助用命令校驗(yàn)扎狱,如果還是不通過(guò)侧到,就不用往下走了。

 - 如果對(duì)方無(wú)法提供openssl的校驗(yàn)證明淤击,能提供校驗(yàn)代碼也可以匠抗,否則的話,他如何說(shuō)明制作的包是符合規(guī)范的呢污抬。

 - 如果確定校驗(yàn)沒(méi)有問(wèn)題汞贸,把相關(guān)的原文,簽名文件印机,證書都拿過(guò)來(lái)矢腻,在家里linux環(huán)境用openssl再確認(rèn)一遍。

2. 手工接觸簽名文件中的證書射赛, 看和提供的證書加到一起多柑,能否從自簽CA到最終證書能否構(gòu)成一條鏈。

3. 有了以上保證楣责,將openssl中校驗(yàn)通過(guò)的content輸入竣灌,去掉begin和end頭尾作為字符串作為原文,和簽名文件一起輸入我們系統(tǒng)中秆麸,必然是能校驗(yàn)通過(guò)的初嘹。

 此時(shí)就對(duì)比前臺(tái)傳入包時(shí)和我們自己構(gòu)造的文件的差別即可定位。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末沮趣,一起剝皮案震驚了整個(gè)濱河市屯烦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌房铭,老刑警劉巖驻龟,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異缸匪,居然都是意外死亡迅脐,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門豪嗽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人豌骏,你說(shuō)我怎么就攤上這事龟梦。” “怎么了窃躲?”我有些...
    開(kāi)封第一講書人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵计贰,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蒂窒,道長(zhǎng)躁倒,這世上最難降的妖魔是什么荞怒? 我笑而不...
    開(kāi)封第一講書人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮秧秉,結(jié)果婚禮上褐桌,老公的妹妹穿的比我還像新娘。我一直安慰自己象迎,他們只是感情好荧嵌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著砾淌,像睡著了一般啦撮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上汪厨,一...
    開(kāi)封第一講書人閱讀 51,775評(píng)論 1 307
  • 那天赃春,我揣著相機(jī)與錄音,去河邊找鬼劫乱。 笑死织中,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的要拂。 我是一名探鬼主播抠璃,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼脱惰!你這毒婦竟也來(lái)了搏嗡?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤拉一,失蹤者是張志新(化名)和其女友劉穎采盒,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蔚润,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡磅氨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嫡纠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烦租。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖除盏,靈堂內(nèi)的尸體忽然破棺而出叉橱,到底是詐尸還是另有隱情,我是刑警寧澤者蠕,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布窃祝,位于F島的核電站,受9級(jí)特大地震影響踱侣,放射性物質(zhì)發(fā)生泄漏粪小。R本人自食惡果不足惜大磺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望探膊。 院中可真熱鬧杠愧,春花似錦、人聲如沸突想。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)猾担。三九已至袭灯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間绑嘹,已是汗流浹背稽荧。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留工腋,地道東北人姨丈。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像擅腰,于是被迫代替她去往敵國(guó)和親蟋恬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

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