{% if theme.baidu_site_verification %} {% endif %}

对接签名验签服务器


前言

        公司现在弄的这个基于java开发的管理系统,上次对接的密码机,这次对接签名验签服务器,其实流程很类似:根据厂家提供的文档,调用厂家提供的Api接口,注意参数和返回值就行。硬件设备已经在公司的机房架设好了,硬件客户端可以登录上查看一些参数。但这里也遇到了很多坑,调试花了些时间。

文件上传代码

 基于ruoyi自带的FileUploadUtils工具类,有个upload通用的文件上传方法,要在这个功能基于这个方法添加上的一个附加的功能:上传一个文件同时,并且对这个文件的完整性做检验。那用什么做呢,公司领导要求调用签名验签服务器这个设备。

/**
     * 文件上传
     *
     * @param baseDir 相对应用的基目录
     * @param file 上传的文件
     * @param allowedExtension 上传文件类型
     * @return 返回上传成功的文件名
     * @throws FileSizeLimitExceededException 如果超出最大大小
     * @throws FileNameLengthLimitExceededException 文件名太长
     * @throws IOException 比如读写文件出错时
     * @throws InvalidExtensionException 文件校验异常
     */
    public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
            throws Exception {
        int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
        if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
        {
            throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
        }

        assertAllowed(file, allowedExtension);

        String fileName = extractFilename(file);
        //输入流
        InputStream in = file.getInputStream();
        //获取上传文件的绝对路径
        String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
        //输出流
        FileOutputStream fileOutputStream = new FileOutputStream(absPath);

        int n;
        byte[] bytes = new byte[4096];
        ByteArrayOutputStream out = null;
           //定义一个byte[],存储数据流文件
        byte[] bytes1=null;
        //前端传过来的输入流读取
        while((n=in.read(bytes))!=-1){
            out= new ByteArrayOutputStream();
            out.write(bytes,0,n);
            bytes1 = out.toByteArray();
        }
        //对数据流签名
        byte[] sign = SecurityUtils.sign(bytes1);
        //存入到硬盘
        fileOutputStream.write(sign);
        in.close();
        fileOutputStream.close();
        //定义文件类型,包含上传文件的地址
        File file1 = new File(absPath);
        int n1;
        FileInputStream fileInputStream = null;
        ByteArrayOutputStream out1= null;
        try {
            //定义一个输出流
            fileInputStream = new FileInputStream(file1);
            out1 = null;
            //签名值
            while ((n1 = fileInputStream.read(bytes)) != -1){
               out1= new ByteArrayOutputStream();
               //从刚上传的文件中,用流读取 
               out1.write(bytes,0,n1);
            }
            //数据类型转换:流转为byte[]
            byte[] bytes2 = out.toByteArray();
            System.out.println(Arrays.toString(bytes2));
            //验签,返回结果如果是true,验签成功,否则失败,byte2数据原文,out1数据原文签名后的值
            boolean result= SecurityUtils.verifySign(bytes2, out1.toByteArray());
            System.out.println(result);
            //校验失败,弹出提示
            if(result==false){
                throw new Exception("数据完整性校验失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("数据完整性校验失败");
        }finally{
            fileInputStream.close();
            out1.close();
        }
        return getPathFileName(baseDir, fileName);
    }

调用签名验签的代码

 以下是根据厂家提供的api方法,自己封装的工具方法,这里有一个bug,我自己单元测试的时候,方法传参很随意,用的String,结果String和byte []转换的有点冗余,把冗余的删了,代码也更清晰了,bug也解决了。这是我反复debug发现。只能说自己的技术还是很菜啊!

SecurityUtils.sign(byte[] b1)对数据流签名

SecurityUtils.verifySign(byte[] b2,byte[] b3) 对数据流验签

 /**
     * sm2签名,调用签名验签服务器
     * @param data 代签名的原文
     * @return 签名值
     */
    public static byte[]  sign(byte[] data){
        FmApi api = new FmApi();
        String ip = "xxx.xxx.xxx.xxx";
        String[] ips = new String[1];
        ips[0] = ip;
        api.FM_DSVS_Connection(ips, 2024, 3);
        System.out.println("连接成功");
        byte[] sign = api.FM_DSVS_SignData(xxx, xxx,xxx, xxx);
        System.out.println("签名成功");
        System.out.println(Arrays.toString(sign));
        return sign;
    }
 /**
     * sm2验签,调用签名验签服务器
     * @param data  代签名的原文
     * @param singData  签名值
     * @return
     */
    public static boolean verifySign(byte[] data,byte[] singData){
        // 7.2直接将证书放入验签
        FmApi api = new FmApi();
        String ip = "xxx.xxx.xxx.xxx";
        String[] ips = new String[1];
        ips[0] = ip;
        api.FM_DSVS_Connection(ips, 2024, 3);
        System.out.println("连接成功");
        //证书
        String cert = "xxxxxxxxxxxxxxx";
        int state = api.FM_DSVS_VerifySignedData(1, Base64.decode(cert.getBytes()), null, data, singData, 0);
        if (state == 0) {
            return true;
        } else {
            return false;
        }
    }

测试验证

 本地idea运行调试,在File file1 = new File(absPath);打断点,然后点击上传文件按钮,程序会在断点处停下。然后修改上传文件的内容,如图我是随意删除了两个字符。

测试

前端页面会弹出“数据完整性校验失败”

如果没有打断点,文件是可以直接上传成功的,这就是测试文件的完整性成功。

总结

 这节运用到了java基础数据流部分的知识。解决问题首先是理清思路,然后匹配技术解决方案。对接这种物理机的代码,和以前学习连接数据库JDBC很类似。只不过现在有些连接数据库框架比如Mybatis都太成熟了,封装迭代。从以上代码中可以看出,我的基础还是太薄弱了。再有就是现在只是做到最基本对接,实际企业级大项目,信息安全的软件业务也是很复杂的,比如签名验签服务器提供很多其它功能接口我都完全看不懂。


文章作者: 煜总
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 煜总 !
  目录