问题
I have this code to generate a CER file using the alias:
public class TestFromAliasToCER {
public static final int KEY_SIZE = 1024;
public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
public static final String END_CERT = "-----END CERTIFICATE-----";
public final static String LINE_SEPARATOR = System.getProperty("line.separator");
public static void main(String[] args) throws FileNotFoundException, IOException, NoSuchAlgorithmException, NoSuchProviderException, KeyStoreException, CertificateException {
KeyStore keyStore = KeyStore.getInstance ("Windows-MY");
keyStore.load (null, null);
Enumeration<String> aux = keyStore.aliases();
String alias = aux.nextElement();
X509Certificate certificate = (X509Certificate) keyStore.getCertificate (alias);
String certString = formatCrtFileContents(certificate);
PrintWriter out = new PrintWriter("cert.CER");
out.println(certString);
out.close();
}
public static String formatCrtFileContents(final Certificate certificate) throws CertificateEncodingException {
final Base64.Encoder encoder = Base64.getMimeEncoder(64, LINE_SEPARATOR.getBytes());
final byte[] rawCrtText = certificate.getEncoded();
final String encodedCertText = new String(encoder.encode(rawCrtText));
final String prettified_cert = BEGIN_CERT + LINE_SEPARATOR + encodedCertText + LINE_SEPARATOR + END_CERT;
return prettified_cert;
}
}
This creates the cer file with
-----BEGIN CERTIFICATE-----
data
-----END CERTIFICATE-----
I want to be able to create a PEM Certificate with the private key included, is it possible? If not, why?
I'm not restricted to Java only and free to use any Java API, but preferable with the least user interaction as possible.
回答1:
Although I don't see it documented, according to the source the SunMSCAPI provider implements only a stub for getEncoded
and cannot export Windows privatekey so you can't do this with JCA.
You could of course write JNI or JNA to call Windows CAPI, but that's not simple.
To use existing tools without user interaction you can use Runtime
or ProcessBuilder
to
run
certutil
with arguments-exportpfx -user -p password certid filename
run
powershell
and tell it to select an object incert:\currentuser\my
and invoke theExport('PFX','password')
method -- examples for machine rather than user cert hereor in (only) recent powershell use
Export-PFXCertificate
cmdlet documentation here
and after any of these, extract from pkcs12 to PEM with openssl pkcs12
, or if you prefer with Java by:
load the PKCS12 keystore and get the PrivateKey entry
call
getEncoded
and encode the result in folded (MIME) base64 like you did for the certificate except use-----BEGIN/END PRIVATE KEY-----
Warning: Java produces an unencrypted (PKCS8) privatekey, so make certain no unauthorized user or program ever has access to this file, your disk/filesystem or any backup(s).
回答2:
A digital certificate doesn't have the private key inside it (the private key is not part of the certificate fields). The certificate and the private key are separate entities, although they're related (one can't exist without the other).
If you take a look at the certificate fields in RFC 5280, you'll see that only the public key is part of it:
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING }
TBSCertificate ::= SEQUENCE {
... lots of fields
subjectPublicKeyInfo SubjectPublicKeyInfo,
... lots of other fields
}
The subjectPublicKeyInfo
is the public key, and there's no field for the private key.
That's because certificates are meant to be public (you can have more details on why they're public taking a look at how a Public Key Infrastructure works).
Although the certificate is public, there's always a correspondent private key somewhere, usually held by the certificate's owner (and ideally by no one else).
Anyway, the file you've got (with BEGIN CERTIFICATE and END CERTIFICATE headers) in only the digital certificate (but not the private key).
If you have the private key and the corresponding certificate, you can create a file that contains both. The most common formats for such file are: JKS (also known as Keystore) and PFX.
There are also another "format": the Windows repository (the one you're reading when you do KeyStore.getInstance("Windows-MY")
). I don't know exactly in what format its files are, but the KeyStore
class abstracts it.
If the private key is present, it will be together with its corresponding certificate, in the same alias. You can check if the key is present with this code:
String alias = aux.nextElement();
if (keyStore.isKeyEntry(alias)) { // alias contains a private key
Key key = keyStore.getKey(alias, "password".toCharArray()); // need to know the password
// key is the private key
// cert is the key's corresponding certificate
X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
} else if (keyStore.isCertificateEntry(alias)) { // alias doesn't contain a key
X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
}
After having the key, you can save it to another keystore with the following code:
// create another keystore
KeyStore output = KeyStore.getInstance("JKS");
// "alias" - choose to whatever name you want
// privateKey is the object you've got from keyStore.getKey()
// "password" is the password for this alias
// cert will be stored in the same alias
output.setKeyEntry("alias", privateKey, "password".toCharArray(), new Certificate[] { cert });
// save the keystore to a file
output.store(new FileOutputStream("outputfile.jks"), "keystore password".toCharArray());
The code above creates the file outputfile.jks
containing the certificate and the private key.
If you want the file to be a PFX, you can change the code above to:
// PKCS12 == PFX format
KeyStore output = KeyStore.getInstance("PKCS12");
// alternative: in pfx, I think that alias can't have specific passwords
// so you can use this as it doesn't require a password for the alias entry
output.setKeyEntry("alias", privateKey.getEncoded(), new Certificate[] { cert });
// change file extension to ".pfx"
output.store(new FileOutputStream("outputfile.pfx"), "keystore password".toCharArray());
来源:https://stackoverflow.com/questions/44311086/from-certificate-alias-to-pem-file-with-private-key-included-using-java