Skip to content

Commit

Permalink
added support for writing Data based PKCS12 files.
Browse files Browse the repository at this point in the history
  • Loading branch information
dghgit committed Sep 14, 2019
1 parent 13f5371 commit a2e6598
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 7 deletions.
1 change: 1 addition & 0 deletions docs/releasenotes.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ <h3>2.1.2 Defects Fixed</h3>
</ul>
<h3>2.1.3 Additional Features and Functionality</h3>
<ul>
<li>PKCS12 key stores key stores containing only certificates can now be created without the need to provide passwords.</li>
</ul>

<h3>2.2.1 Version</h3>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.BEROctetString;
import org.bouncycastle.asn1.BERSequence;
import org.bouncycastle.asn1.DERBMPString;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DEROctetString;
Expand Down Expand Up @@ -767,11 +768,6 @@ public void engineLoad(
return;
}

if (password == null)
{
throw new NullPointerException("No password supplied for PKCS#12 KeyStore.");
}

BufferedInputStream bufIn = new BufferedInputStream(stream);

bufIn.mark(10);
Expand Down Expand Up @@ -804,6 +800,11 @@ public void engineLoad(

if (bag.getMacData() != null) // check the mac code
{
if (password == null)
{
throw new NullPointerException("no password supplied when one expected");
}

MacData mData = bag.getMacData();
DigestInfo dInfo = mData.getMac();
macAlgorithm = dInfo.getAlgorithmId();
Expand Down Expand Up @@ -845,6 +846,16 @@ public void engineLoad(
throw new IOException("error constructing MAC: " + e.toString());
}
}
else
{
if (!Properties.isOverrideSet("org.bouncycastle.pkcs12.ignore_useless_passwd"))
{
if (password != null)
{
throw new IOException("password supplied for keystore that does not require one");
}
}
}

keys = new IgnoresCaseHashtable();
localIds = new Hashtable();
Expand Down Expand Up @@ -1293,9 +1304,110 @@ public void engineStore(OutputStream stream, char[] password)
private void doStore(OutputStream stream, char[] password, boolean useDEREncoding)
throws IOException
{
if (password == null)
if (keys.size() == 0)
{
if (password == null)
{
Enumeration cs = certs.keys();

ASN1EncodableVector certSeq = new ASN1EncodableVector();

while (cs.hasMoreElements())
{
try
{
String certId = (String)cs.nextElement();
Certificate cert = (Certificate)certs.get(certId);
boolean cAttrSet = false;

CertBag cBag = new CertBag(
x509Certificate,
new DEROctetString(cert.getEncoded()));
ASN1EncodableVector fName = new ASN1EncodableVector();

if (cert instanceof PKCS12BagAttributeCarrier)
{
PKCS12BagAttributeCarrier bagAttrs = (PKCS12BagAttributeCarrier)cert;
//
// make sure we are using the local alias on store
//
DERBMPString nm = (DERBMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
if (nm == null || !nm.getString().equals(certId))
{
bagAttrs.setBagAttribute(pkcs_9_at_friendlyName, new DERBMPString(certId));
}

Enumeration e = bagAttrs.getBagAttributeKeys();

while (e.hasMoreElements())
{
ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();

// a certificate not immediately linked to a key doesn't require
// a localKeyID and will confuse some PKCS12 implementations.
//
// If we find one, we'll prune it out.
if (oid.equals(PKCSObjectIdentifiers.pkcs_9_at_localKeyId))
{
continue;
}

ASN1EncodableVector fSeq = new ASN1EncodableVector();

fSeq.add(oid);
fSeq.add(new DERSet(bagAttrs.getBagAttribute(oid)));
fName.add(new DERSequence(fSeq));

cAttrSet = true;
}
}

if (!cAttrSet)
{
ASN1EncodableVector fSeq = new ASN1EncodableVector();

fSeq.add(pkcs_9_at_friendlyName);
fSeq.add(new DERSet(new DERBMPString(certId)));

fName.add(new DERSequence(fSeq));
}

SafeBag sBag = new SafeBag(certBag, cBag.toASN1Primitive(), new DERSet(fName));

certSeq.add(sBag);
}
catch (CertificateEncodingException e)
{
throw new IOException("Error encoding certificate: " + e.toString());
}
}

if (useDEREncoding)
{
ContentInfo bagInfo = new ContentInfo(PKCSObjectIdentifiers.data, new DEROctetString(new DERSequence(certSeq).getEncoded()));

Pfx pfx = new Pfx(new ContentInfo(PKCSObjectIdentifiers.data, new DEROctetString(new DERSequence(bagInfo).getEncoded())), null);

pfx.encodeTo(stream, ASN1Encoding.DER);
}
else
{
ContentInfo bagInfo = new ContentInfo(PKCSObjectIdentifiers.data, new BEROctetString(new BERSequence(certSeq).getEncoded()));

Pfx pfx = new Pfx(new ContentInfo(PKCSObjectIdentifiers.data, new BEROctetString(new BERSequence(bagInfo).getEncoded())), null);

pfx.encodeTo(stream, ASN1Encoding.BER);
}

return;
}
}
else
{
throw new NullPointerException("No password supplied for PKCS#12 KeyStore.");
if (password == null)
{
throw new NullPointerException("no password supplied for PKCS#12 KeyStore");
}
}

//
Expand Down Expand Up @@ -1792,6 +1904,11 @@ public Enumeration elements()
{
return orig.elements();
}

public int size()
{
return orig.size();
}
}

private static class DefaultSecretKeyProvider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,24 @@ public class PKCS12StoreTest
+ "MDEwITAJBgUrDgMCGgUABBT3iAwuHw7KQXrl09gBkHaUVbOoBAQIIm90qua1"
+ "2i4CAggA");

private static byte[] certsOnly = Base64.decode(
"MIICnwIBAzCCApgGCSqGSIb3DQEHAaCCAokEggKFMIICgTCCAn0GCSqGSIb3" +
"DQEHAaCCAm4EggJqMIICZjCCAmIGCyqGSIb3DQEMCgEDoIICHDCCAhgGCiq" +
"GSIb3DQEJFgGgggIIBIICBDCCAgAwggFpoAMCAQICBHcheqIwDQYJKoZIhv" +
"cNAQELBQAwMjENMAsGA1UEChMERGVtbzENMAsGA1UECxMERGVtbzESMBAGA" +
"1UEAxMJRGVtbyBjZXJ0MCAXDTE5MDgzMTEzMDgzNloYDzIxMDkwNTE5MTMw" +
"ODM2WjAyMQ0wCwYDVQQKEwREZW1vMQ0wCwYDVQQLEwREZW1vMRIwEAYDVQQ" +
"DEwlEZW1vIGNlcnQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKOVC4" +
"Qeg0KPAPRB9WcZdvXitiJ+E6rd3czQGNzEFC6FesAllH3PHSWuUZ2YjhiVM" +
"YJyzwVP1II04iCRaIc65R45oVrHZ2ybWAOda2hBtySjQ2pIQQpoKE7nvL3j" +
"JcHoCIBJVf3c3xpfh7RucCOGiZDjU9CYPG8yznsazb5+fPF/AgMBAAGjITA" +
"fMB0GA1UdDgQWBBR/7wUDwa7T0vNzNgjOKdjz2Up9RzANBgkqhkiG9w0BAQ" +
"sFAAOBgQADzPFsaLhVYD/k9qMueYKi8Ftwijr37niF98cgAHEtq6TGsh3Se" +
"8gEK3dNJL18vm7NXgGsl8jUWsE9hCF9ar+/cDZ+KrZlZ5PLfifXJJKFqVAh" +
"sOORef0NRIVcTCoyQTW4pNpNZP9Ul5LJ3iIDjafgJMyEkRbavqdyfSqVTvY" +
"NpjEzMBkGCSqGSIb3DQEJFDEMHgoAYQBsAGkAYQBzMBYGDGCGSAGG+Watyn" +
"sBATEGBgRVHSUA");

/**
* we generate a self signed certificate for the sake of testing - RSA
*/
Expand Down Expand Up @@ -585,6 +603,41 @@ public Certificate createCert(
return TestUtils.createCert(issuerBldr.build(), privKey, subjectBldr.build(), "SHA1withRSA", null, pubKey);
}

private void testCertsOnly()
throws Exception
{
KeyStore pkcs12 = KeyStore.getInstance("PKCS12", "BC");

pkcs12.load(new ByteArrayInputStream(certsOnly), null);

isTrue(pkcs12.containsAlias("alias"));

ByteArrayOutputStream bOut = new ByteArrayOutputStream();

pkcs12.store(bOut, null);

pkcs12 = KeyStore.getInstance("PKCS12", "BC");

pkcs12.load(new ByteArrayInputStream(bOut.toByteArray()), null);

isTrue(pkcs12.containsAlias("alias"));

try
{
pkcs12.load(new ByteArrayInputStream(certsOnly), "1".toCharArray());
fail("no exception");
}
catch (IOException e)
{
isEquals("password supplied for keystore that does not require one", e.getMessage());
}

System.setProperty("org.bouncycastle.pkcs12.ignore_useless_passwd", "true");

pkcs12.load(new ByteArrayInputStream(certsOnly), "1".toCharArray());

System.setProperty("org.bouncycastle.pkcs12.ignore_useless_passwd", "false");
}
private void testGOSTStore()
throws Exception
{
Expand Down Expand Up @@ -1493,6 +1546,7 @@ public void performTest()
testGOSTStore();
testChainCycle();
testBCFKSLoad();
testCertsOnly();

// converter tests

Expand Down

0 comments on commit a2e6598

Please sign in to comment.