我想创建一个网站,让最终用户生成可以进行身份验证的证书。 我能够使用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
我找到了Bouncy Castle 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
我在这个网站上看过许多有帮助我的问答,但答案仍然没有找到。
答案 0 :(得分:2)
好的,我重写了我的最后一个答案,使用Bouncy Castle v1.5代替OpenSSL。从Bouncy Castle找到非弃用的功能需要大量的Google搜索。我仍然使用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("\0", "").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;
}
}
答案 1 :(得分:-1)
好的,我找到了合适的答案。我在openssl中使用了ProcessBuilder,并使用HTML标签“keygen”生成了客户端密钥。我从中得到了这个想法 http://www.scriptjunkie.us/2013/11/adding-easy-ssl-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("\0", "") );
//.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();
}
}