So, I have written some code to take a PEM, add it to a PKCS keystore via bouncycastle, and then use java crypto to import the PKCS keystore value into a JKS keystore.
I swear that yesterday I had a unit test pass successfully having executed these steps, but this morning I started hitting this
Caused by: java.security.NoSuchAlgorithmException: Algorithm HmacPBESHA256 not available
at javax.crypto.Mac.getInstance(Mac.java:181) ~[na:1.8.0_60]
at sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:2039) ~[na:1.8.0_65]
Now, it's always possible something changed underneath me but I cannot figure out what it was. It seems whatever provider I was using for that algorithm has since disappeared.
here's my java.security file snippet:
security.provider.1=sun.security.provider.Sun
security.provider.2=sun.security.rsa.SunRsaSign
security.provider.3=sun.security.ec.SunEC
security.provider.4=com.sun.net.ssl.internal.ssl.Provider
security.provider.5=com.sun.crypto.provider.SunJCE
security.provider.6=sun.security.jgss.SunProvider
security.provider.7=com.sun.security.sasl.Provider
security.provider.8=org.jcp.xml.dsig.internal.dom.XMLDSigRI
security.provider.9=sun.security.smartcardio.SunPCSC
security.provider.10=apple.security.AppleProvider
There's not a whole lot to the code. First I create a PKCS keystore via bouncycastle, adding a pem and saving to disk as PKCS12. Then import via java crypto, saving back out as JKS.
public KeystoreBuilder createJksFromPem(String pemPrivate, String pemPublic, String alias) throws Exception
{
Preconditions.checkState(StringUtils.isNotEmpty(pemPrivate), "pemPrivate must not be empty");
Preconditions.checkState(StringUtils.isNotEmpty(pemPublic), "pemPublic must not be empty");
Preconditions.checkState(StringUtils.isNotEmpty(alias), "alias must not be empty");
String pkcsFilename = filename + ".pkcs";
convertPemToPkcs(pemPrivate, pemPublic, pkcsFilename);
importPkcsIntoJks(pkcsFilename);
return this;
}
private void importPkcsIntoJks(String pkcsFilename) throws Exception
{
KeyStore pkcs = KeyStore.getInstance("PKCS12");
File pkcsFile = new File(pkcsFilename);
try (FileInputStream fis = new FileInputStream(pkcsFile))
{
pkcs.load(fis, password.toCharArray());
}
pkcsFile.delete();
KeyStore jks = KeyStore.getInstance("JKS");
jks.load(null);
Enumeration<String> aliases = pkcs.aliases();
while (aliases.hasMoreElements())
{
String alias = aliases.nextElement();
if (!pkcs.isKeyEntry(alias))
{
continue;
}
Key key = pkcs.getKey(alias, password.toCharArray());
Certificate[] chain = pkcs.getCertificateChain(alias);
jks.setKeyEntry(alias, key, password.toCharArray(), chain);
}
persist(jks);
}
private void convertPemToPkcs(String pemPrivate, String pemPublic, String pkcsFilename) throws IOException, NoSuchAlgorithmException, OperatorCreationException, PKCSException, FileNotFoundException
{
Security.addProvider(new BouncyCastleProvider());
X509CertificateHolder cert = (X509CertificateHolder) readObject(pemPublic);
PEMKeyPair keyPair = (PEMKeyPair) readObject(pemPrivate);
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
PKCS12SafeBagBuilder pkcs12BagBuilder = new PKCS12SafeBagBuilder(cert);
pkcs12BagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString("Kafka SSL Certificate"));
pkcs12BagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId, extUtils.createSubjectKeyIdentifier(keyPair.getPublicKeyInfo()));
PKCS12PfxPduBuilder builder = new PKCS12PfxPduBuilder();
builder.addData(pkcs12BagBuilder.build());
builder.addEncryptedData(new JcePKCSPBEOutputEncryptorBuilder(PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC).setProvider("BC").build(password.toCharArray()), pkcs12BagBuilder.build());
PKCS12PfxPdu pfx = builder.build(new JcePKCS12MacCalculatorBuilder(NISTObjectIdentifiers.id_sha256), password.toCharArray());
try (FileOutputStream fos = new FileOutputStream(new File(pkcsFilename)))
{
fos.write(pfx.getEncoded(ASN1Encoding.DL));
}
}
and boom it blows up on
pkcs.load(fis, password.toCharArray());
As you can see, the BouncyCastleProvider was added explicitly. Any suggestions?
UPDATE: Thanks dave_thompson_085 for the suggestion. Can't believe I didn't see that overloaded method, but the solution was to specify the provider in the call get Keystore.getInstance("PKCS12", "BC").
KeyStore.getInstance("PKCS12","BC")
or if you want to live dangerously inserting the BC provider at or below position 4. Or just do the PB-MAC on the PKCS12 with SHA1 like everybody else -- you're already using it for the PBE. Plus it looks to me like you're strongly encryping the cert -- why?? – Wheezy