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

com.venafi.vcert.sdk.connectors.tpp.TppConnector Maven / Gradle / Ivy

Go to download

VCert is a Java library, SDK, designed to simplify key generation and enrollment of machine identities (also known as SSL/TLS certificates and keys) that comply with enterprise security policy by using the Venafi Platform or Venafi Cloud.

There is a newer version: 0.9.3
Show newest version
package com.venafi.vcert.sdk.connectors.tpp;

import static java.lang.String.format;
import static java.time.Duration.ZERO;
import static java.util.Objects.isNull;
import static java.util.stream.Collectors.toList;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

import java.net.InetAddress;
import java.text.MessageFormat;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import com.google.common.annotations.VisibleForTesting;
import com.venafi.vcert.sdk.VCertException;
import com.venafi.vcert.sdk.certificate.CertificateRequest;
import com.venafi.vcert.sdk.certificate.ChainOption;
import com.venafi.vcert.sdk.certificate.CsrOriginOption;
import com.venafi.vcert.sdk.certificate.ImportRequest;
import com.venafi.vcert.sdk.certificate.ImportResponse;
import com.venafi.vcert.sdk.certificate.KeyType;
import com.venafi.vcert.sdk.certificate.PEMCollection;
import com.venafi.vcert.sdk.certificate.PublicKeyAlgorithm;
import com.venafi.vcert.sdk.certificate.RenewalRequest;
import com.venafi.vcert.sdk.certificate.RevocationRequest;
import com.venafi.vcert.sdk.connectors.Connector;
import com.venafi.vcert.sdk.connectors.Policy;
import com.venafi.vcert.sdk.connectors.ServerPolicy;
import com.venafi.vcert.sdk.connectors.ZoneConfiguration;
import com.venafi.vcert.sdk.connectors.tpp.endpoint.*;
import com.venafi.vcert.sdk.endpoint.Authentication;
import com.venafi.vcert.sdk.endpoint.ConnectorType;
import com.venafi.vcert.sdk.policy.api.domain.TPPPolicy;
import com.venafi.vcert.sdk.policy.domain.PolicySpecification;
import com.venafi.vcert.sdk.policy.converter.TPPPolicySpecificationConverter;
import com.venafi.vcert.sdk.utils.Is;
import com.venafi.vcert.sdk.utils.VCertUtils;

import feign.Response;
import lombok.Getter;


public class TppConnector extends AbstractTppConnector implements Connector {
  @VisibleForTesting
  OffsetDateTime bestBeforeEnd;
  @Getter
  private String apiKey;

  private TppAPI tppAPI;

  public TppConnector(Tpp tpp) {
    super(tpp);
  }

  @Override
  public ConnectorType getType() {
    return ConnectorType.TPP;
  }

  @Override
  public void setBaseUrl(String url) throws VCertException {
    throw new UnsupportedOperationException("Method not yet implemented");
  }

  @Override
  public void setZone(String zone) {
    this.zone = zone;
  }

  @Override
  public void setVendorAndProductName(String vendorAndProductName) {
    this.vendorAndProductName = vendorAndProductName;
  }

  @Override
  public String getVendorAndProductName() {
    return vendorAndProductName;
  }

  @Override
  public void ping() throws VCertException {
    Response response = doPing();
    if (response.status() != 200) {
      throw new VCertException(
          format("ping failed with status %d and reason %s", response.status(), response.reason()));
    }
  }

  private Response doPing() {
    return tpp.ping(apiKey);
  }

  public void authenticate(Authentication auth) throws VCertException {
	VCertException.throwIfNull(auth, MISSING_CREDENTIALS_MESSAGE);
    AuthorizeResponse response = tpp.authorize(new AuthorizeRequest(auth.user(), auth.password()));
    apiKey = response.apiKey();
    bestBeforeEnd = response.validUntil();
  }

  @Override
  public ZoneConfiguration readZoneConfiguration(String zone) throws VCertException {
    VCertException.throwIfNull(zone, "empty zone");
    ReadZoneConfigurationRequest request = new ReadZoneConfigurationRequest(getPolicyDN(zone));
    ReadZoneConfigurationResponse response = tpp.readZoneConfiguration(request, apiKey);
    ServerPolicy serverPolicy = response.policy();
    Policy policy = serverPolicy.toPolicy();
    ZoneConfiguration zoneConfig = serverPolicy.toZoneConfig();
    zoneConfig.policy(policy);
    zoneConfig.zoneId(zone);
    return zoneConfig;
  }

  @Override
  public CertificateRequest generateRequest(ZoneConfiguration config, CertificateRequest request)
      throws VCertException {
    // todo: should one really have to pass a request into a "generate request" method?
    if (config == null) {
      config = readZoneConfiguration(zone);
    }
    String tppMgmtType = config.customAttributeValues().get(TPP_ATTRIBUTE_MANAGEMENT_TYPE);
    if ("Monitoring".equals(tppMgmtType) || "Unassigned".equals(tppMgmtType)) {
      throw new VCertException(
          "Unable to request certificate from TPP, current TPP configuration would not allow the request to be processed");
    }

    config.applyCertificateRequestDefaultSettingsIfNeeded(request);

    switch (request.csrOrigin()) {
      case LocalGeneratedCSR: {
        if ("0".equals(config.customAttributeValues().get(TPP_ATTRIBUTE_MANUAL_CSR))) {
          throw new VCertException(
              "Unable to request certificate by local generated CSR when zone configuration is 'Manual Csr' = 0");
        }
        request.generatePrivateKey();
        request.generateCSR();
        break;
      }
      case UserProvidedCSR: {
        if ("0".equals(config.customAttributeValues().get(TPP_ATTRIBUTE_MANUAL_CSR))) {
          throw new VCertException(
              "Unable to request certificate with user provided CSR when zone configuration is 'Manual Csr' = 0");
        }
        if (Is.blank(request.csr())) {
          throw new VCertException("CSR was supposed to be provided by user, but it's empty");
        }
        break;
      }
      case ServiceGeneratedCSR: {
        request.csr(null);
        break;
      }
    }
    return request; // TODO: should we return the request we modified? It's not a copy, it's the one
                    // that was passed in, mutated.
  }

  @Override
  public String requestCertificate(CertificateRequest request, String zone) throws VCertException {
    return requestCertificate(request, new ZoneConfiguration().zoneId(zone));
  }

  @Override
  public String requestCertificate(CertificateRequest request, ZoneConfiguration zoneConfiguration)
      throws VCertException {
    if (isBlank(zoneConfiguration.zoneId())) {
      zoneConfiguration.zoneId(this.zone);
    }
    CertificateRequestsPayload payload = prepareRequest(request, zoneConfiguration.zoneId());
    Tpp.CertificateRequestResponse response = tpp.requestCertificate(payload, apiKey);
    String requestId = response.certificateDN();
    request.pickupId(requestId);
    return requestId;
  }

  private CertificateRequestsPayload prepareRequest(CertificateRequest request, String zone)
      throws VCertException {
    CertificateRequestsPayload payload;
    Collection> caSpecificAttributes =
        new ArrayList>();

    // Workaround to send Origin to TPP versions that does not support it in the payload
    if (!isBlank(vendorAndProductName)) {
      caSpecificAttributes.add(new NameValuePair("Origin", vendorAndProductName));
    }

    switch (request.csrOrigin()) {
      case LocalGeneratedCSR:
        payload = new CertificateRequestsPayload().policyDN(getPolicyDN(zone))
            .pkcs10(new String(request.csr())).objectName(request.friendlyName())
            .disableAutomaticRenewal(true).origin(vendorAndProductName)
            .caSpecificAttributes(caSpecificAttributes);
        break;
      case UserProvidedCSR:
        payload = new CertificateRequestsPayload().policyDN(getPolicyDN(zone))
            .pkcs10(new String(request.csr())).objectName(request.friendlyName())
            .subjectAltNames(wrapAltNames(request)).disableAutomaticRenewal(true)
            .origin(vendorAndProductName).caSpecificAttributes(caSpecificAttributes);
        break;
      case ServiceGeneratedCSR:
        payload = new CertificateRequestsPayload().policyDN(getPolicyDN(zone))
            .objectName(request.friendlyName()).subject(request.subject().commonName()) // TODO (Go
                                                                                        // SDK):
                                                                                        // there is
                                                                                        // some
                                                                                        // problem
                                                                                        // because
                                                                                        // Subject
                                                                                        // is not
                                                                                        // only CN
            .subjectAltNames(wrapAltNames(request)).disableAutomaticRenewal(true)
            .origin(vendorAndProductName).caSpecificAttributes(caSpecificAttributes);
        break;
      default:
        throw new VCertException(MessageFormat.format("Unexpected option in PrivateKeyOrigin: {0}",
            request.csrOrigin()));
    }

    if (request.keyType() == null) {
      request.keyType(KeyType.defaultKeyType());
    }

    switch (request.keyType()) {
      case RSA: {
        payload.keyAlgorithm(PublicKeyAlgorithm.RSA.name());
        payload.keyBitSize(request.keyLength());
        break;
      }
      case ECDSA: {
        payload.keyAlgorithm("ECC");
        payload.ellipticCurve(request.keyCurve().value());
        break;
      }
    }
    
    
    //support for validity hours begins
    VCertUtils.addExpirationDateAttribute(request, payload);
   //support for validity hours ends
    
    //support for custom fields begins
    VCertUtils.addCustomFieldsToRequest(request, payload);
    //support for custom fields ends
    
    return payload;
  }

  private Collection wrapAltNames(CertificateRequest request) {
    List sanItems = new ArrayList<>();
    sanItems.addAll(toSanItems(request.emailAddresses(), 1));
    sanItems.addAll(toSanItems(request.dnsNames(), 2));
    sanItems.addAll(toSanItems(request.ipAddresses(), 7));
    return sanItems;
  }

  private List toSanItems(Collection collection, int type) {
    return Optional.ofNullable(collection).orElse(Collections.emptyList()).stream()
        .filter(Objects::nonNull)
        .map(entry -> new SANItem().type(type)
            .name(type == 7 ? ((InetAddress) entry).getHostAddress() : entry.toString()))
        .collect(toList());
  }

  @Override
  public PEMCollection retrieveCertificate(CertificateRequest request) throws VCertException {
    boolean includeChain = request.chainOption() != ChainOption.ChainOptionIgnore;
    boolean rootFirstOrder =
        includeChain && request.chainOption() == ChainOption.ChainOptionRootFirst;

    if (isNotBlank(request.pickupId()) && isNotBlank(request.thumbprint())) {
      Tpp.CertificateSearchResponse searchResult =
          searchCertificatesByFingerprint(request.thumbprint());
      if (searchResult.certificates().size() == 0) {
        throw new VCertException(
            format("No certificate found using fingerprint %s", request.thumbprint()));
      }
      if (searchResult.certificates().size() > 1) {
        throw new VCertException(format(
            "Error: more than one CertificateRequestId was found with the same thumbprint %s",
            request.thumbprint()));
      }
      request.pickupId(searchResult.certificates().get(0).certificateRequestId());
    }

    CertificateRetrieveRequest certReq =
        new CertificateRetrieveRequest().certificateDN(request.pickupId()).format("base64")
            .rootFirstOrder(rootFirstOrder).includeChain(includeChain);

    if (request.csrOrigin() == CsrOriginOption.ServiceGeneratedCSR || request.fetchPrivateKey()) {
      certReq.includePrivateKey(true);
      certReq.password(request.keyPassword());
    }

    // TODO move this retry logic to feign client
    Instant startTime = Instant.now();
    while (true) {
      Tpp.CertificateRetrieveResponse retrieveResponse = retrieveCertificateOnce(certReq);
      if (isNotBlank(retrieveResponse.certificateData())) {
        PEMCollection pemCollection = PEMCollection.fromResponse(
            org.bouncycastle.util.Strings
                .fromByteArray(Base64.getDecoder().decode(retrieveResponse.certificateData())),
            request.chainOption(), request.privateKey(), request.keyPassword());
        request.checkCertificate(pemCollection.certificate());
        return pemCollection;
      }

      if (ZERO.equals(request.timeout())) {
        throw new VCertException(format("Failed to retrieve certificate %s. Status %s",
            request.pickupId(), retrieveResponse.status()));
      }

      if (Instant.now().isAfter(startTime.plus(request.timeout()))) {
        throw new VCertException(
            format("Timeout trying to retrieve certificate %s", request.pickupId()));
      }

      try {
        TimeUnit.SECONDS.sleep(2);
      } catch (InterruptedException e) {
        e.printStackTrace();
        throw new VCertException("Error attempting to retry", e);
      }
    }
  }

  private Tpp.CertificateRetrieveResponse retrieveCertificateOnce(
      CertificateRetrieveRequest certificateRetrieveRequest) {
    return tpp.certificateRetrieve(certificateRetrieveRequest, apiKey);
  }


  private Tpp.CertificateSearchResponse searchCertificatesByFingerprint(String fingerprint) {
    final Map searchRequest = new HashMap();
    searchRequest.put("Thumbprint", fingerprint);

    return searchCertificates(searchRequest);
  }

  private Tpp.CertificateSearchResponse searchCertificates(Map searchRequest) {
    return tpp.searchCertificates(searchRequest, apiKey);
  }

  @Override
  public void revokeCertificate(RevocationRequest request) throws VCertException {
    Integer reason = revocationReasons.get(request.reason());
    if (reason == null) {
      throw new VCertException(format("could not parse revocation reason `%s`", request.reason()));
    }

    CertificateRevokeRequest revokeRequest = new CertificateRevokeRequest()
        .certificateDN(request.certificateDN()).thumbprint(request.thumbprint()).reason(reason)
        .comments(request.comments()).disable(request.disable());

    Tpp.CertificateRevokeResponse revokeResponse = revokeCertificate(revokeRequest);
    if (!revokeResponse.success()) {
      throw new VCertException(format("Revocation error: %s", revokeResponse.error()));
    }
  }

  private Tpp.CertificateRevokeResponse revokeCertificate(CertificateRevokeRequest request) {
    return tpp.revokeCertificate(request, apiKey);
  }

  @Override
  public String renewCertificate(RenewalRequest request) throws VCertException {
    String certificateDN;

    if (isNotBlank(request.thumbprint()) && isBlank(request.certificateDN())) {
      Tpp.CertificateSearchResponse searchResult =
          searchCertificatesByFingerprint(request.thumbprint());
      if (searchResult.certificates().isEmpty()) {
        throw new VCertException(
            String.format("No certificate found using fingerprint %s", request.thumbprint()));
      }
      if (searchResult.certificates().size() > 1) {
        throw new VCertException(
            String.format("More than one certificate was found with the same thumbprint"));
      }
      certificateDN = searchResult.certificates().get(0).certificateRequestId();
    } else {
      certificateDN = request.certificateDN();
    }

    if (isNull(certificateDN)) {
      throw new VCertException(
          "Failed to create renewal request: CertificateDN or Thumbprint required");
    }

    final CertificateRenewalRequest renewalRequest = new CertificateRenewalRequest();
    renewalRequest.certificateDN(certificateDN);

    if (Objects.nonNull(request.request()) && request.request().csr().length > 0) {
      String pkcs10 = org.bouncycastle.util.Strings.fromByteArray(request.request().csr());
      renewalRequest.PKCS10(pkcs10);
    }

    final Tpp.CertificateRenewalResponse response = tpp.renewCertificate(renewalRequest, apiKey());
    if (!response.success()) {
      throw new VCertException(String.format("Certificate renewal error: %s", response.error()));
    }

    return certificateDN;
  }

  @Override
  public ImportResponse importCertificate(ImportRequest request) throws VCertException {
    if (isBlank(request.policyDN())) {
      request.policyDN(getPolicyDN(zone));
    }

    return doImportCertificate(request);
  }

  private ImportResponse doImportCertificate(ImportRequest request) {
    return tpp.importCertificate(request, apiKey);
  }

  @Override
  public Policy readPolicyConfiguration(String zone) throws VCertException {
    throw new UnsupportedOperationException("Method not yet implemented");
  }

  @Override
  public void setPolicy(String policyName, PolicySpecification policySpecification) throws VCertException {
    try {
      TPPPolicy tppPolicy = TPPPolicySpecificationConverter.INSTANCE.convertFromPolicySpecification(policySpecification);
      setPolicy(policyName, tppPolicy);
    }catch (Exception e){
      throw new VCertException(e);
    }
  }

  @Override
  public PolicySpecification getPolicy(String policyName) throws VCertException {
    PolicySpecification policySpecification;
    try {
      TPPPolicy tppPolicy = getTPPPolicy(policyName);

      policySpecification = TPPPolicySpecificationConverter.INSTANCE.convertToPolicySpecification( tppPolicy );

    }catch (Exception e){
      throw new VCertException(e);
    }

    return policySpecification;
  }

  @Override
  protected TppAPI getTppAPI() {

    if(tppAPI == null){
      tppAPI = new TppAPI(tpp) {

        @Override
        String getAuthKey() throws VCertException {
          if(apiKey() == null)
            throw new VCertException("API Key is null");

          return apiKey();
        }

        @Override
        public DNIsValidResponse dnIsValid(DNIsValidRequest request) throws VCertException {
          return tpp.dnIsValid(request, getAuthKey());
        }

        @Override
        CreateDNResponse createDN(CreateDNRequest request) throws VCertException {
          return tpp.createDN(request, getAuthKey());
        }

        @Override
        SetPolicyAttributeResponse setPolicyAttribute(SetPolicyAttributeRequest request) throws VCertException {
          return tpp.setPolicyAttribute(request, getAuthKey());
        }

        @Override
        GetPolicyAttributeResponse getPolicyAttribute(GetPolicyAttributeRequest request) throws VCertException {
          return tpp.getPolicyAttribute(request, getAuthKey());
        }

        @Override
        GetPolicyResponse getPolicy(GetPolicyRequest request) throws VCertException {
          return tpp.getPolicy(request, getAuthKey());
        }

        @Override
        Response clearPolicyAttribute(ClearPolicyAttributeRequest request) throws VCertException {
          return tpp.clearPolicyAttribute(request, getAuthKey());
        }
      };
    }
    return tppAPI;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy