如何编程生成客户端身份验证证书



我想创建一个网站,让最终用户可以生成可以使用的证书。我能够使用这些命令使用OpenSSL创建客户端身份验证证书。

#Make client ssl cert.
CLIENT_PASSWORD="password"
CLIENT_ALIAS="client"
touch ./client.cnf
echo "
[ req ]
prompt = no
distinguished_name = req_distinguished_name
output_password = $CLIENT_PASSWORD
[ req_distinguished_name ]
localityName           = "L" # L=
organizationName       = "O" # O=
organizationalUnitName = "OU" # OU=
commonName             = "CN" # CN=
" > ./client.cnf
openssl req -config client.cnf -newkey rsa:2048 -keyout client.key -out client.csr
openssl ca -keyfile ca.key -cert ca.crt -out client.crt -policy policy_anything -days 3650 -batch -passin pass:$CLIENT_PASSWORD -infiles client.csr
openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -passin pass:$CLIENT_PASSWORD -passout pass:$CLIENT_PASSWORD -name $CLIENT_ALIAS

我找到了充气城堡库V1.5,我找到了一些例子。我把它们放在一起,但不是制作可用的证书。

private static void MakeP12() throws Exception {
    Security.addProvider(new BouncyCastleProvider());
    String sigName = "SHA1withRSA";
    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC");
    kpg.initialize(2048);
    KeyPair kp = kpg.genKeyPair();
    X500NameBuilder x500NameBld = new X500NameBuilder(BCStyle.INSTANCE);
    x500NameBld.addRDN(BCStyle.C, "AU");
    x500NameBld.addRDN(BCStyle.ST, "Victoria");
    x500NameBld.addRDN(BCStyle.L, "Melbourne");
    x500NameBld.addRDN(BCStyle.O, "The Legion of the Bouncy Castle");
    X500Name subject = x500NameBld.build();
    PKCS10CertificationRequestBuilder requestBuilder = new JcaPKCS10CertificationRequestBuilder(subject, kp.getPublic());
    ExtensionsGenerator extGen = new ExtensionsGenerator();
    extGen.addExtension(Extension.subjectAlternativeName, false, new GeneralNames(new GeneralName(GeneralName.rfc822Name, "feedback-crypto@bouncycastle.org")));
    requestBuilder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extGen.generate());
    PKCS10CertificationRequest req1 = requestBuilder.build(new JcaContentSignerBuilder(sigName).setProvider("BC").build(kp.getPrivate()));
    if (req1.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider("BC").build(kp.getPublic())))
    {
        System.out.println(sigName + ": PKCS#10 request verified.");
    }
    else
    {
        System.out.println(sigName + ": Failed verify check.");
    }
    PKCS10CertificationRequest csr = req1;


    FileInputStream is = new FileInputStream("D:/Sun/certs/ca.jks");
    KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
    keystore.load(is, "password".toCharArray());
    PrivateKey cakey = (PrivateKey)keystore.getKey("my_ca", "password".toCharArray());
    X509Certificate cacert = (X509Certificate)keystore.getCertificate("my_ca");
    AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA");
    AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
    X500Name issuer = new X500Name(cacert.getSubjectX500Principal().getName());
    BigInteger serial = new BigInteger(32, new SecureRandom());
    Date from = new Date();
    int validity = 1;
    Date to = new Date(System.currentTimeMillis() + (validity * 86400000L));
    X509v3CertificateBuilder certgen = new X509v3CertificateBuilder(issuer, serial, from, to, csr.getSubject(), csr.getSubjectPublicKeyInfo());
    certgen.addExtension(X509Extension.basicConstraints, false, new BasicConstraints(false));
    certgen.addExtension(X509Extension.subjectKeyIdentifier, false, new SubjectKeyIdentifier(csr.getSubjectPublicKeyInfo()));
    certgen.addExtension(X509Extension.authorityKeyIdentifier, false, new AuthorityKeyIdentifier(new GeneralNames(new GeneralName(new X509Name(cacert.getSubjectX500Principal().getName()))), cacert.getSerialNumber()));
    ContentSigner signer = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(PrivateKeyFactory.createKey(cakey.getEncoded()));
    X509CertificateHolder holder = certgen.build(signer);
    /*===========================================================================*/
    PKCS12SafeBagBuilder eeCertBagBuilder = new JcaPKCS12SafeBagBuilder(new JcaX509CertificateConverter().setProvider( "BC" ).getCertificate( holder ));
    eeCertBagBuilder.addBagAttribute(PKCS12SafeBag.friendlyNameAttribute, new DERBMPString("Eric's Key"));
    JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
    SubjectKeyIdentifier pubKeyId = extUtils.createSubjectKeyIdentifier(kp.getPublic());
    eeCertBagBuilder.addBagAttribute(PKCS12SafeBag.localKeyIdAttribute, pubKeyId);
    OutputEncryptor encOut = new JcePKCSPBEOutputEncryptorBuilder(NISTObjectIdentifiers.id_aes256_CBC).setProvider("BC").build(JcaUtils.KEY_PASSWD);
    PKCS12SafeBagBuilder keyBagBuilder = new JcaPKCS12SafeBagBuilder(kp.getPrivate(), encOut);
    keyBagBuilder.addBagAttribute(PKCS12SafeBag.friendlyNameAttribute, new DERBMPString("Eric's Key"));
    keyBagBuilder.addBagAttribute(PKCS12SafeBag.localKeyIdAttribute, pubKeyId);
    PKCS12PfxPduBuilder builder = new PKCS12PfxPduBuilder();
    builder.addData(keyBagBuilder.build());
    builder.addEncryptedData(new JcePKCSPBEOutputEncryptorBuilder(
            PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC).setProvider("BC").build(JcaUtils.KEY_PASSWD), 
            new PKCS12SafeBag[]{eeCertBagBuilder.build()});
    PKCS12PfxPdu pfx = builder.build(new JcePKCS12MacCalculatorBuilder(NISTObjectIdentifiers.id_sha256), JcaUtils.KEY_PASSWD);
    readPKCS12File(pfx, "password".toCharArray());
    /*===========================================================================*/
}

它也没有给我一致的错误消息。

    Exception in thread "main" org.bouncycastle.pkcs.PKCSException: unable to read encrypted data: failed to construct sequence from byte[]: DER length more than 4 bytes: 79
    at org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo.decryptPrivateKeyInfo(Unknown Source)
    at cwguide.JcePKCS12Example.readPKCS12File(JcePKCS12Example.java:272)
    at cwguide.JcePKCS12Example.MakeP12(JcePKCS12Example.java:211)
    at cwguide.JcePKCS12Example.main(JcePKCS12Example.java:81)
Caused by: java.lang.IllegalArgumentException: failed to construct sequence from byte[]: DER length more than 4 bytes: 79
    at org.bouncycastle.asn1.ASN1Sequence.getInstance(Unknown Source)
    at org.bouncycastle.asn1.pkcs.PrivateKeyInfo.getInstance(Unknown Source)
    ... 4 more
Exception in thread "main" org.bouncycastle.pkcs.PKCSException: unable to read encrypted data: unknown object in getInstance: org.bouncycastle.asn1.DERApplicationSpecific
    at org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo.decryptPrivateKeyInfo(Unknown Source)
    at cwguide.JcePKCS12Example.readPKCS12File(JcePKCS12Example.java:272)
    at cwguide.JcePKCS12Example.MakeP12(JcePKCS12Example.java:211)
    at cwguide.JcePKCS12Example.main(JcePKCS12Example.java:81)
Caused by: java.lang.IllegalArgumentException: unknown object in getInstance: org.bouncycastle.asn1.DERApplicationSpecific
    at org.bouncycastle.asn1.ASN1Sequence.getInstance(Unknown Source)
    at org.bouncycastle.asn1.ASN1Sequence.getInstance(Unknown Source)
    at org.bouncycastle.asn1.pkcs.PrivateKeyInfo.getInstance(Unknown Source)
    ... 4 more
Exception in thread "main" org.bouncycastle.pkcs.PKCSException: unable to read encrypted data: failed to construct sequence from byte[]: unknown tag 41 encountered
    at org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo.decryptPrivateKeyInfo(Unknown Source)
    at cwguide.JcePKCS12Example.readPKCS12File(JcePKCS12Example.java:272)
    at cwguide.JcePKCS12Example.MakeP12(JcePKCS12Example.java:211)
    at cwguide.JcePKCS12Example.main(JcePKCS12Example.java:81)
Caused by: java.lang.IllegalArgumentException: failed to construct sequence from byte[]: unknown tag 41 encountered
    at org.bouncycastle.asn1.ASN1Sequence.getInstance(Unknown Source)
    at org.bouncycastle.asn1.pkcs.PrivateKeyInfo.getInstance(Unknown Source)
    ... 4 more

我在此网站上阅读了许多Q/A,可以帮助我,但是答案仍然让我感到不安。

好吧,我对使用弹力城堡V1.5的最后一个答案代替了OpenSSL。从弹力城堡中找到了不剥夺的功能,花了很多谷歌搜索。我仍然使用了keygen HTML5标签来生成Firefox的私钥。我只对使用Firefox感兴趣,但是根据我阅读的大多数Web浏览器,我应该可以使用。如果使用此代码,请确保为每个证书设置不同的序列数,如果您尝试用名称编号导入两个;Firefox只是忽略了请求。

import java.io.*;
import java.math.*;
import java.security.*;
import java.security.cert.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.bouncycastle.asn1.misc.*;
import org.bouncycastle.asn1.x500.*;
import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.cert.*;
import org.bouncycastle.cert.jcajce.*;
import org.bouncycastle.jce.netscape.*;
import org.bouncycastle.jce.provider.*;
import org.bouncycastle.operator.*;
import org.bouncycastle.operator.jcajce.*;
import org.bouncycastle.util.encoders.*;
public class ExampleClientAuth extends HttpServlet {
    private static final long serialVersionUID = 5599842503981845987L;
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head>");
        out.println("<title>SSL Generator</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<form method="post">");
        out.println("<keygen name="pubkey" challenge="randomchars">");
        out.println("Username: <input type="text" name="username" value="John Doe">");
        out.println("<input type="submit" name="createcert" value="Generate">");
        out.println("</form>");
        out.println("</body>");
        out.println("</html>");     
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String CN = "/var/lib/tomcat7/webapps/ROOT/WEB-INF/certs/";
        PrintWriter out = new PrintWriter(CN+"log.txt", "UTF-8");
        try {
            response.setContentType("application/x-x509-user-cert");
            OutputStream os=response.getOutputStream();
            String pubkey = request.getParameter("pubkey");
            pubkey = pubkey.replace("n", "").replace("r", "").replace("t", "").replace("", "").replace("u000B", "");
            String username = request.getParameter("username");
            BC_SingCert_Spkac(os,pubkey,username,out);
            os.close();
            os.flush();         
        } catch (Throwable t) {
            t.printStackTrace(out);
        } finally {
            out.close();
            out.flush();
        }
    }
    protected void BC_SingCert_Spkac(OutputStream os,String Spkac, String ID, PrintWriter log) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
        keystore.load(new FileInputStream("/usr/share/tomcat7/bin/cacerts.jks"),
            "password".toCharArray());
        PrivateKey cakey = (PrivateKey)keystore.getKey("my_ca", "password".toCharArray());
        X509Certificate cacert = (X509Certificate)keystore.getCertificate("my_ca");
        X509Certificate ncert = createCertFromSpkac(cacert, cakey, Spkac, ID);
        byte[] buf = ncert.getEncoded();
        os.write(buf, 0, buf.length);
    }
    private X509Certificate createCertFromSpkac(X509Certificate cacert,
        PrivateKey caPrivKey, String spkacData, String ID) throws Exception {
        X500Name subject = new X500Name("CN=""+ID+"",OU="Organizational Unit",O="Organizational",L="City",ST="California",C="US",E="email@example.com"");
        X500Name issuer = JcaX500NameUtil.getIssuer(cacert);
        int VALIDITY_PERIOD = 365 * 24 * 60 * 60 * 1000; // one year
        Date startDate = new Date(System.currentTimeMillis());
        Date endDate = new Date(System.currentTimeMillis() + VALIDITY_PERIOD);
        String subjAltNameURI = "http://example.com";
        BigInteger serialNumber = BigInteger.valueOf(1000);
        PublicKey caPubKey = cacert.getPublicKey();
        NetscapeCertRequest netscapeCertReq = new NetscapeCertRequest(Base64.decode(spkacData));
        PublicKey certPubKey = netscapeCertReq.getPublicKey();
        X509v3CertificateBuilder certGenerator = new X509v3CertificateBuilder(
            issuer, 
            serialNumber, 
            startDate, 
            endDate, 
            subject, 
            SubjectPublicKeyInfo.getInstance(certPubKey.getEncoded())
        );
        // Adds the Basic Constraint (CA: false) extension.
        certGenerator.addExtension(Extension.basicConstraints, true,
            new BasicConstraints(false));
        // Adds the Key Usage extension.
        certGenerator.addExtension(Extension.keyUsage, true, new KeyUsage(
            KeyUsage.digitalSignature | KeyUsage.nonRepudiation
            | KeyUsage.keyEncipherment | KeyUsage.keyAgreement
            | KeyUsage.keyCertSign));
        // Adds the Netscape certificate type extension.
        certGenerator.addExtension(MiscObjectIdentifiers.netscapeCertType,
            false, new NetscapeCertType(NetscapeCertType.sslClient
            | NetscapeCertType.smime));
        // Adds the subject key identifier extension.
        SubjectKeyIdentifier subjectKeyIdentifier =  
            new JcaX509ExtensionUtils().createSubjectKeyIdentifier(certPubKey);
        certGenerator.addExtension(Extension.subjectKeyIdentifier, false,
            subjectKeyIdentifier);
        // Adds the subject alternative-name extension (critical).
        if (subjAltNameURI != null) {
            GeneralNames subjectAltNames = new GeneralNames(new GeneralName(
                GeneralName.uniformResourceIdentifier, subjAltNameURI));
            certGenerator.addExtension(Extension.subjectAlternativeName,
                false, subjectAltNames);
        }
        // Creates and sign this certificate with the private key corresponding
        // to the public key of the certificate 
        ContentSigner signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(caPrivKey);
        X509CertificateHolder holder = certGenerator.build(signer);
        X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(holder);
        // Checks that this certificate has indeed been correctly signed.
        cert.verify(caPubKey);
        return cert;
    }
}

好吧,我找到了一个工作答案。我使用openSSL使用ProcessBuilder,并使用HTML标签" KeyGen"生成了客户端键。我从中得到了http://www.scriptjunkie.us/2013/11/adding-easy-ssl-client-client-authentication-to-any-webapp/此示例使V1 SSL证书将在接下来使用V3进行研究。

class RunProcessBuilder {
    public File path;
    public PrintWriter out;
    public RunProcessBuilder(File p, PrintWriter o) throws Exception {
        path = p;
        out = o;
    }
    public void Run(String args[]) throws Exception {
        out.printf("Output of running %s is:",Arrays.toString(args));
        out.flush();
        ProcessBuilder pb = new ProcessBuilder(args);
        Map<String, String> env = pb.environment();
        env.put("HOME", "/var/lib/tomcat7/webapps/ROOT/WEB-INF/certs/");        
        env.put("RANDFILE", "/var/lib/tomcat7/webapps/ROOT/WEB-INF/certs/demoCA/.rnd");     
        pb.directory(path);
        pb.redirectErrorStream(true);
        Process shell = pb.start();
        InputStream shellIn = shell.getInputStream();
        int shellExitStatus = shell.waitFor();
        int c;
        while ((c = shellIn.read()) != -1) { out.write(c); out.flush();}
        try {shellIn.close();} catch (IOException ignoreMe) { out.write(ignoreMe.toString()); out.flush();}
    }
}
public class ClientCert2 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head>");
        out.println("<title>Example</title>");
        out.println("</head>");
        out.println("<body bgcolor="white">");
        out.println("<form method="post">");
        out.println("<keygen name="pubkey" challenge="randomchars">");
        out.println("Username: <input type="text" name="username" value="Sam Sanders">");
        out.println("<input type="submit" name="createcert" value="Generate">");
        out.println("</form>");
        out.println("</body>");
        out.println("</html>");     
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("application/x-x509-user-cert");
        String CN = "/var/lib/tomcat7/webapps/ROOT/WEB-INF/certs/";
        PrintWriter out = new PrintWriter(CN+"log.txt", "UTF-8");
        try {
            String ClientID = "CilentX";
            String pass = "password";
            String pubkey = request.getParameter("pubkey");
            String username = request.getParameter("username");
            PrintWriter writer = new PrintWriter(CN+ClientID+".spkac", "UTF-8");
            writer.println("SPKAC="+pubkey.replace("n", "").replace("r", "").replace("t", "").replace("", "") );
            //.replace("x0B", "")
            //username needs to be unique
            writer.println("CN="+username);
            writer.println("emailAddress=test@test.com");
            writer.println("0.OU=test client certificate");
            writer.println("organizationName=organizationName");
            writer.println("countryName=AU");
            writer.println("stateOrProvinceName=State");
            writer.println("localityName=City");
            writer.close();
            writer.flush();
            writer = new PrintWriter(CN+ClientID+".cnf", "UTF-8");
            writer.println("[ ca ]");
            writer.println("default_ca      = CA_default");
            writer.println("[ CA_default ]");
            writer.println("dir            = "+CN);
            writer.println("database       = "+CN+"demoCA/index.txt");
            writer.println("new_certs_dir  = "+CN+"demoCA/newcerts");
            writer.println("certificate    = "+CN+"ca.crt");
            writer.println("serial         = "+CN+"demoCA/serial");
            writer.println("private_key    = "+CN+"ca.key");
            writer.println("RANDFILE       = "+CN+"demoCA/.rnd");
            writer.println("HOME           = "+CN);
            writer.println("default_days   = 3650");
            writer.println("default_crl_days= 60");
            writer.println("default_md     = sha1");
            writer.println("policy         = policy_any");
            writer.println("email_in_dn    = yes");
            writer.println("name_opt       = ca_default");
            writer.println("cert_opt       = ca_default");
            writer.println("copy_extensions = none");
            writer.println("[ policy_any ]");
            writer.println("countryName            = supplied");
            writer.println("stateOrProvinceName    = optional");
            writer.println("organizationName       = optional");
            writer.println("organizationalUnitName = optional");
            writer.println("commonName             = supplied");
            writer.println("emailAddress           = optional");
            writer.close();
            writer.flush();
            RunProcessBuilder x = new RunProcessBuilder(new File(CN), out);
            x.Run(new String[] {"openssl","ca","-config",CN+ClientID+".cnf","-days","365","-notext","-batch","-spkac",CN+ClientID+".spkac","-passin","pass:"+pass,"-out",CN+ClientID+".crt"});
            InputStream is=new BufferedInputStream(new FileInputStream(new File(CN+ClientID+".crt")));
            OutputStream os=response.getOutputStream();
            byte[] buf = new byte[1024];
            for (int nChunk = is.read(buf); nChunk!=-1; nChunk = is.read(buf)) {
                os.write(buf, 0, nChunk);
            } 
            os.close();
            os.flush();
        } catch (Throwable t) {
            t.printStackTrace(out);
        }
        out.close();
        out.flush();
    }
}

最新更新