Skip to content

Can't create p12 keystore with PKCS12-AES256-AES128 if BC provider is not added to system providers #2275

@mauromol

Description

@mauromol

Consider the following Java app, to be run with BC 1.83 and Java 21:

package com.example;

import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

import java.io.FileOutputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.util.Date;
import javax.security.auth.x500.X500Principal;

public class Test {

  public static void main(final String[] args) throws Exception {
    final String outFile = "example.p12";
    final char[] password = "changeit".toCharArray();

    final Provider provider = new BouncyCastleProvider();
    //Security.addProvider(provider);
    final KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", provider);
    final KeyPair kp1 = kpg.generateKeyPair();
    final X509Certificate cert1 =
        selfSigned(kp1, "CN=RSA Key 1, O=Example, C=IT", Duration.ofDays(3650), provider);
    final KeyStore ks = KeyStore.getInstance("PKCS12-AES256-AES128", provider);
    ks.load(null, null);
    ks.setKeyEntry("rsa-key-1", kp1.getPrivate(), password, new Certificate[] { cert1 });

    try (final FileOutputStream fos = new FileOutputStream(outFile)) {
      ks.store(fos, password);
    }

    System.out.println("Created: " + outFile);
    System.out.println("Aliases: rsa-key-1, rsa-key-2");
  }

  private static X509Certificate selfSigned(final KeyPair keyPair, final String dn, final Duration validity,
      final Provider provider) throws Exception {
    final X500Principal subject = new X500Principal(dn);
    final X500Principal issuer = subject;
    final long now = System.currentTimeMillis();
    final Date notBefore = new Date(now - 60_000L); // 1 min skew
    final Date notAfter = new Date(now + validity.toMillis());
    final BigInteger serial = new BigInteger(64, SecureRandom.getInstanceStrong()).abs().add(BigInteger.ONE);
    final JcaX509v3CertificateBuilder builder =
        new JcaX509v3CertificateBuilder(issuer, serial, notBefore, notAfter, subject, keyPair.getPublic());
    final JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
    builder.addExtension(Extension.basicConstraints, true, new BasicConstraints(false));
    builder.addExtension(Extension.keyUsage, true,
        new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment));
    builder.addExtension(Extension.subjectKeyIdentifier, false,
        extUtils.createSubjectKeyIdentifier(keyPair.getPublic()));
    return new JcaX509CertificateConverter()
        .setProvider(provider)
        .getCertificate(builder.build(
            new JcaContentSignerBuilder("SHA256withRSA").setProvider(provider).build(keyPair.getPrivate())));
  }
}

If you run it, it fails with:

Exception in thread "main" java.io.IOException: exception encrypting data - java.security.NoSuchAlgorithmException: 2.16.840.1.101.3.4.1.42 AlgorithmParameters not available
	at org.bouncycastle.jcajce.provider.keystore.pkcs12.PKCS12KeyStoreSpi.wrapKey(Unknown Source)
	at org.bouncycastle.jcajce.provider.keystore.pkcs12.PKCS12KeyStoreSpi.doStore(Unknown Source)
	at org.bouncycastle.jcajce.provider.keystore.pkcs12.PKCS12KeyStoreSpi.engineStore(Unknown Source)
	at org.bouncycastle.jcajce.provider.keystore.util.AdaptingKeyStoreSpi.engineStore(Unknown Source)
	at java.base/java.security.KeyStore.store(KeyStore.java:1431)
	at com.example.Test.main(Test.java:45)

Unless you uncomment line: //Security.addProvider(provider);
This is because PKCS12KeyStoreSpi.wrapKey(EncryptionScheme, Key, PBKDF2Params, char[]) does not specify the Bouncy Castle provider when getting an instance of AlgorithmParameters. This is unexpected: I don't want to be forced to add Bouncy Castle as a system provider, I'm just specifying it on every getInstance call that is under my control.

This sounds very similar to #2085.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions