专利在线申请之入门到精通再到放弃

开通专利申请的在线模式是一种巨大的进步,虽然仅仅只是创新工作上的一小步。经通周末两天两夜的"研发",终于把专利在线申请流程和软件搞明白了。此文适合于懂.NET C#、证书、控件等技术的人员阅读,也适合不懂这些技术的人员阅读。

"研发"专利申请软件的使用技术是一种非常辛苦的工作,所以我觉得有必要记录下来,以后每年清明节可以来此瞻仰和扫墓。我对CPC软件的评价也是中肯的:它不是一堆屎,因为不仅仅是一堆屎!

一、环境搭建

专利申请软件简称CPC客户端,无需登录即可使用,签名的时候指定证书即可,所以CPC实际上就是一个专利案卷编辑器,经过测试发现,CPC在XP下运行状况相对比较良好(仅此而已),按以下配置部署软件环境:

操作系统:WindowsXP

public static bool IsUnsupported =>
            ((!IsWindowsXP && !IsWindows2003) && !IsWindows7);

.Net框架版本:最高3.5

IE版本:7、8、9(注:XP下无法安装IE9)

证书控件:http://interactive.cponline.cnipa.gov.cn/txnDownloadTl01.do?select-key:filename=OCX.zip

CPC客户端:http://cponline.sipo.gov.cn/nas/nas03/web/content/attachment/201202/14130914kpjg.rar

CPC更新程序:http://cponline.sipo.gov.cn/nas/nas03/web/content/attachment/201905/18095452b7az.zip

Office版本:2003或2007,高了不行

IE选项设置:给cponline.sipo.gov.cn和interactive.cponline.cnipa.gov.cn任何想要的权限,不得讲条件,包括但不限于允许未认证的控件下载或运行、加入信任站点、将安全等级能调多低就调多低。

你会问,没有XP怎么办?这个问题问得好,下载安装Oracle VM VirtualBox,虚拟一个XP。

专利在线申请之入门到精通再到放弃_第1张图片

二、安装软件及操作

安装CPC软件后根据自带说明安装更新程序,运行软件:

专利在线申请之入门到精通再到放弃_第2张图片

在网站http://cponline.sipo.gov.cn/注册一个个人或企业的账号,申请证书,整个流程网上申请网站的流程图是准确的。

由于IE下操作可能出现的问题多,对控件要求、安全要求也多,所以不在线上编译申请文件,全部在线下完成,但是证书也是要在网上下载的,必须。另外,要完整提交申请,线上必须用证书登录,但是一直没有办法使用证书登录,如下图:

专利在线申请之入门到精通再到放弃_第3张图片

不知道为什么检索不到证书,我查看源代码,它是用控件检索证书:


    
    
//初始化数据
		jq(function() {
			pageSetup_RegSet();
			//获取登录地址
			func_getDwfwUri();
			//初始化设置账号登录验证码
			func_setDefaultCode(1);
			//控件初始化
			ctl.onInit();
			plugin = ctl;
			//初始化证书列表
			certs = plugin_GetCertificates(ctl);
			if (certs == null || certs.length == 0) {
				jq("#certificate").append("");
			} else {
				for (var i = 0; i < certs.length; i++) {
					var cert = certs[i];
					var strCertNumber = cert.SerialNumber;
					var strCertCn = cert.CommonName + "";
					jq("#certificate").append(
							"");
				}
			}
			checkKeyType(jq('#certificate')[0]);
		});

查了一下,application/x-causn是指控件

专利在线申请之入门到精通再到放弃_第4张图片

就是“C:\Program Files (x86)\kairende\CA证书控件x86”下的那堆东西!

要是检索不到证书,分析代码就无能为力了,我保证,证书是安装了的。

专利在线申请之入门到精通再到放弃_第5张图片

由此可见,当时是用pfx文件安装的,它包含了私钥。可恨的是,当时安装证书的时候没有允许导出私钥,pfx文件找不到了,只能导出cer证书,这种证书没有带私钥,在别的电脑上后,也会检测不到证书

回过头来,登录网站,下载证书!再啰嗦一下,切记保存pfx到硬盘,并且按照他的指示,保存到证书安装目录。

专利在线申请之入门到精通再到放弃_第6张图片

下载一次后,就不再允许下载了,这个坑让我废了两个证件号。如果你说没有废,请教我怎么重新下载证书!?我本以为作废证书以后就可以重新申请证书,然并卵!

三、制作专利申请文件

打开CPC客户端,设置网络服务器

专利在线申请之入门到精通再到放弃_第7张图片

选择一个能通的服务器。

经过测试,如果你勾选https,可能无法连接服务器。

专利在线申请之入门到精通再到放弃_第8张图片

证书管理,选择一个证书。

专利在线申请之入门到精通再到放弃_第9张图片

现在可以开始制作专利申请文件了,申请什么类型专利可以根据自己的需要选择。

专利在线申请之入门到精通再到放弃_第10张图片

进入案卷文件编辑器。

专利在线申请之入门到精通再到放弃_第11张图片

有两个钉子问题,需要单独拿出来说:

1.上传图片格式和尺寸问题

上CPC源代码先,怎么解决就不用多说了:

string str6 = Path.GetExtension(str3).Replace(".", "");
                        if (str5 != str6)
                        {
                            this.dtdErr = string.Concat(new object[] { "Validation", wj.WENJIANMC, "第", i + 1, "幅图片", innerText, "的文件类型与list.xml文件中的描述不一致!请重新编辑图片后再签名打包。", Environment.NewLine, this.dtdErr });
                        }
                        else if ((((str5.ToLower() != "jpg") && (str5.ToLower() != "jpeg")) && (str5.ToLower() != "tiff")) && (str5.ToLower() != "tif"))
                        {
                            if ((wj.BEIZHU.ToUpper() != "PDF") || (str5.ToUpper() != "PDF"))
                            {
                                this.dtdErr = string.Concat(new object[] { "Validation", wj.WENJIANMC, "第", i + 1, "幅图片", innerText, "是", str5, "格式!请插入jpg、jpeg、tiff或tif格式后再签名打包。", Environment.NewLine, this.dtdErr });
                            }
                        }
                        else
                        {
                            box.Load(str3);
                            image = box.Image;
                            num2 = ((float.Parse(image.Width.ToString()) * 2.54) * 10.0) / ((double) image.HorizontalResolution);
                            num = ((float.Parse(image.Height.ToString()) * 2.54) * 10.0) / ((double) image.VerticalResolution);
                            if ((image.HorizontalResolution > 300f) || (image.VerticalResolution > 300f))
                            {
                                this.dtdErr = string.Concat(new object[] { "Validation", wj.WENJIANMC, "第", i + 1, "幅图片", innerText, " DPI大于300,请修改后再签名打包。", Environment.NewLine, this.dtdErr });
                                str2 = "error";
                            }
                            else
                            {
                                if (str3.IndexOf("130001") >= 0)
                                {
                                    if ((num2 > 150.0) || (num > 220.0))
                                    {
                                        this.dtdErr = string.Concat(new object[] { "Validation", wj.WENJIANMC, "第", i + 1, "幅图片", innerText, "超出150mm x 220mm!请验证后再签名打包。", Environment.NewLine, this.dtdErr });
                                        str2 = "error";
                                        continue;
                                    }
                                }
                                else if ((num2 > 165.0) || (num > 245.0))
                                {
                                    this.dtdErr = string.Concat(new object[] { "Validation", wj.WENJIANMC, "第", i + 1, "幅图片", innerText, "超出165mm x 245mm!请验证后再签名打包。", Environment.NewLine, this.dtdErr });
                                    str2 = "error";
                                    continue;
                                }
                                if (((str5 == "tif") || (str5 == "tiff")) && !clsImageHandle.IsCCITT4Image(str3))
                                {
                                    this.dtdErr = string.Concat(new object[] { "Validation", wj.WENJIANMC, "第", i + 1, "幅图片", innerText, " 不是Group 4 压缩方式的TIF文件!请修改验证后再签名打包。", Environment.NewLine, this.dtdErr });
                                    str2 = "error";
                                }
                            }
                        }

所以制作附图的时候,图片全部用灰度,不要用RGB模式,假如是300分辨率,图片宽<1948px,高小于3000px就行了, 至于其它分辨率,自己再根据上图公式去算一下吧。另外,如果你上传TIFF图片,要用Group 4压缩,用ACDSee10免费版就很好压缩。

2.签名失败问题

private bool Sign()
{
    StreamWriter writer;
    AnJuan aJ = this.SQLF.AJ;
    string path = Program.TEMP_ROOT_PATH + this.ZippedPath + @"zip\" + aJ.ANJUANBH.ToString() + ".zip";
    if (Settings.Default.IsRemPassword)
    {
        string str2 = this.sign.SignFile(path);
        if (string.IsNullOrEmpty(str2))
        {
            return false;
        }
        writer = new StreamWriter(Program.TEMP_ROOT_PATH + this.ZippedPath + @"zip\" + aJ.ANJUANBH.ToString() + "_ca.txt", true);
        writer.WriteLine(str2);
        writer.Close();
        if (!this.sign.VerifyFile(path, str2))
        {
            return false;
        }
        return true;
    }
    string str4 = Program.TOOLS_ROOT_PATH + "testsign.exe";
    SignFileReturnClass class2 = this.sign.SignFile(path, str4, pub.GetIssuerDn());
    if (class2.ReturnCode != "00")
    {
        return false;
    }
    writer = new StreamWriter(Program.TEMP_ROOT_PATH + this.ZippedPath + @"zip\" + aJ.ANJUANBH.ToString() + "_ca.txt", true);
    writer.WriteLine(class2.ReturnString);
    writer.Close();
    if (!this.sign.VerifyFile(path, class2.ReturnString))
    {
        return false;
    }
    return true;
}

看到没,知道为什么签名错误了吧!?为此我写了一个小工具,验证一下签名看有什么问题,以下是代码片断:

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            openFileDialog1.Title = "请选择有效的案卷文件(zip)";
            openFileDialog1.Filter = "案卷文件|*.zip";
            openFileDialog1.FileName = "";
            DialogResult dr = openFileDialog1.ShowDialog(this);
            if (dr == DialogResult.OK)
            {
                bool rs = Sign(openFileDialog1.FileName);
                if (rs)
                {
                    label1.Text = "签名结果:" + rs.ToString();
                }
            }
        }

        private bool Sign(string path)
        {
            try
            {
                FileInfo file = new FileInfo(path);
                string aj = file.Name.Substring(0, file.Name.Length - 4);
                StreamWriter writer;
                listBox1.Items.Add("开始准备签名...");
                listBox1.Items.Add("Path..." + path);
                SignClass sign = new SignClass(GetIssuerDn());
                bool s1 = sign.SetCurrentCert(0);
                listBox1.Items.Add("证书数..." + sign.GetCertsCount());
                listBox1.Items.Add("设置当前证书..." + s1.ToString());
                string str2 = sign.SignFile(path);
                listBox1.Items.Add("签名值长度..." + str2.Length);
                if (string.IsNullOrEmpty(str2))
                {
                    listBox1.Items.Add("签名错误代码:" + sign.cur_cert.GetLastErr());
                    return false;
                }
                writer = new StreamWriter(file.DirectoryName + "\\" + aj + "_ca.txt", true);
                writer.WriteLine(str2);
                writer.Close();
                if (!sign.VerifyFile(path, str2))
                {
                    listBox1.Items.Add("验证签名...失败!");
                    return false;
                }
                return true;
            }
            catch (Exception ex)
            {
                listBox1.Items.Add("签名异常..." + ex.ToString());
                return false;
            }
        }

        private string GetIssuerDn()
        {
            return "CN=国家知识产权局CA证书, OU=国家知识产权局, O=国家知识产权局, L=BeiJing, S=BeiJing, C=CN";
        }

        private string GetIssuerDn_CN()
        {
            return "国家知识产权局CA证书";
        }
    }

我可以再完善一下,使用  testsign.exe 再测试一下,这货应该能够返回为什么不能签名成功,懒得试了,谁有空谁试!

过了五分钟,强迫症发作了。。。

你们不用试了,代码跟SignClass一样,也不知道是谁抄谁!

   using DS20SignLib;
    using SignTest.Utility;
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;

    public class SignClass
    {
        private IDS20SignCtl sign = new DS20SignCtlClass();
        private Certificates certs;
        public Certificate cur_cert;

        public SignClass(string strIssuerDn)
        {
            DS20SignLib.Filter filter = this.sign.Filter;
            filter.Clear();
            filter.Issuer = strIssuerDn;
            this.certs = this.sign.Certificates;
        }

        public int GetCertsCount() =>
            this.certs.Count;

        public string[] GetCommandNameList()
        {
            int count = this.certs.Count;
            if (count <= 0)
            {
                return null;
            }
            string[] strArray = new string[count];
            for (int i = 0; i < count; i++)
            {
                strArray[i] = ((Certificate)this.certs[i]).CommonName;
            }
            return strArray;
        }

        public Dictionary GetCurCertInfo()
        {
            if (this.cur_cert == null)
            {
                throw new Exception("没有指定证书");
            }
            return new Dictionary {
                {
                    "CommonName",
                    this.cur_cert.CommonName
                },
                {
                    "Subject",
                    this.cur_cert.Subject
                },
                {
                    "Issuer",
                    this.cur_cert.Issuer
                },
                {
                    "SerialNumber",
                    this.cur_cert.SerialNumber
                },
                {
                    "ValidFrom",
                    this.cur_cert.ValidFrom.ToString("yyyy年MM月dd日")
                },
                {
                    "ValidTo",
                    this.cur_cert.ValidTo.ToString("yyyy年MM月dd日")
                }
            };
        }

        public string GetCurCertName()
        {
            if (this.cur_cert == null)
            {
                throw new Exception("没有指定证书");
            }
            foreach (string str2 in this.cur_cert.Subject.Split(new char[] { ',' }))
            {
                string str3 = str2.Substring(0, str2.IndexOf("="));
                string str4 = str2.Substring(str2.IndexOf("=") + 1);
                if (((str3.Trim().ToLower() == "ou") && (str4.Trim().ToLower() != "zljca")) && (str4.Trim().ToLower() != "国家知识产权局"))
                {
                    return str4;
                }
            }
            return "";
        }

        public bool IsVaild()
        {
            if (this.cur_cert == null)
            {
                throw new Exception("没有指定证书");
            }
            return (this.cur_cert.ValidTo >= DateTime.Now);
        }

        public bool SetCurrentCert(int index)
        {
            if (this.certs.Count <= 0)
            {
                throw new Exception("没有找到证书");
            }
            if (this.certs[index] == null)
            {
                throw new Exception("指定证书越界");
            }
            this.cur_cert = (Certificate)this.certs[index];
            return (this.cur_cert != null);
        }

        public bool SetCurrentCert(string commonname)
        {
            if (this.certs.Count <= 0)
            {
                throw new Exception("没有找到证书");
            }
            for (int i = 0; i < this.certs.Count; i++)
            {
                Certificate certificate = (Certificate)this.certs[i];
                if (certificate.CommonName == commonname)
                {
                    this.cur_cert = certificate;
                    break;
                }
            }
            return (this.cur_cert != null);
        }

        public string SignFile(string path)
        {
            if (this.cur_cert == null)
            {
                throw new Exception("没有指定证书");
            }
            return this.cur_cert.EnvSignFile(path, 0);
        }

        public SignFileReturnClass SignFile(string path, string sign_exe_path, string IssuerDn)
        {
            SignFileReturnClass class2;
            if (this.cur_cert == null)
            {
                throw new Exception("没有指定证书");
            }
            if (!File.Exists(sign_exe_path))
            {
                throw new Exception("签名验证程序文件不存在");
            }
            string fileName = "\"" + sign_exe_path + "\"";
            string arguments = "\"" + this.cur_cert.CommonName + "\" \"" + IssuerDn + "\" \"" + path + "\"";
            ProcessStartInfo startInfo = new ProcessStartInfo(fileName, arguments)
            {
                RedirectStandardOutput = true,
                RedirectStandardInput = true,
                CreateNoWindow = true,
                UseShellExecute = false
            };
            string str3 = "";
            try
            {
                Process process = Process.Start(startInfo);
                str3 = process.StandardOutput.ReadToEnd();
                process.WaitForExit();
                if (string.IsNullOrEmpty(str3.Trim()))
                {
                    throw new Exception("没有获取到返回值");
                }
                string returncode = str3.Substring(0, 2);
                string returnstring = str3.Substring(3);
                class2 = new SignFileReturnClass(returncode, returnstring);
            }
            catch (Exception exception)
            {
                throw exception;
            }
            return class2;
        }

        public string SignString(string strChars)
        {
            if (this.cur_cert == null)
            {
                throw new Exception("没有指定证书");
            }
            return this.cur_cert.Sign(strChars);
        }

        public bool VerifyFile(string path, string encryptString) =>
            (this.sign.VerifyEnvFile(encryptString, 0, path) != null);
    }

    namespace Utility
    {
        public class SignFileReturnClass
        {
            private string returncode;
            private string returnstring;

            public SignFileReturnClass(string returncode, string returnstring)
            {
                this.returncode = returncode;
                this.returnstring = returnstring;
            }

            public string ReturnCode
            {
                get =>
                    this.returncode;
                set =>
                    returncode = value;
            }

            public string ReturnString
            {
                get =>
                    this.returnstring;
                set =>
                    returnstring = value;
            }
        }
    }

 需要引用CPC目录下的 Ds20Sign.dll 文件,这个不是.net链接库,直接添加引用就好了。

用自己的代码签名看看:

专利在线申请之入门到精通再到放弃_第12张图片

真的签名失败,而这个错误码是 Ds20Sign.dll 里报出来的,没办法,只能认为是证书的问题了,这个签名只是本地签名,似乎没有与CPC服务器通讯,暂且认为是本地证书的问题吧!签名测试我使用了真实的案卷文件,在Temp/Upload/zip目录下,签名成功的话会生成一个签名文件,这是CPC的逻辑,我只是照搬下来!

理论上找http://www.kairende.com/可以解决返回10001的错误原因,从这公司的网站可以看得出来,没有技术支持费用估计人家不会提供文档或是理你!专利局开发CPC那帮人购买了他们的CA产品?

假设你都已经编辑完成了,现在可以签名提交了。

专利在线申请之入门到精通再到放弃_第13张图片

如果证书没有问题,签名通过之后就是打包上传了,上传成功,恭喜你,完成专利申请的递交了,等待受理!

四,想对专利局那帮程序员说点什么~!

1.重写代码,并使用.net 4.6以上版本开发,明年.Net 5都快出来了;

2.别用DevExpress了吧,有花钱买吗?

3.可以多用开源的组件,如NOPI,这样可以不用依赖MS Office,现在电脑我都不安装Office家庭了,用WPS足够能够应付日常工作。

4.网站功能重写,兼容IE\Firefox\Safari\Chrome等。为什么登录总是检测不到证书?为什么IE问题验证码错误?放弃Ds20Sign.dll换个别的方案吧!?证书签名的方案很多的啊!最好是有开源的!

5.招一个月薪超过5000的程序员开发!

6.再招个产品经理,规划一下软件功能!

7.招一个技术总监,将系统重新架构一下!

祝你们好运!别再开发出一堆屎,影响国家形象!

对了,你会问我后来是怎么申请发明专利的。我放弃软件递交了,因为签名错误是一个不可逾越的问题,再加上我也没有办法重新下载证书,还能怎么办!?经过两天熬夜到4点,最后还是打印申请文件,邮递到代办点了!~

我不打算找中介,没钱!

 

你可能感兴趣的:(杂谈)