【问题标题】:JAVA API to create a keystore and attaching a csr and keypair to it用于创建密钥库并将 csr 和密钥对附加到它的 JAVA API
【发布时间】:2016-10-31 21:09:08
【问题描述】:

我需要将现有的 csr 和密钥对附加到密钥库。下面给出的是一个使用 GUI(java swing) 获取用户输入的实现,例如密钥库名称、别名、通用名称、组织等。

我尝试使用 keystore.setkeyentry(...) 将 csr 链接到密钥库,但是密钥库仍然是空的。

我在下面附上了我的代码,任何帮助都会非常有用:

下面这段代码是用来创建csr的

            public String getCSR(String cn, String ou, String o, String l,String s) throws Exception {
            byte[] csr = generatePKCS10(cn, ou, o, l,s,"US");

            return new String(csr);
        }


private static byte[] generatePKCS10(String CN, String OU, String O,
                String L, String S, String C) throws Exception {
            // generate PKCS10 certificate request
            String sigAlg = "MD5WithRSA";
            PKCS10 pkcs10 = new PKCS10(publicKey);
            Signature signature = Signature.getInstance(sigAlg);
            signature.initSign(privateKey);
            // common, orgUnit, org, locality, state, country
            X500Principal principal = new X500Principal( "CN=Ole Nordmann, OU=ACME, O=Sales, C=NO");

       //     pkcs10CertificationRequest kpGen = new PKCS10CertificationRequest(sigAlg, principal, publicKey, null, privateKey);  
         //   byte[] c = kpGen.getEncoded();  
            X500Name x500name=null;
            x500name= new X500Name(principal.getEncoded());
          pkcs10.encodeAndSign(x500name, signature);
            ByteArrayOutputStream bs = new ByteArrayOutputStream();
            PrintStream ps = new PrintStream(bs);
            pkcs10.print(ps);
            byte[] c = bs.toByteArray();
            try {
                if (ps != null)
                    ps.close();
                if (bs != null)
                    bs.close();
            } catch (Throwable th) {
            }
            return c;
        }


        public static X509Certificate generateX509Certificate(String certEntry) throws IOException {

            InputStream in = null;
            X509Certificate cert = null;
            try {
                byte[] certEntryBytes = certEntry.getBytes();
                in = new ByteArrayInputStream(certEntryBytes);
                CertificateFactory certFactory = CertificateFactory.getInstance("X.509");

                cert = (X509Certificate) certFactory.generateCertificate(in);
            } catch (CertificateException ex) {

            } finally {
                if (in != null) {
                        in.close();
                }
            }
            return cert;
        }

在主要方法中,我执行以下操作来创建密钥库并将其附加到 csr

KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());


                        char[] pass = password.toCharArray();
                        ks.load(null, pass);

                        ks.store(fos, pass);
                        fos.close();
                         GenerateCSR gcsr = GenerateCSR.getInstance();

                    System.out.println("Public Key:\n"+gcsr.getPublicKey().toString());

                    System.out.println("Private Key:\n"+gcsr.getPrivateKey().toString());

                    String csr = gcsr.getCSR(CN,OU,O,L,S);

                    System.out.println("CSR Request Generated!!");
                    System.out.println(csr);


                    X509Certificate[] certChain = new X509Certificate[1];
                 //   certChain[0]= gcsr.generateX509Certificate(csr);
                    X509Certificate myCert = (X509Certificate) CertificateFactory
                                                 .getInstance("X509")
                                                 .generateCertificate(
                                                    // string encoded with default charset
                                                    new ByteArrayInputStream(csr.getBytes())
                                                 ); 
                    certChain[0]= myCert;
                    ks.setKeyEntry("alias", (Key)gcsr.getPrivateKey(), pass, certChain);

当我检查密钥库的内容时,它是空的。 任何建议将不胜感激

谢谢你!!!

【问题讨论】:

  • 您可能会从示例中删除所有导入语句,并完全删除 GUI 配对器,只留下一个生成 keyPair 和 CSR 并演示使用 '.setKeyEntry()' 如何失败的示例将密钥添加到商店。 (这将使回答问题更容易:))
  • 很好的建议!!谢谢

标签: java security bouncycastle keytool csr


【解决方案1】:

你有两个主要错误

  • 证书签名请求又名 CSR 又名 PKCS10 不是证书CertificateFactory.generateCertificate 只会读取证书而不是 CSR,当您向其提供 CSR 时,它会引发异常,您的代码会巧妙地抑制该异常,而不会向任何人表明存在严重问题。您在早期版本中注释掉的代码更接近生成证书所需的代码。

  • (如果您确实创建/拥有有效证书)KeyStore.set* 仅在内存中的 KeyStore 对象中设置条目。如果您希望在程序退出后将密钥库内容保存在某个文件中,您必须在执行“设置”后store

这是您的代码修改得足够多,我相信它可以按照您的意愿工作。除了琐碎的格式和脚手架外,我更改的点标记为//-- 用于删除,//** 用于添加。尽管如此,我还是不推荐它,因为:

  • 我继续使用不受支持的 sun.security 类,即使您使用的是 BC 并且它支持 PKCS10 和相关位的类,而且只有当您想从加利福尼亚州;自己生成证书直接生成证书更容易

  • (不太严重)在最近版本的 BC pkix 中已被拆分到一个单独的 jar 中,X509V3CertificateGenerator 现在已弃用,取而代之的是 X509v3CertificateBuilder


//nopackage
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.*;
import java.util.*;
import javax.security.auth.x500.*;

import org.bouncycastle.jce.X509Principal;
import org.bouncycastle.x509.X509V3CertificateGenerator;

//--import sun.security.pkcs.PKCS10; -- Java7
import sun.security.pkcs10.PKCS10; //** Java8
import sun.security.x509.X500Name;

public class SO40350607GenerateCertIntoKeystoreFile8 {
    public static void main (String[] args) throws Exception {
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        //**dummy value for test
        KeyPairGenerator kpgen = KeyPairGenerator.getInstance("RSA"); 
        kpgen.initialize(1024); keyPair = kpgen.generateKeyPair();

        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        char[] pass = "password".toCharArray();
        ks.load(null, pass);
        //--ks.store(fos, pass);    useless here
        //--fos.close();

        String csr = new String(generatePKCS10("CommonName","OrgUnit","Org","Locality","State", "US"));
        System.out.println("CSR Request Generated!!");
        System.out.println(csr);

        //--X509Certificate myCert = (X509Certificate) CertificateFactory.getInstance("X509")
        //--    .generateCertificate(new ByteArrayInputStream(csr.getBytes()) ); // string encoded with default charset*/
        X509Certificate myCert = generateCertificate2 (csr); //**
        X509Certificate[] certChain = new X509Certificate[]{myCert};
        ks.setKeyEntry("alias", keyPair.getPrivate(), pass, certChain);
        FileOutputStream fos = new FileOutputStream ("newksfile");
        ks.store(fos,pass); fos.close(); //** NOW store to file
    }
    private static KeyPair keyPair;

    private static byte[] generatePKCS10(String CN, String OU, String O,
            String L, String S, String C) throws Exception {
        // generate PKCS10 certificate request
        String sigAlg = "SHA1WithRSA"; //** don't use "MD5WithRSA" even for CSR 
        PKCS10 pkcs10 = new PKCS10(keyPair.getPublic());
        Signature signature = Signature.getInstance(sigAlg);
        signature.initSign(keyPair.getPrivate());
        // common, orgUnit, org, locality, state, country
        //--X500Principal principal = new X500Principal( "CN=Ole Nordmann, OU=ACME, O=Sales, C=NO");
        //--X500Name x500name= new X500Name(principal.getEncoded());
        //** can do this directly (and better)
        X500Name x500name = new X500Name ("CN="+CN+",OU="+OU+",O="+O+",L="+L+",S="+S+",C="+C);
        pkcs10.encodeAndSign(x500name, signature);
        ByteArrayOutputStream bs = new ByteArrayOutputStream();
        PrintStream ps = new PrintStream(bs);
        pkcs10.print(ps);
        byte[] c = bs.toByteArray();
        ps.close(); //** bs,ps are never null, ps.close automatically closes underlying bs,
        //** and anyway BAOS doesn't need to be closed (although most streams do)
        return c;
    }

    //** (whole) routine to generate an actual (though selfsigned) certificate 
    public static X509Certificate generateCertificate2 (String csrpem) throws Exception {  
        String csrtrim = csrpem.replaceAll("-----[^\\n]*\\n","").replaceAll("\\r?\\n","");
        //--PKCS10 pkcs10 = new PKCS10 (Base64.decode (csrtrim.toCharArray())); --Java7
        PKCS10 pkcs10 = new PKCS10 (Base64.getDecoder().decode (csrtrim.getBytes())); //Java8
        // or use the one we had before encoding it -- or the input data directly?? 

        // X509V3CertificateGenerator is deprecated but stay with it for now
        X509V3CertificateGenerator cert = new X509V3CertificateGenerator();   
        cert.setSerialNumber(BigInteger.valueOf(1));   //or generate a random number  
        cert.setSubjectDN(pkcs10.getSubjectName().asX500Principal());  
        cert.setIssuerDN(pkcs10.getSubjectName().asX500Principal()); //same since it is self-signed  
        cert.setPublicKey(pkcs10.getSubjectPublicKeyInfo());  
        Date now = new Date(); cert.setNotBefore(now);  
        now.setYear(now.getYear()+1); cert.setNotAfter(now);  
        cert.setSignatureAlgorithm("SHA1WithRSA");   
        PrivateKey signingKey = keyPair.getPrivate();    
        return cert.generate(signingKey, "BC");  
    }

}

【讨论】:

  • 非常感谢。我还有一个问题——import sun.security.pkcs.PKCS10;似乎不起作用。因此 PKCS10 pkcs10 = new PKCS10 (Base64.decode (csrtrim.toCharArray()));给出一个错误。有解决这个问题的办法吗?
  • @user6784240:糟糕!我执行此操作的 IDE 仍在 Java7 上,在检查 8 时,我看到它将此类移至 sun.security.pkcs10 - 并且还更改了 java.util.Base64,我忘记了这在 7 中不是官方的。因此确认了我关于不受支持的类的建议两次!请参阅编辑以获取最小修复。但我真正的建议是评论:您根本不需要 CSR,而是直接从 CSR 中的数据生成证书。
  • 非常感谢!!!这行得通。我需要 CSR 的原因是因为我需要从 CA 签署。
  • @user6784240:你的 Q 没有这么说。您可能会考虑keytool 如何处理这个问题:-genkeypair 生成一个密钥对和自签名证书(用于该密钥)并将它们(一起)放在密钥库中; -certreq 使用密钥对和名称 from 密钥库来创建 CSR 输出到屏幕或文件(不是密钥库);当您从 CA 获得证书(通常也是链)时,-importcert 用“正确”证书链替换密钥库条目中的自签名证书。 ...
  • ...如果您(可以)拥有 BC,请尝试记录(并且显然稳定)org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder 可能(我没有测试)org.bouncycastle.operator.jcajce.JcaContentSignerBuilder 的结果。
猜你喜欢
  • 2012-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-15
  • 1970-01-01
  • 1970-01-01
  • 2017-11-13
  • 1970-01-01
相关资源
最近更新 更多