jjzjj

java - 使用充气城堡使用中间证书正确创建新证书

coder 2024-03-30 原文

所以我的问题如下,

基本上我想使用充气城堡(jdk16 版本 1.46)创建一个证书链。我对充气城堡和 java.security 总体上还是比较陌生的,所以如果我的方法可能完全错误,但无论如何这就是我所做的:

到目前为止,我能够创建一个自签名证书,用作根证书。这是使用以下代码完成的:

//-----create CA certificate with key
KeyPair caPair = Signing.generateKeyPair("DSA", 1024, null, null);

这基本上创建了 key 对,如果需要,两个空选项用于提供者和安全随机数。
Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> caMap = new HashMap<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>>();
caMap.put(X509Extensions.BasicConstraints, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(true, new BasicConstraints(true)));

//------this creates the self signed certificate        
X509Certificate caCert = X509CertificateGenerator.generateX509Certificate(serial, "CN=CA", "CN=CA", start, end, "SHA1withDSA", caPair.getPrivate(), caPair.getPublic(), null, caMap);

这将使用提供的属性创建证书。
  • serial = 以毫秒为单位的当前时间
  • start = 与串行基本相同(可能有 1 或 2 毫秒的差异)
  • 结束 = 开始 + 2 天

  • 该映射只是添加了基本约束来将证书设置为 CA。我在这里使用 map ,因为我希望能够在需要时添加额外的 X509Extensions。
    //-----save ca certificate in PEM format
    X509CertificateGenerator.savePemX509Certificate(caCert, caPair.getPrivate(), caWriter);
    

    这将使用 bouncy caste pem writer 将证书和私钥存储在 pem 文件中。

    之后生成文件,我也可以安装该文件(我使用 IE,然后通过 Internet 选项将其安装为受信任的 CA。证书也显示为有效)。

    之后,我使用以下代码创建中间证书(请注意,上述代码在同一范围内,因此这些变量也可用)
    KeyPair intermediatePair = Signing.generateKeyPair("DSA", 1024, null, null);    
    
    Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> intermediateMap = new HashMap<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>>();
    intermediateMap.put(X509Extensions.AuthorityKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new AuthorityKeyIdentifierStructure(caCert)));
    intermediateMap.put(X509Extensions.SubjectKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new SubjectKeyIdentifierStructure(intermediatePair.getPublic())));
    
    X509Certificate intermediateCert = X509CertificateGenerator.generateX509Certificate(serial.add(BigInteger.valueOf(1l)), "CN=intermediate", caCert.getSubjectX500Principal().toString(), start, end, "SHA1withDSA", caPair.getPrivate(), intermediatePair.getPublic(), null, intermediateMap);   
    
    //-----save intermediate certificate in PEM format
    X509CertificateGenerator.savePemX509Certificate(intermediateCert, intermediatePair.getPrivate(), intermediateWriter);
    

    过程基本相同,但我添加了额外的 X509Extensions:
  • X509Extensions.AuthorityKeyIdentifier = 将 CA 证书设置为中间父级
  • X509Extensions.SubjectKeyIdentifier = 使用生成的证书公钥

  • 此外,CA 用作颁发者,CA 私钥用于创建中间证书。

    这也有效,我可以安装中间证书(再次使用 IE),还显示父证书是生成的 CA 证书并且证书有效。

    现在到了棘手的部分,我猜我犯了一个错误。我现在使用中间证书创建一个新证书,使用以下代码。
    KeyPair endPair = Signing.generateKeyPair("DSA", 1024, null, null);
    
    Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> endMap = new HashMap<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>>();
    endMap.put(X509Extensions.AuthorityKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new AuthorityKeyIdentifierStructure(intermediateCert)));
    endMap.put(X509Extensions.SubjectKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new SubjectKeyIdentifierStructure(endPair.getPublic())));
    
    X509Certificate endCert = X509CertificateGenerator.generateX509Certificate(serial.add(BigInteger.valueOf(1l)), "CN=end", intermediateCert.getSubjectX500Principal().toString(), start, end, "SHA1withDSA", intermediatePair.getPrivate(), endPair.getPublic(), null, endMap);
    
    X509CertificateGenerator.savePemX509Certificate(endCert, endPair.getPrivate(), endWriter);
    

    本质上它与创建中间证书相同。但是我现在使用以下 X509Extension 设置:
  • X509Extensions.AuthorityKeyIdentifier = 将中间证书设置为证书父级
  • X509Extensions.SubjectKeyIdentifier = 使用生成的证书公钥

  • 此外,中间证书用作颁发者,其私钥用于创建证书。

    我也可以安装新证书,但是当我检查是否(再次 IE)时,它显示该证书无效,因为“此 CA 要么无权颁发证书,要么该证书不能用作最终实体。”

    因此,我以某种方式需要通过添加一些我假设的 KeyUsages/ExtendedKeyUsage 来使中间证书也能够创建新证书。

    有人知道我如何启用中间证书来做我需要它做的事情,或者我做错了什么?

    编辑 1:

    好吧,我忘了提供创建证书的方法的代码以及将它保存为 PEM 格式的方法的代码(我将其重命名为 savePemX509Certificate,因为旧的存在误导)。

    证书生成代码:
    public static X509Certificate generateX509Certificate(BigInteger serialnumber, String subject, String issuer, Date start , Date end, String signAlgorithm, PrivateKey privateKey, PublicKey publicKey, String provider, Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> map) throws CertificateEncodingException, InvalidKeyException, IllegalStateException, NoSuchProviderException, NoSuchAlgorithmException, SignatureException
    {
        if(serialnumber!=null && subject!=null && issuer!=null && start!=null && end!=null && signAlgorithm !=null && privateKey!=null && publicKey!=null)
        {
            //-----GENERATE THE X509 CERTIFICATE
            X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
            X509Principal dnSubject = new X509Principal(subject);
            X509Principal dnIssuer = new X509Principal(issuer);
    
            certGen.setSerialNumber(serialnumber);
            certGen.setSubjectDN(dnSubject);
            certGen.setIssuerDN(dnIssuer);
            certGen.setNotBefore(start);
            certGen.setNotAfter(end);
            certGen.setPublicKey(publicKey);
            certGen.setSignatureAlgorithm(signAlgorithm);
    
            //-----insert extension if needed
            if(map!=null)
                for(ASN1ObjectIdentifier extension : map.keySet())
                    certGen.addExtension(extension, map.get(extension).getKey(), map.get(extension).getValue());
    
            return certGen.generate(privateKey, provider);  
        }
        return null;
    }
    

    保存证书和 key 的代码:
    public static boolean savePemX509Certificate(X509Certificate cert, PrivateKey key, Writer writer) throws NoSuchAlgorithmException, NoSuchProviderException, CertificateEncodingException, SignatureException, InvalidKeyException, IOException 
    {       
        if(cert!=null && key!=null && writer!=null)
        {               
            PEMWriter pemWriter = new PEMWriter(writer);
            pemWriter.writeObject(cert);
            pemWriter.flush();
    
            if(key!=null)
            {
                pemWriter.writeObject(key);
                pemWriter.flush();
            }
            pemWriter.close();
            return true;
            }
        return false;
    }
    

    如您所见,我基本上将证书和 key 放在文件中,仅此而已。结果如下,对我来说似乎很好。
    -----BEGIN CERTIFICATE-----
    MIICdjCCAjagAwIBAgIGAUDuXLRLMAkGByqGSM44BAMwDTELMAkGA1UEAwwCQ0Ew
    HhcNMTMwOTA1MTM0MzA3WhcNMTMwOTA3MTM0MzA3WjANMQswCQYDVQQDDAJDQTCC
    AbcwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADD
    Hj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gE
    exAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/Ii
    Axmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4
    V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozI
    puE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4Vrl
    nwaSi2ZegHtVJWQBTDv+z0kqA4GEAAKBgAeFoGATLbIr8+QNuxcbYJ7RhbefKWSC
    Br67Pp4Ynikxx8FZN4kCjGX7pwT1KffN3gta7jxIXNM5G3IFbs4XnYljh5TbdnjP
    9Ge3kxpwncsbMQfCqIwHh8T5gh55KaxH7yYV2mrtEEqj7NBL4thQhJe2WGwgkB9U
    NxNmLoMq3m4poyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAJ
    BgcqhkjOOAQDAy8AMCwCFFm5ybLY09y8y2uGsEnpceffy2KaAhQIyshgy3ohCLxQ
    q3CmnvC+cfT2VQ==
    -----END CERTIFICATE-----
    -----BEGIN DSA PRIVATE KEY-----
    MIIBuwIBAAKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR
    +1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb
    +DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdg
    UI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlX
    TAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCj
    rh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQB
    TDv+z0kqAoGAB4WgYBMtsivz5A27FxtgntGFt58pZIIGvrs+nhieKTHHwVk3iQKM
    ZfunBPUp983eC1ruPEhc0zkbcgVuzhediWOHlNt2eM/0Z7eTGnCdyxsxB8KojAeH
    xPmCHnkprEfvJhXaau0QSqPs0Evi2FCEl7ZYbCCQH1Q3E2YugyrebikCFDJCJHtt
    NWB4LWYc4y4QvJ/l46ap
    -----END DSA PRIVATE KEY-----
    

    因此,在 gtrig 为我提供了创建证书的正确方法后,我最终使用此方法创建了普通或自签名(如果私钥与公钥来自相同的 keyPair)证书
    public static X509Certificate createX509V3Certificate(X500Principal name, BigInteger serial, Date start, Date end, PublicKey pubKey, String algorithm, PrivateKey privateKey, Map<ASN1ObjectIdentifier, Entry<Boolean, ASN1Object>> map, X509Certificate parentCert) throws IOException, OperatorCreationException, CertificateException
    {
        if(serial!=null && start!=null && end!=null && name!=null && pubKey!=null && algorithm!=null && privateKey!=null)
        {
            ContentSigner signer = new JcaContentSignerBuilder(algorithm).build(privateKey);
            X509v3CertificateBuilder certBldr = null;
            if(parentCert==null)
                certBldr = new JcaX509v3CertificateBuilder(name, serial, start, end, name, pubKey);
            else
                certBldr = new JcaX509v3CertificateBuilder(parentCert, serial, start, end, name, pubKey);
    
            if(map!=null)
                for(ASN1ObjectIdentifier extension : map.keySet())
                    certBldr.addExtension(extension, map.get(extension).getKey(), map.get(extension).getValue());
    
            return new JcaX509CertificateConverter().setProvider("BC").getCertificate(certBldr.build(signer));  
        }
        return null;
    }
    

    最佳答案

    您创建 PEM 文件的方式似乎有问题。您正在使用一种名为 generateSelfSignedPemX509Certificate 的方法,但你并不真正想要一个自签名证书,你想要一个由中间私钥签名的最终证书,并且你想要一个由 CA 私钥签名的中间证书。

    另外,您需要basic constraintskey usage证书上的扩展名。

    为了创建由其他实体(非自签名)签名的证书,我使用 Bouncy CaSTLe 中的这些方法来创建“结束”证书。

      ASN1Sequence seq= 
         (ASN1Sequence) new ASN1InputStream(parentPubKey.getEncoded()).readObject();
    
      SubjectPublicKeyInfo parentPubKeyInfo = new SubjectPublicKeyInfo(seq);
    
      ContentSigner signer = new JcaContentSignerBuilder(algorithm).build(parentPrivKey);
    
      X509v3CertificateBuilder certBldr = 
         new JcaX509v3CertificateBuilder(
            parentCert, 
            serialNum,
            startDate, 
            endDate, 
            distName, 
            pubKey)
         .addExtension(
               new ASN1ObjectIdentifier("2.5.29.35"),
               false,
               new AuthorityKeyIdentifier(parentPubKeyInfo))
         .addExtension(
            new ASN1ObjectIdentifier("2.5.29.19"), 
            false,
            new BasicConstraints(false)) // true if it is allowed to sign other certs
         .addExtension(
            new ASN1ObjectIdentifier("2.5.29.15"),
            true,
            new X509KeyUsage(
               X509KeyUsage.digitalSignature |
               X509KeyUsage.nonRepudiation   |
               X509KeyUsage.keyEncipherment  |
               X509KeyUsage.dataEncipherment));
    
      // Build/sign the certificate.
      X509CertificateHolder certHolder = certBldr.build(signer);
    
      X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC)
         .getCertificate(certHolder);
    

    对于 CA 或中间证书,您需要添加 SubjectKeyIdentifier延期。另外,BasicConstraints应该是 true , KeyUsage 应该是:
            new X509KeyUsage(
               X509KeyUsage.keyCertSign|
               X509KeyUsage.cRLSign));
    

    关于java - 使用充气城堡使用中间证书正确创建新证书,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18633273/

    有关java - 使用充气城堡使用中间证书正确创建新证书的更多相关文章

    1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

      我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

    2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

      我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

    3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

      类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

    4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

      很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

    5. ruby - 在 Ruby 中使用匿名模块 - 2

      假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

    6. ruby - 如何在 Ruby 中顺序创建 PI - 2

      出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

    7. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

      我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

    8. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

      关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

    9. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

      我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

    10. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

      我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

    随机推荐