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

No one assigned

    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