前言:
P1簽名:即裸簽名,簽名值中只有簽名信息.
p7簽名:即,簽名中可以帶有其他的附加信息,例如簽名證書信息,簽名原文信息,時間戳信息等.
所以要注意,不要p7的簽名,用p1的方式來驗簽,這樣是不對的.是錯誤的.
數字簽名中,包含了兩個過程:
1.對要簽名的信息,用指定的hash算法,獲取信息的hash值.
2.用私鑰,對hash值進行加密,輸出加密串(也就是簽名值).
以上方式也就是裸簽名,PKCS#1
驗證簽名:
1.對要簽名的信息(也就是簽名原文),用指定的hash算法,獲取信息的hash值.
2.用公鑰信息,解密簽名值,從中獲取加密的hash串,和上面獲取的hash值進行對比,一致則認為驗簽通過,不一致則不通過.
需要注意,如果調用遠程簽名(比如電子簽名),因為根據要簽名的數據格式的不同,所以我們本地驗簽的時候,也要根據不同的簽名方式,來進行驗證(也就說,他們那邊簽名的時候,真正用來簽名的字節數組是怎么來的)
目前常見的幾種方式:(P1簽名驗簽)
contentType==CT_MESSAGE時,為待簽名的消息;
contentType==CT_BASE64_DATA時,為待簽名的base64編碼數據;
contentType==CT_HASH時為待簽名的HASH;
contentType==CT_FILE_URL時為待簽名文件地址URL
contentType== CT_STORAGE時為待簽名內容存儲編號
CT_HASH:告訴簽名方,我這個是對簽名原文hash過了的hash串,你們直接對這個值進行加密即可,不需要再hash了.
所以這種方式,我們本地驗簽的時候,要將原文放入進行驗簽,而不是hash過后的hash值(因為驗簽的時候,它又會hash一次)
/*** 測試hash方式的簽名驗簽* @throws Exception */@Testpublic void testSignWithHash() {try {CertificateFactory cf = CertificateFactory.getInstance("X.509"); Certificate usercert = cf.generateCertificate(new FileInputStream(USER_CERT)); PublicKey publicKey = usercert.getPublicKey();//讀取pfx證書上的秘鑰對信息BouncyCastleProvider provider = new BouncyCastleProvider();Signature signature = Signature.getInstance("SHA1withRSA"); MessageDigest digest = MessageDigest.getInstance("SHA-1", provider);List<UserInfo> userInfoList = userInfoService.findAll();UserInfo userInfo = userInfoList.get(0);Map<String, Object> puserCert = new HashMap<>();puserCert.put("userInfo", userInfo);List<UserCert> userCertList = userCertService.getListByParam(puserCert);UserCert userCert = userCertList.get(0);YuanZi_P1SignRequest yuanZiP1SignRequest = new YuanZi_P1SignRequest();yuanZiP1SignRequest.setAlias(userInfo.getAlias());yuanZiP1SignRequest.setHashAlg("SHA1");yuanZiP1SignRequest.setContentType("CT_HASH");String content = "我是簽名原文";byte[] dataToSign = digest.digest(content.getBytes(Charset.forName("UTF-8")));System.out.println("dataToSign:"+Base64.encodeBytes(dataToSign));yuanZiP1SignRequest.setContent(Base64.encodeBytes(dataToSign));YuanZi_P1SignResponse yuanZiP1SignResponse = yuanZiService.signP1(yuanZiP1SignRequest);System.out.println("原子服務返回的簽名值:"+yuanZiP1SignResponse.getSignedData());byte[] dataHadSignature = Base64.decode(yuanZiP1SignResponse.getSignedData());YuanZi_VerifyP1SignRequest yuanZiVerifyP1SignRequest = new YuanZi_VerifyP1SignRequest();yuanZiVerifyP1SignRequest.setSignedData(yuanZiP1SignResponse.getSignedData());yuanZiVerifyP1SignRequest.setContentType("CT_HASH");yuanZiVerifyP1SignRequest.setContent(Base64.encodeBytes(dataToSign));yuanZiVerifyP1SignRequest.setHashAlg("SHA1");yuanZiVerifyP1SignRequest.setCert(userCert.getCertBuf().getCertsignBuf());YuanZi_VerifyP1SignResponse yuanZiVerifyP1SignResponse = yuanZiService.verifyP1Sign(yuanZiVerifyP1SignRequest);System.out.println(yuanZiVerifyP1SignResponse.getCode());//本地驗簽boolean verify=false; signature.initVerify(usercert); //使用公鑰初始化簽名對象,用于驗證簽名 signature.update(content.getBytes(Charset.forName("UTF-8")));verify= signature.verify(dataHadSignature); //得到驗證結果 System.out.println("本地的驗簽結果:"+verify);} catch (Exception e) {e.printStackTrace();// TODO: handle exception}}
CT_MESSAGE:即,告訴簽名方,我發過去的內容,就是簽名的原文.一般來說,因為簽名值都是字節數組,所以簽名方會根據約定的編碼方式,對這個簽名原文按規定的編碼方式,取它的字節數組之后,進行簽名
我們本地驗簽的時候,也要對原文做同樣的操作
/*** 測試原子服務P1方式的簽名和驗簽通過* @throws Exception */@Testpublic void testSign_P1_CT_MESSAGE() {try {CertificateFactory cf = CertificateFactory.getInstance("X.509"); Certificate usercert = cf.generateCertificate(new FileInputStream(USER_CERT)); PublicKey publicKey = usercert.getPublicKey();//讀取pfx證書上的秘鑰對信息BouncyCastleProvider provider = new BouncyCastleProvider();Signature signature = Signature.getInstance("SHA1withRSA"); MessageDigest digest = MessageDigest.getInstance("SHA-1", provider);List<UserInfo> userInfoList = userInfoService.findAll();UserInfo userInfo = userInfoList.get(0);Map<String, Object> puserCert = new HashMap<>();puserCert.put("userInfo", userInfo);List<UserCert> userCertList = userCertService.getListByParam(puserCert);UserCert userCert = userCertList.get(0);YuanZi_P1SignRequest yuanZiP1SignRequest = new YuanZi_P1SignRequest();yuanZiP1SignRequest.setAlias(userInfo.getAlias());yuanZiP1SignRequest.setHashAlg("SHA1");yuanZiP1SignRequest.setContentType("CT_MESSAGE");String content = "我是簽名原文";yuanZiP1SignRequest.setContent(content);YuanZi_P1SignResponse yuanZiP1SignResponse = yuanZiService.signP1(yuanZiP1SignRequest);System.out.println("原子服務返回的簽名值:"+yuanZiP1SignResponse.getSignedData());byte[] dataHadSignature = Base64.decode(yuanZiP1SignResponse.getSignedData());YuanZi_VerifyP1SignRequest yuanZiVerifyP1SignRequest = new YuanZi_VerifyP1SignRequest();yuanZiVerifyP1SignRequest.setSignedData(yuanZiP1SignResponse.getSignedData());yuanZiVerifyP1SignRequest.setContentType("CT_MESSAGE");yuanZiVerifyP1SignRequest.setContent(content);yuanZiVerifyP1SignRequest.setHashAlg("SHA1");yuanZiVerifyP1SignRequest.setCert(userCert.getCertBuf().getCertsignBuf());YuanZi_VerifyP1SignResponse yuanZiVerifyP1SignResponse = yuanZiService.verifyP1Sign(yuanZiVerifyP1SignRequest);System.out.println(yuanZiVerifyP1SignResponse.getCode());//本地驗簽boolean verify=false; signature.initVerify(usercert); //使用公鑰初始化簽名對象,用于驗證簽名 signature.update(content.getBytes(Charset.forName("UTF-8")));verify= signature.verify(dataHadSignature); //得到驗證結果 System.out.println("本地的驗簽結果:"+verify);} catch (Exception e) {e.printStackTrace();// TODO: handle exception}}
CT_BASE64_DATA:因為有時候,我們要簽名的原文,它就是一個byte[],但是為了方便傳輸,一般簽名方都要求接受的是一個字符串.所以就有了這種簽名方式.即:我們需要對簽名原文的byte[]做base64編碼,簽名方收到之后,再做解碼,并且把解碼后的byte[]進行簽名.
所以我們本身驗簽的時候,只要傳入byte[]就行.
第三方返回的簽名值,一般也是簽名值byte[]做base64編碼后的字符串,所以我們要做base64解碼,才能獲取到簽名值byte[].
?
/*** 測試原子服務P1方式的CT_BASE64_DATA簽名和驗簽通過* @throws Exception * * 注意多種簽名方式,你傳給它要簽名的數據,和它最后執行簽名的時候,真正要簽的那個原文可能跟你傳的不是同一個(比如需要做String 轉為byte,base64編碼要解碼)*/@Testpublic void testSign_P1_CT_BASE64_DATA() {try {CertificateFactory cf = CertificateFactory.getInstance("X.509"); Certificate usercert = cf.generateCertificate(new FileInputStream(USER_CERT)); PublicKey publicKey = usercert.getPublicKey();//讀取pfx證書上的秘鑰對信息BouncyCastleProvider provider = new BouncyCastleProvider();Signature signature = Signature.getInstance("SHA1withRSA"); MessageDigest digest = MessageDigest.getInstance("SHA-1", provider);List<UserInfo> userInfoList = userInfoService.findAll();UserInfo userInfo = userInfoList.get(0);Map<String, Object> puserCert = new HashMap<>();puserCert.put("userInfo", userInfo);List<UserCert> userCertList = userCertService.getListByParam(puserCert);UserCert userCert = userCertList.get(0);YuanZi_P1SignRequest yuanZiP1SignRequest = new YuanZi_P1SignRequest();yuanZiP1SignRequest.setAlias(userInfo.getAlias());yuanZiP1SignRequest.setHashAlg("SHA1");yuanZiP1SignRequest.setContentType("CT_BASE64_DATA");byte[] dataToSign = new byte[10];String base64ToSign = Base64.encodeBytes(dataToSign);yuanZiP1SignRequest.setContent(base64ToSign);YuanZi_P1SignResponse yuanZiP1SignResponse = yuanZiService.signP1(yuanZiP1SignRequest);System.out.println("原子服務返回的簽名狀態:"+yuanZiP1SignResponse.getCode());System.out.println("原子服務返回的簽名值:"+yuanZiP1SignResponse.getSignedData());//簽名后的byte[] dataHadSignature = Base64.decode(yuanZiP1SignResponse.getSignedData());YuanZi_VerifyP1SignRequest yuanZiVerifyP1SignRequest = new YuanZi_VerifyP1SignRequest();yuanZiVerifyP1SignRequest.setSignedData(yuanZiP1SignResponse.getSignedData());yuanZiVerifyP1SignRequest.setContentType("CT_BASE64_DATA");yuanZiVerifyP1SignRequest.setContent(base64ToSign);yuanZiVerifyP1SignRequest.setHashAlg("SHA1");yuanZiVerifyP1SignRequest.setCert(userCert.getCertBuf().getCertsignBuf());YuanZi_VerifyP1SignResponse yuanZiVerifyP1SignResponse = yuanZiService.verifyP1Sign(yuanZiVerifyP1SignRequest);System.out.println(yuanZiVerifyP1SignResponse.getCode());System.out.println(yuanZiVerifyP1SignResponse.getMessage());//本地驗簽boolean verify=false; signature.initVerify(publicKey); //使用公鑰初始化簽名對象,用于驗證簽名 //原子signature.update(dataToSign);verify= signature.verify(dataHadSignature); //得到驗證結果 System.out.println("本地的驗簽結果:"+verify);} catch (Exception e) {e.printStackTrace();// TODO: handle exception}}
P7簽名:
P7簽名其實也就是P1簽名的基礎上,附加一些其他的信息(因為P1是裸簽名,只有加密串,除了本人自己,根本不知道簽名證書,公鑰,簽名算法,簽名用的hash算法,時間戳,簽名原文等等信息是什么,不便于驗證簽名,所以就需要有P7簽名作為補充,其實也就是在P1之后,在加密串的基礎上,附加這些信息上去)
補充:
因為P7簽名值包含簽名證書,原文等信息,所以發送到服務器驗簽的時候,只需要把簽名值發給服務器即可,而p1,是裸簽名,發送到服務器驗證的時候,需要簽名證書,簽名值,和原文三個參數才能驗證
所以我們在itextpdf的源碼上能看到,其實它做簽名的時候,也就是先構建出一個要簽名的字符串,然后丟給某個私鑰進行簽名,獲得簽名值,它再對這個簽名值附加:證書鏈,時間戳等等信息,然后構建出一個P7簽名,然后放入PDF文件中.
PKCS#1:定義RSA公開密鑰算法加密和簽名機制,主要用于組織PKCS#7中所描述的數字簽名和數字信封[22]。
PKCS#7:定義一種通用的消息語法,包括數字簽名和加密等用于增強的加密機制,PKCS#7與PEM兼容,所以不需其他密碼操作,就可以將加密的消息轉換成PEM消息[26]。
?
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔為你收集整理的p1和p7签名的区别的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。