问题
I need to attach an existing csr and keypair to a keystore. Given below is an implementation that uses GUI(java swing) to take the input from the user such as keystore name, alias,common name, organization etc.
I try to link the csr to the keystore using keystore.setkeyentry(...), however the keystore is still empty.
I have attached my code below, any help will be very useful:
This code below is used to create a 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;
}
In the main method I do the following to create a keystore and attach it to the 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);
When I check the contents of the keystore, it is empty. Any advice will be appreciated
Thank You!!!
回答1:
You have two main mistakes:
a Certificate Signing Request aka CSR aka PKCS10 is NOT a certificate.
CertificateFactory.generateCertificate
will only read a certificate and not a CSR, and when you provide it with a CSR it throws an exception which your code cleverly suppresses with no indication to anybody there was a serious problem. The commented-out code you had in your earlier revision was closer to that needed to generate a certificate.(if you do create/have a valid certificate)
KeyStore.set*
only sets the entry in the in-memory KeyStore object. If you want the keystore contents saved somewhere like in a file after your program exits, you muststore
it AFTER doing the 'set'(s).
Here is your code modified enough it works as I believe you want. Except for trivial formatting and scaffolding, spots I changed are marked by //--
for deletions and //**
for additions. Even so I do not recommend it because:
I continue your use of the unsupported
sun.security
classes, even though you are using BC and it has supported classes for PKCS10 and related bits, plus a CSR is only needed if you want to request a certificate from a CA; to generate a cert yourself just generating the cert directly is easier(less serious) in recent versions of BC pkix has been split to a separate jar and
X509V3CertificateGenerator
is now deprecated in favor ofX509v3CertificateBuilder
//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");
}
}
来源:https://stackoverflow.com/questions/40350607/java-api-to-create-a-keystore-and-attaching-a-csr-and-keypair-to-it