All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xipki.security.pkcs12.P12ContentSignerBuilder Maven / Gradle / Ivy

/*
 *
 * Copyright (c) 2013 - 2020 Lijun Liao
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.xipki.security.pkcs12;

import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.Signer;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.signers.*;
import org.bouncycastle.jcajce.provider.asymmetric.dsa.DSAUtil;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.bc.BcContentSignerBuilder;
import org.xipki.security.*;
import org.xipki.security.util.GMUtil;
import org.xipki.security.util.SignerUtil;
import org.xipki.util.CollectionUtil;

import javax.crypto.NoSuchPaddingException;
import java.security.*;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.EllipticCurve;
import java.util.ArrayList;
import java.util.List;

import static org.xipki.util.Args.notNull;
import static org.xipki.util.Args.positive;

/**
 * Builder of signer based PKCS#12 keystore.
 *
 * @author Lijun Liao
 * @since 2.0.0
 */

public class P12ContentSignerBuilder {

  // CHECKSTYLE:SKIP
  private static class RSAContentSignerBuilder extends BcContentSignerBuilder {

    private final SignAlgo signAlgo;

    private RSAContentSignerBuilder(SignAlgo signAlgo)
        throws NoSuchAlgorithmException {
      super(signAlgo.getAlgorithmIdentifier(), signAlgo.getHashAlgo().getAlgorithmIdentifier());
      if (!(signAlgo.isRSAPSSSigAlgo() || signAlgo.isRSAPkcs1SigAlgo())) {
        throw new NoSuchAlgorithmException("the given algorithm is not a valid RSA signature "
            + "algorithm '" + signAlgo + "'");
      }
      this.signAlgo = signAlgo;
    }

    protected Signer createSigner(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier digAlgId)
        throws OperatorCreationException {
      signAlgo.assertSameAlgorithm(sigAlgId, digAlgId);
      if (signAlgo.isRSAPSSSigAlgo()) {
        try {
          return SignerUtil.createPSSRSASigner(signAlgo);
        } catch (XiSecurityException ex) {
          throw new OperatorCreationException(ex.getMessage(), ex);
        }
      } else {
        Digest dig = digestProvider.get(digAlgId);
        return new RSADigestSigner(dig);
      }
    }

  } // class RSAContentSignerBuilder

  // CHECKSTYLE:SKIP
  private static class DSAContentSignerBuilder extends BcContentSignerBuilder {

    private final SignAlgo signAlgo;

    private DSAContentSignerBuilder(SignAlgo signAlgo)
        throws NoSuchAlgorithmException {
      super(signAlgo.getAlgorithmIdentifier(), signAlgo.getHashAlgo().getAlgorithmIdentifier());
      if (!signAlgo.isDSASigAlgo()) {
        throw new NoSuchAlgorithmException("the given algorithm is not a valid DSA signature "
            + "algirthm " + signAlgo);
      }
      this.signAlgo = signAlgo;
    }

    protected Signer createSigner(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier digAlgId)
        throws OperatorCreationException {
      signAlgo.assertSameAlgorithm(sigAlgId, digAlgId);
      Digest dig = signAlgo.getHashAlgo().createDigest();
      return new DSADigestSigner(new DSASigner(), dig);
    }

  } // class DSAContentSignerBuilder

  // CHECKSTYLE:SKIP
  private static class ECDSAContentSignerBuilder extends BcContentSignerBuilder {

    private final SignAlgo signAlgo;

    private ECDSAContentSignerBuilder(SignAlgo signAlgo)
        throws NoSuchAlgorithmException {
      super(signAlgo.getAlgorithmIdentifier(), signAlgo.getHashAlgo().getAlgorithmIdentifier());
      if (!signAlgo.isECDSASigAlgo()) {
        throw new NoSuchAlgorithmException("the given algorithm is not a valid ECDSA signature "
            + "algirthm " + signAlgo);
      }
      this.signAlgo = signAlgo;
    }

    protected Signer createSigner(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier digAlgId)
        throws OperatorCreationException {
      signAlgo.assertSameAlgorithm(sigAlgId, digAlgId);

      Digest dig = signAlgo.getHashAlgo().createDigest();
      ECDSASigner dsaSigner = new ECDSASigner();

      return signAlgo.isPlainECDSASigAlgo()
          ? new DSAPlainDigestSigner(dsaSigner, dig)
          : new DSADigestSigner(dsaSigner, dig);
    }

  } // class ECDSAContentSignerBuilder

  // CHECKSTYLE:SKIP
  private static class SM2ContentSignerBuilder extends BcContentSignerBuilder {

    private final SignAlgo signAlgo;

    private SM2ContentSignerBuilder(SignAlgo signAlgo)
        throws NoSuchAlgorithmException {
      super(signAlgo.getAlgorithmIdentifier(), signAlgo.getHashAlgo().getAlgorithmIdentifier());
      if (!signAlgo.isSM2SigAlgo()) {
        throw new NoSuchAlgorithmException("the given algorithm is not a valid SM2 signature "
            + "algirthm " + signAlgo);
      }
      this.signAlgo = signAlgo;
    }

    protected Signer createSigner(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier digAlgId)
        throws OperatorCreationException {
      signAlgo.assertSameAlgorithm(sigAlgId, digAlgId);
      return new SM2Signer(signAlgo.getHashAlgo().createDigest());
    }

  } // class SM2ContentSignerBuilder

  private final PrivateKey key;

  private final PublicKey publicKey;

  private final X509Cert[] certificateChain;

  public P12ContentSignerBuilder(PrivateKey privateKey, PublicKey publicKey) {
    this.key = notNull(privateKey, "privateKey");
    this.publicKey = notNull(publicKey, "publicKey");
    this.certificateChain = null;
  }

  public P12ContentSignerBuilder(KeypairWithCert keypairWithCert)
      throws XiSecurityException {
    notNull(keypairWithCert, "keypairWithCert");
    this.key = keypairWithCert.getKey();
    this.publicKey = keypairWithCert.getPublicKey();
    this.certificateChain = keypairWithCert.getCertificateChain();
  }

  public X509Cert getCertificate() {
    return (certificateChain != null && certificateChain.length > 0) ? certificateChain[0] : null;
  }

  public X509Cert[] getCertificateChain() {
    return certificateChain;
  }

  public PrivateKey getKey() {
    return key;
  }

  public ContentSigner createContentSigner(SignAlgo signAlgo, SecureRandom random)
          throws XiSecurityException {
    notNull(signAlgo, "signAlgo");

    String provName = getProviderName(signAlgo);

    if (provName != null && Security.getProvider(provName) != null) {
      try {
        Signature signature = createSignature(signAlgo, provName, true);
        return new SignatureSigner(signAlgo, signature, key);
      } catch (Exception ex) {
        // do nothing
      }
    }

    Object[] rv = ff(signAlgo, random);
    BcContentSignerBuilder signerBuilder = (BcContentSignerBuilder) rv[0];
    AsymmetricKeyParameter keyparam = (AsymmetricKeyParameter) rv[1];

    try {
      return signerBuilder.build(keyparam);
    } catch (OperatorCreationException ex) {
      throw new XiSecurityException("operator creation error", ex);
    }
  } // method createContentSigner

  public ConcurrentContentSigner createSigner(SignAlgo signAlgo, int parallelism,
      SecureRandom random)
          throws XiSecurityException, NoSuchPaddingException {
    notNull(signAlgo, "signAlgo");
    positive(parallelism, "parallelism");

    List signers = new ArrayList<>(parallelism);

    String provName = getProviderName(signAlgo);
    if (provName != null && Security.getProvider(provName) != null) {
      try {
        for (int i = 0; i < parallelism; i++) {
          Signature signature = createSignature(signAlgo, provName, i == 0);
          XiContentSigner signer = new SignatureSigner(signAlgo, signature, key);
          signers.add(signer);
        }
      } catch (Exception ex) {
        signers.clear();
      }
    }

    if (CollectionUtil.isEmpty(signers)) {
      Object[] rv = ff(signAlgo, random);
      BcContentSignerBuilder signerBuilder = (BcContentSignerBuilder) rv[0];
      AsymmetricKeyParameter keyparam = (AsymmetricKeyParameter) rv[1];

      for (int i = 0; i < parallelism; i++) {
        ContentSigner signer;
        try {
          signer = signerBuilder.build(keyparam);
        } catch (OperatorCreationException ex) {
          throw new XiSecurityException("operator creation error", ex);
        }
        signers.add(new XiWrappedContentSigner(signer, true));
      }
    }

    final boolean mac = false;
    ConcurrentContentSigner concurrentSigner;
    try {
      concurrentSigner = new DfltConcurrentContentSigner(mac, signers, key);
    } catch (NoSuchAlgorithmException ex) {
      throw new XiSecurityException(ex.getMessage(), ex);
    }

    if (certificateChain != null) {
      concurrentSigner.setCertificateChain(certificateChain);
    } else {
      concurrentSigner.setPublicKey(publicKey);
    }
    return concurrentSigner;
  } // method createSigner

  private String getProviderName(SignAlgo signAlgo) {
    if (signAlgo.isRSAPkcs1SigAlgo()) {
      return "SunRsaSign";
    } else if (signAlgo.isECDSASigAlgo()) {
      // Currently, the provider SunEC is much slower (5x) than BC,
      // so we do not use the Signature variant.
      return null;
    } else if (signAlgo.isDSASigAlgo()) {
      return "SUN";
    } else if (signAlgo.isEDDSASigAlgo()) {
      return "BC";
    } else {
      return null;
    }
  }

  private Signature createSignature(SignAlgo signAlgo, String provName, boolean test)
      throws NoSuchAlgorithmException, NoSuchProviderException,
      InvalidKeyException, SignatureException {
    Signature signature = Signature.getInstance(signAlgo.getJceName(), provName);
    signature.initSign(key);
    if (test) {
      signature.update(new byte[]{1, 2, 3, 4});
      signature.sign();
    }
    return signature;
  }

  private Object[] ff(SignAlgo signAlgo, SecureRandom random)
      throws XiSecurityException {
    BcContentSignerBuilder signerBuilder;
    AsymmetricKeyParameter keyparam;
    try {
      if (key instanceof RSAPrivateKey) {
        keyparam = SignerUtil.generateRSAPrivateKeyParameter((RSAPrivateKey) key);
        signerBuilder = new RSAContentSignerBuilder(signAlgo);
      } else if (key instanceof DSAPrivateKey) {
        keyparam = DSAUtil.generatePrivateKeyParameter(key);
        signerBuilder = new DSAContentSignerBuilder(signAlgo);
      } else if (key instanceof ECPrivateKey) {
        keyparam = ECUtil.generatePrivateKeyParameter(key);
        EllipticCurve curve = ((ECPrivateKey) key).getParams().getCurve();
        if (GMUtil.isSm2primev2Curve(curve)) {
          signerBuilder = new SM2ContentSignerBuilder(signAlgo);
        } else {
          signerBuilder = new ECDSAContentSignerBuilder(signAlgo);
        }
      } else {
        throw new XiSecurityException("unsupported key " + key.getClass().getName());
      }
    } catch (InvalidKeyException ex) {
      throw new XiSecurityException("invalid key", ex);
    } catch (NoSuchAlgorithmException ex) {
      throw new XiSecurityException("no such algorithm", ex);
    }

    if (random != null) {
      signerBuilder.setSecureRandom(random);
    }

    return new Object[] {signerBuilder, keyparam};
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy