p1和p7签名的区别

前言:

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 
     */
    @Test
    public 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 
         */
        @Test
        public 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编码要解码)
         */
        @Test
        public 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]。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容