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

org.xipki.security.DfltConcurrentContentSigner Maven / Gradle / Ivy

/*
 *
 * Copyright (c) 2013 - 2019 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;

import java.io.IOException;
import java.io.OutputStream;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cert.X509CertificateHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xipki.password.PasswordResolver;
import org.xipki.security.util.AlgorithmUtil;
import org.xipki.util.Args;
import org.xipki.util.CollectionUtil;
import org.xipki.util.LogUtil;
import org.xipki.util.concurrent.ConcurrentBag;

/**
 * TODO.
 * @author Lijun Liao
 * @since 2.0.0
 */

public class DfltConcurrentContentSigner implements ConcurrentContentSigner {

  private static final Logger LOG = LoggerFactory.getLogger(DfltConcurrentContentSigner.class);

  private static final AtomicInteger NAME_INDEX = new AtomicInteger(1);

  private static int defaultSignServiceTimeout = 10000; // 10 seconds

  private final ConcurrentBag signers = new ConcurrentBag<>();

  private final String name;

  private final String algorithmName;

  private final boolean mac;

  private byte[] sha1OfMacKey;

  private final Key signingKey;

  private final AlgorithmCode algorithmCode;

  private PublicKey publicKey;

  private X509Certificate[] certificateChain;

  private X509CertificateHolder[] bcCertificateChain;

  static {
    final String propKey = "org.xipki.security.signservice.timeout";
    String str = System.getProperty(propKey);

    if (str != null) {
      int vi = Integer.parseInt(str);
      // valid value is between 0 and 60 seconds
      if (vi < 0 || vi > 60 * 1000) {
        LOG.error("invalid {}: {}", propKey, vi);
      } else {
        LOG.info("use {}: {}", propKey, vi);
        defaultSignServiceTimeout = vi;
      }
    }
  }

  public DfltConcurrentContentSigner(boolean mac, List signers)
      throws NoSuchAlgorithmException {
    this(mac, signers, null);
  }

  public DfltConcurrentContentSigner(boolean mac, List signers, Key signingKey)
      throws NoSuchAlgorithmException {
    Args.notEmpty(signers, "signers");

    this.mac = mac;
    AlgorithmIdentifier algorithmIdentifier = signers.get(0).getAlgorithmIdentifier();
    this.algorithmName = AlgorithmUtil.getSigOrMacAlgoName(algorithmIdentifier);
    this.algorithmCode = AlgorithmUtil.getSigOrMacAlgoCode(algorithmIdentifier);

    for (XiContentSigner signer : signers) {
      this.signers.add(new ConcurrentBagEntrySigner(signer));
    }

    this.signingKey = signingKey;
    this.name = "defaultSigner-" + NAME_INDEX.getAndIncrement();
  }

  @Override
  public String getName() {
    return name;
  }

  @Override
  public boolean isMac() {
    return mac;
  }

  public void setSha1DigestOfMacKey(byte[] digest) {
    if (digest == null) {
      this.sha1OfMacKey = null;
    } else if (digest.length == 20) {
      this.sha1OfMacKey = Arrays.copyOf(digest, 20);
    } else {
      throw new IllegalArgumentException("invalid sha1Digest.length (" + digest.length + " != 20)");
    }
  }

  @Override
  public byte[] getSha1OfMacKey() {
    return (sha1OfMacKey == null) ? null : Arrays.copyOf(sha1OfMacKey, 20);
  }

  @Override
  public AlgorithmCode getAlgorithmCode() {
    return algorithmCode;
  }

  @Override
  public ConcurrentBagEntrySigner borrowSigner() throws NoIdleSignerException {
    return borrowSigner(defaultSignServiceTimeout);
  }

  /**
   * TODO.
   * @param soTimeout timeout in milliseconds, 0 for infinitely.
   */
  @Override
  public ConcurrentBagEntrySigner borrowSigner(int soTimeout) throws NoIdleSignerException {
    ConcurrentBagEntrySigner signer = null;
    try {
      signer = signers.borrow(soTimeout, TimeUnit.MILLISECONDS);
    } catch (InterruptedException ex) { // CHECKSTYLE:SKIP
    }

    if (signer == null) {
      throw new NoIdleSignerException("no idle signer available");
    }

    return signer;
  }

  @Override
  public void requiteSigner(ConcurrentBagEntrySigner signer) {
    signers.requite(signer);
  }

  @Override
  public void initialize(String conf, PasswordResolver passwordResolver)
      throws XiSecurityException {
  }

  @Override
  public Key getSigningKey() {
    return signingKey;
  }

  @Override
  public void setCertificateChain(X509Certificate[] certificateChain) {
    if (CollectionUtil.isEmpty(certificateChain)) {
      this.certificateChain = null;
      this.bcCertificateChain = null;
      return;
    }

    this.certificateChain = certificateChain;
    setPublicKey(certificateChain[0].getPublicKey());
    final int n = certificateChain.length;
    this.bcCertificateChain = new X509CertificateHolder[n];

    for (int i = 0; i < n; i++) {
      X509Certificate cert = this.certificateChain[i];
      try {
        this.bcCertificateChain[i] = new X509CertificateHolder(cert.getEncoded());
      } catch (CertificateEncodingException | IOException ex) {
        throw new IllegalArgumentException(
            String.format("%s occurred while parsing certificate at index %d: %s",
                ex.getClass().getName(), i, ex.getMessage()), ex);
      }
    }
  }

  @Override
  public PublicKey getPublicKey() {
    return publicKey;
  }

  @Override
  public void setPublicKey(PublicKey publicKey) {
    this.publicKey = publicKey;
  }

  @Override
  public X509Certificate getCertificate() {
    return CollectionUtil.isEmpty(certificateChain) ? null : certificateChain[0];
  }

  @Override
  public X509CertificateHolder getBcCertificate() {
    return CollectionUtil.isEmpty(bcCertificateChain) ? null : bcCertificateChain[0];
  }

  @Override
  public X509Certificate[] getCertificateChain() {
    return certificateChain;
  }

  @Override
  public X509CertificateHolder[] getBcCertificateChain() {
    return bcCertificateChain;
  }

  @Override
  public boolean isHealthy() {
    ConcurrentBagEntrySigner signer = null;
    try {
      signer = borrowSigner();
      OutputStream stream = signer.value().getOutputStream();
      stream.write(new byte[]{1, 2, 3, 4});
      byte[] signature = signer.value().getSignature();
      return signature != null && signature.length > 0;
    } catch (Exception ex) {
      LogUtil.error(LOG, ex);
      return false;
    } finally {
      if (signer != null) {
        requiteSigner(signer);
      }
    }
  }

  @Override
  public String getAlgorithmName() {
    return algorithmName;
  }

  @Override
  public void close() {
  }

  @Override
  public byte[] sign(byte[] data) throws NoIdleSignerException, SignatureException {
    ConcurrentBagEntrySigner signer = borrowSigner();
    try {
      OutputStream signatureStream = signer.value().getOutputStream();
      try {
        signatureStream.write(data);
      } catch (IOException ex) {
        throw new SignatureException(
            "could not write data to SignatureStream: " + ex.getMessage(), ex);
      }
      return signer.value().getSignature();
    } finally {
      requiteSigner(signer);
    }
  }

  @Override
  public byte[][] sign(byte[][] data) throws NoIdleSignerException, SignatureException {
    byte[][] signatures = new byte[data.length][];
    ConcurrentBagEntrySigner signer = borrowSigner();

    try {
      XiContentSigner xiSigner = signer.value();

      for (int i = 0; i < data.length; i++) {
        OutputStream signatureStream = xiSigner.getOutputStream();
        try {
          signatureStream.write(data[i]);
        } catch (IOException ex) {
          throw new SignatureException(
              "could not write data to SignatureStream: " + ex.getMessage(), ex);
        }
        signatures[i] = xiSigner.getSignature();
      }
    } finally {
      requiteSigner(signer);
    }

    return signatures;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy