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

com.venafi.vcert.sdk.connectors.cloud.CloudConnector 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.cloud;

import static com.venafi.vcert.sdk.connectors.cloud.CloudConnectorException.*;
import static java.lang.String.format;
import static java.time.Duration.ZERO;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

import java.io.IOException;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import com.venafi.vcert.sdk.connectors.cloud.domain.*;
import com.venafi.vcert.sdk.policy.api.domain.CloudPolicy;
import com.venafi.vcert.sdk.policy.domain.PolicySpecification;
import com.venafi.vcert.sdk.policy.converter.CloudPolicySpecificationConverter;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.util.Strings;

import com.google.common.io.CharStreams;
import com.google.gson.annotations.SerializedName;
import com.venafi.vcert.sdk.VCertException;
import com.venafi.vcert.sdk.certificate.CertificateRequest;
import com.venafi.vcert.sdk.certificate.CertificateStatus;
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.PEMCollection;
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.ZoneConfiguration;
import com.venafi.vcert.sdk.endpoint.Authentication;
import com.venafi.vcert.sdk.endpoint.ConnectorType;
import com.venafi.vcert.sdk.utils.VCertUtils;

import feign.Response;
import lombok.Data;
import lombok.Getter;
	
public class CloudConnector implements Connector {

  private Cloud cloud;

  @Getter
  private UserDetails user;
  private Authentication auth;
  private String zone;
  @Getter
  private String vendorAndProductName;

  public CloudConnector(Cloud cloud) {
    this.cloud = cloud;
  }

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

  @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 UnexpectedStatusException( response.status(), response.reason());
    }
  }

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

  @Override
  public void authenticate(Authentication auth) throws VCertException {
    VCertException.throwIfNull(auth, "Failed to authenticate: missing credentials");
    this.auth = auth;
    this.user = cloud.authorize(auth.apiKey());
  }

  @Override
  public ZoneConfiguration readZoneConfiguration(String zone) throws VCertException {
	  
	  String valies[] = StringUtils.split(zone, "\\");
	  String appName = valies[0];
	  String citAlias = valies[1];
	  
	  CertificateIssuingTemplate cit = null;
	    String zoneId = null;
	    if((appName != null && appName != "") && (citAlias != null && citAlias != "")) {
	    	
	    	 cit = cloud.certificateIssuingTemplateByAppNameAndCitAlias(appName, citAlias, auth.apiKey());
	    	
	    }else {
	    	  throw new ZoneFormatException("The parameters: appName, citAlias or both are empty");
	    }
	    
	    //get application id.
	    Application app = cloud.applicationByName(appName, auth.apiKey());
	    String appId = app.id();

	    ZoneConfiguration zoneConfig = cit.toZoneConfig();
	    zoneConfig.policy(cit.toPolicy());
	    zoneConfig.zoneId(zoneId);
	    zoneConfig.applicationId(appId);
	    zoneConfig.certificateIssuingTemplateId(cit.id());

	    return zoneConfig;
  }

  @Override
  public CertificateRequest generateRequest(ZoneConfiguration zoneConfig,
      CertificateRequest request) throws VCertException {
    switch (request.csrOrigin()) {
      case LocalGeneratedCSR:
        if (zoneConfig == null) {
          zoneConfig = readZoneConfiguration(zone);
        }
        zoneConfig.applyCertificateRequestDefaultSettingsIfNeeded(request);
        zoneConfig.validateCertificateRequest(request);
        request.generatePrivateKey();
        request.generateCSR();
        break;
      case UserProvidedCSR:
        if (request.csr().length == 0) {
          throw new CSRNotProvidedByUserException();
        }
        break;
      case ServiceGeneratedCSR:
        request.csr(null);
        break;
      default:
        throw new UnreconigzedCSROriginException(request.csrOrigin());
    }

    return request;
  }

  @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);
    }

    if (CsrOriginOption.ServiceGeneratedCSR == request.csrOrigin()) {
      throw new UnsupportedServiceGeneratedCSRException();
    }
    if (user == null || user.company() == null) {
      throw new UserNotAuthenticatedException("Must be authenticated to request a certificate");
    }
    
    CertificateRequestsPayload payload = new CertificateRequestsPayload()
    .zoneId(zoneConfiguration.zoneId()).csr(new String(request.csr()));
    
    //support for validity hours begins
    if( request.validityHours() > 0 ) {	
    	
    	String validityHours =  "PT" + request.validityHours() + "H";
    	payload.validityPeriod(validityHours);
    	
    }
    //support for validity hours ends
    
    //add certificateIssuingTemplate and applicationId
    payload.applicationId(zoneConfiguration.applicationId());
    payload.certificateIssuingTemplateId(zoneConfiguration.certificateIssuingTemplateId());

    //add client information
    VCertUtils.addApiClientInformation(payload);

    
    CertificateRequestsResponse response =
        cloud.certificateRequest( auth.apiKey(), payload );

    String requestId = response.certificateRequests().get(0).id();
    request.pickupId(requestId);
    return requestId;
  }

  @Override
  public PEMCollection retrieveCertificate(CertificateRequest request) throws VCertException {
	  CertificateStatus certificateStatus = null;
    if (request.fetchPrivateKey()) {
      throw new UnsupportedPrivateKeyRetrieveException();
    }
    String certId = "";
    if (isBlank(request.pickupId()) && isNotBlank(request.thumbprint())) {
      String certificateRequestId = null;
      Cloud.CertificateSearchResponse certificateSearchResponse =
          searchCertificatesByFingerprint(request.thumbprint());
      if (certificateSearchResponse.certificates().size() == 0) {
        throw new CertificateNotFoundByFingerprintException(request.thumbprint());
      }

      List reqIds = new ArrayList<>();
      boolean isOnlyOneCertificateRequestId = true;
      for (Cloud.Certificate certificate : certificateSearchResponse.certificates()) {
        reqIds.add(certificate.certificateRequestId());
        if (isNotBlank(certificateRequestId)
            && certificateRequestId.equals(certificate.certificateRequestId())) {
          isOnlyOneCertificateRequestId = false;
        }
        if (isNotBlank(certificate.certificateRequestId())) {
          certificateRequestId = certificate.certificateRequestId();
        } else {
          certId = certificate.id();
        }
      }
      if (!isOnlyOneCertificateRequestId) {
        throw new MoreThanOneCertificateRequestIdException(reqIds);
      }
      request.pickupId(certificateRequestId);
    }

    // TODO move this retry logic to feign client
    Instant startTime = Instant.now();
    while (true) {
      if (isBlank(request.pickupId())) {
        break;
      }

      certificateStatus = getCertificateStatus(request.pickupId());
      if ("ISSUED".equals(certificateStatus.status())) {
        break;
      } else if ("FAILED".equals(certificateStatus.status())) {
        throw new CertificateStatusFailedException( certificateStatus.toString());
      }

      // Status either REQUESTED or PENDING
      if (ZERO.equals(request.timeout())) {
        throw new CertificatePendingException(request.pickupId());
      }

      if (Instant.now().isAfter(startTime.plus(request.timeout()))) {
        throw new RetrieveCertificateTimeoutException(request.pickupId());
      }

      try {
        TimeUnit.SECONDS.sleep(2);
      } catch (InterruptedException e) {
        e.printStackTrace();
        throw new AttemptToRetryException(e);
      }
    }

    if (user == null || user.company() == null) {
      throw new UserNotAuthenticatedException("Must be authenticated to retieve certificate");
    }
    
    if(certificateStatus == null) {
    	throw new FailedToRetrieveCertificateStatusException(request.pickupId());
    }
    
    String certificateId = certificateStatus.certificateIds().get(0);

    if (isNotBlank(certificateId)) {

      // Todo cleanup unnecessary switch
      String chainOption;
      switch (request.chainOption()) {
        case ChainOptionRootFirst:
          chainOption = "ROOT_FIRST";
          break;
        case ChainOptionRootLast:
        case ChainOptionIgnore:
        default:
          chainOption = "EE_FIRST";
          break;
      }
      String body = certificateViaCSR(certificateId, chainOption, request);
      PEMCollection pemCollection =
          PEMCollection.fromResponse(body, request.chainOption(), request.privateKey(),
            request.keyPassword());
      request.checkCertificate(pemCollection.certificate());
      return pemCollection;
    } else {
      String body = certificateAsPem(certId, request);
      return PEMCollection.fromResponse(body, ChainOption.ChainOptionIgnore,
        request.privateKey(), request.keyPassword());
    }
  }

  private String certificateViaCSR(String certificateId, String chainOrder, CertificateRequest request) throws VCertException {
	  // We should decode this as is not REST, multiple decoders should be supported
	  // by feign as a potential improvement.
	  Instant startTime = Instant.now();
	  while (true) {

		  Response response = cloud.certificateViaCSR(certificateId, auth.apiKey(), chainOrder);
		  if (response.status() == 200) {
			  try {
				  return CharStreams.toString(response.body().asReader());
			  } catch (IOException e) {
				  throw new UnableToReadPEMCertificateException(certificateId);
			  }
		  }

		  // Status either REQUESTED or PENDING
		  if (ZERO.equals(request.timeout())) {
			  throw new CertificatePendingException(request.pickupId());
		  }

		  if (Instant.now().isAfter(startTime.plus(request.timeout()))) {
			  throw new RetrieveCertificateTimeoutException(request.pickupId());
		  }

		  try {
			  TimeUnit.SECONDS.sleep(2);
		  } catch (InterruptedException e) {
			  e.printStackTrace();
			  throw new AttemptToRetryException(e);
		  }
	  }
  }

  private String certificateAsPem(String certificateId, CertificateRequest request) throws VCertException{
	  
	  Instant startTime = Instant.now();
	  while (true) {

		  Response response = cloud.certificateAsPem(certificateId, auth.apiKey());
		  if (response.status() == 200) {
			  try {
				  return CharStreams.toString(response.body().asReader());
			  } catch (IOException e) {
				  throw new UnableToReadPEMCertificateException(certificateId);
			  }
		  }

		  // Status either REQUESTED or PENDING
		  if (ZERO.equals(request.timeout())) {
			  throw new CertificatePendingException(request.pickupId());
		  }

		  if (Instant.now().isAfter(startTime.plus(request.timeout()))) {
			  throw new RetrieveCertificateTimeoutException(request.pickupId());
		  }

		  try {
			  TimeUnit.SECONDS.sleep(2);
		  } catch (InterruptedException e) {
			  e.printStackTrace();
			  throw new AttemptToRetryException(e);
		  }
	  }
  }
  
  /**
   * @deprecated
   * @param requestId
   * @return
   * @throws VCertException
   */
  public String certificateAsPem(String requestId) throws VCertException{
	  Response response = cloud.certificateAsPem(requestId, auth.apiKey());
	  if (response.status() != 200) {
		  throw new VCertException(String
				  .format("Invalid response fetching the certificate via CSR: %s", response.reason()));
	  }
	  try {
		  return CharStreams.toString(response.body().asReader());
	  } catch (IOException e) {
		  throw new VCertException("Unable to read the PEM certificate");
	  }
  }

  private CertificateStatus getCertificateStatus(String requestId) {
    return cloud.certificateStatus(requestId, auth.apiKey());
  }

  @Override
  public void revokeCertificate(RevocationRequest request) throws VCertException {
    throw new UnsupportedOperationException("not supported by endpoint");
  }

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

    String certificateRequestId = null;

    if (isNotBlank(request.thumbprint())) {
      Cloud.CertificateSearchResponse result =
          this.searchCertificatesByFingerprint(request.thumbprint());
      Set requestIds = result.certificates().stream().map(c -> c.certificateRequestId())
          .collect(Collectors.toSet());

      if (requestIds.size() > 1) {
        throw new MoreThanOneCertificateRequestIdException(request.thumbprint());
      } else if (requestIds.size() == 0) {
        throw new CertificateNotFoundByFingerprintException(request.thumbprint());
      }

      certificateRequestId = requestIds.iterator().next();

    } else if (isNotBlank(request.certificateDN())) {
      certificateRequestId = request.certificateDN();
    } else {
      throw new CertificateDNOrFingerprintWasNotProvidedException();
    }

    final CertificateStatus status = cloud.certificateStatus(certificateRequestId, auth.apiKey());
    
    String certificateId = status.certificateIds().get(0);
    
    
    CertificateDetails certDetails = cloud.certificateDetails(certificateId, auth.apiKey());
    
    if (!certDetails.certificateRequestId().equals(certificateRequestId)) {
      final StringBuilder errorStr = new StringBuilder();
      errorStr.append("Certificate under requestId %s ");
      errorStr.append(isNotBlank(request.thumbprint())
          ? String.format("with thumbprint %s ", request.thumbprint())
          : "");
      errorStr
          .append("is not the latest under ManagedCertificateId %s. The latest request is %s. ");
      errorStr.append("This error may happen when revoked certificate is requested to be renewed.");

      throw new VCertException(String.format(errorStr.toString(), certificateRequestId,
    		  certDetails.id(), certDetails.certificateRequestId()));
    }

    final CertificateRequestsPayload certificateRequest = new CertificateRequestsPayload();
    certificateRequest.existingCertificateId(certDetails.id());
    certificateRequest.applicationId(status.applicationId());
    certificateRequest.certificateIssuingTemplateId(status.certificateIssuingTemplateId());
    
    //add client information
    VCertUtils.addApiClientInformation(certificateRequest);
    
  
    certificateRequest
        .reuseCSR(!(Objects.nonNull(request.request()) && request.request().csr().length > 0));
    if (!certificateRequest.reuseCSR) {
      certificateRequest.csr(Strings.fromByteArray(request.request().csr()));
    }else {
    	throw new CSRNotProvidedException();
    }

    CertificateRequestsResponse response =
        cloud.certificateRequest(auth.apiKey(), certificateRequest);
    return response.certificateRequests().get(0).id();
  }

  @Override
  public ImportResponse importCertificate(ImportRequest request) throws VCertException {
    throw new UnsupportedOperationException("Method not yet implemented");
  }

  @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 {
      CloudPolicy cloudPolicy = CloudPolicySpecificationConverter.INSTANCE.convertFromPolicySpecification(policySpecification);
      CloudConnectorUtils.setCit(policyName, cloudPolicy.certificateIssuingTemplate(), cloudPolicy.caInfo(), auth.apiKey(), cloud);
    } catch ( Exception e ) {
      throw new VCertException(e);
    }
  }

  @Override
  public PolicySpecification getPolicy(String policyName) throws VCertException {
    PolicySpecification policySpecification;
    try {
      CloudPolicy cloudPolicy = CloudConnectorUtils.getCloudPolicy( policyName, auth.apiKey(), cloud );
      policySpecification = CloudPolicySpecificationConverter.INSTANCE.convertToPolicySpecification( cloudPolicy );
    }catch (Exception e){
      throw new VCertException(e);
    }

    return policySpecification;
  }

  private Cloud.CertificateSearchResponse searchCertificates(Cloud.SearchRequest searchRequest) {
    return cloud.searchCertificates(auth.apiKey(), searchRequest);
  }

  private Cloud.CertificateSearchResponse searchCertificatesByFingerprint(String fingerprint) {
    String cleanFingerprint = fingerprint.replaceAll(":", "").replaceAll("/.", "");

    return searchCertificates(Cloud.SearchRequest.findByFingerPrint(cleanFingerprint));
  }

  private String[] parseZoneIdentifiers(String zone) throws VCertException {
    try {
      // Check if zone is UUID
      UUID.fromString(zone);
      return new String[] {zone, null, null};
    } catch (IllegalArgumentException iae) {
      // The zone argument is not UUID, so we expect to be ProjectName\ZoneName
      String zoneParsed[] = zone.split(Pattern.quote("\\"));

      if (zoneParsed.length != 2) {
        throw new VCertException(format(
            "Invalid zone ID or path. We expect UUID or 'ProjectName\\ZoneName', but we got '%s'.",
            zone));
      }

      if (isBlank(zoneParsed[0])) {
        throw new VCertException(format("Unable to get Project Name from '%s'", zone));
      }

      if (isBlank(zoneParsed[1])) {
        throw new VCertException(format("Unable to get Zone Name from '%s'", zone));
      }
      return new String[] {null, zoneParsed[0], zoneParsed[1]};
    }
  }

  @Data
  public static class CertificateRequestsPayload {
    // private String companyId;
    // private String downloadFormat;
    @SerializedName("certificateSigningRequest")
    private String csr;
    private String zoneId;
    private String existingManagedCertificateId;
    private boolean reuseCSR;
    private String validityPeriod;
    private String applicationId;
    private String certificateIssuingTemplateId;
    private String existingCertificateId;
    private ApiClientInformation apiClientInformation;
  }

  @Data
  public static class CertificateRequestsResponse {
    private List certificateRequests;
  }

  @Data
  static class CertificateRequestsResponseData {
    private String id;
    private String zoneId;
    private String status;
    private String subjectDN;
    private boolean generatedKey;
    private boolean defaultKeyPassword;
    private Collection certificateInstanceIds;
    private OffsetDateTime creationDate;
    private String pem;
    private String der;
  }
  
  @Data
  public static class ApiClientInformation{
	  String type;
	  String identifier;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy