diff --git a/pom.xml b/pom.xml index 0a91269..67e85e9 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ - 1.18.6 + 1.18.20 1.67 10.4.0 30.1.1-jre @@ -76,6 +76,7 @@ 4.5.13 4.4.14 3.0.3-FINAL + 1.1.2 @@ -191,6 +192,11 @@ ${maverick-base.version} test + + org.purejava + tweetnacl-java + ${tweetnacl-java.version} + @@ -200,8 +206,8 @@ maven-compiler-plugin 3.6.1 - 8 - 8 + 11 + 11 diff --git a/src/main/java/com/venafi/vcert/sdk/certificate/CertificateRequest.java b/src/main/java/com/venafi/vcert/sdk/certificate/CertificateRequest.java index b805bdc..12ec9dd 100644 --- a/src/main/java/com/venafi/vcert/sdk/certificate/CertificateRequest.java +++ b/src/main/java/com/venafi/vcert/sdk/certificate/CertificateRequest.java @@ -64,6 +64,7 @@ public class CertificateRequest { private KeyPair keyPair; private CsrOriginOption csrOrigin = CsrOriginOption.defaultCsrOrigin(); private String pickupId; + private String certId; private ChainOption chainOption; private String keyPassword; private boolean fetchPrivateKey; diff --git a/src/main/java/com/venafi/vcert/sdk/certificate/PEMCollection.java b/src/main/java/com/venafi/vcert/sdk/certificate/PEMCollection.java index d373f3d..51ffcde 100644 --- a/src/main/java/com/venafi/vcert/sdk/certificate/PEMCollection.java +++ b/src/main/java/com/venafi/vcert/sdk/certificate/PEMCollection.java @@ -41,12 +41,15 @@ import org.bouncycastle.openssl.bc.BcPEMDecryptorProvider; import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator; import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder; import org.bouncycastle.openssl.jcajce.JcePEMEncryptorBuilder; +import org.bouncycastle.operator.InputDecryptorProvider; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.OutputEncryptor; import org.bouncycastle.pkcs.PKCS12PfxPdu; import org.bouncycastle.pkcs.PKCS12PfxPduBuilder; import org.bouncycastle.pkcs.PKCS12SafeBag; +import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo; import org.bouncycastle.pkcs.PKCSException; import org.bouncycastle.pkcs.jcajce.JcaPKCS12SafeBagBuilder; import org.bouncycastle.pkcs.jcajce.JcePKCS12MacCalculatorBuilder; @@ -329,6 +332,14 @@ public static SecretKeySpec passwordToCipherSecretKey(char[] password, byte[] iv byte[] key = keyFactory.generateSecret(spec).getEncoded(); return new SecretKeySpec(key, SECRET_KEY_ALGORITHM); } + + public static PrivateKey decryptPKCS8PrivateKey(PEMParser pemParser, String keyPassword) throws IOException, OperatorCreationException, PKCSException{ + PKCS8EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = (PKCS8EncryptedPrivateKeyInfo) pemParser.readObject(); + InputDecryptorProvider pkcs8Prov = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(keyPassword.toCharArray()); + JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC"); + PrivateKeyInfo decryptedPrivateKeyInfo = encryptedPrivateKeyInfo.decryptPrivateKeyInfo(pkcs8Prov); + return converter.getPrivateKey(decryptedPrivateKeyInfo); + } @Data public static class RawPrivateKey { diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/ConnectorException.java b/src/main/java/com/venafi/vcert/sdk/connectors/ConnectorException.java index 0cc041e..565b6a7 100644 --- a/src/main/java/com/venafi/vcert/sdk/connectors/ConnectorException.java +++ b/src/main/java/com/venafi/vcert/sdk/connectors/ConnectorException.java @@ -6,6 +6,8 @@ import static java.lang.String.format; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import com.venafi.vcert.sdk.VCertException; import com.venafi.vcert.sdk.certificate.CsrOriginOption; @@ -384,5 +386,57 @@ public CAOrGUIDNotProvidedException() { super("CA template or GUID are not specified"); } } + + public static class PolicyMatchException extends ConnectorException { + + private static final long serialVersionUID = 1L; + + private static String formatArrayToString(String[] arrayOfStrings) { + return Stream.of(arrayOfStrings).collect(Collectors.joining(",","[","]")); + } + + String policySpecificationAttribute; + String[] policySpecificationAttributeValues; + String policyAttribute; + String[] policyAttributeValues; + + public PolicyMatchException(String policySpecificationAttribute, String policySpecificationAttributeValues + , String policyAttribute, String[] policyAttributeValues) { + this(policySpecificationAttribute, new String[] {policySpecificationAttributeValues}, policyAttribute, policyAttributeValues); + } + + public PolicyMatchException(String policySpecificationAttribute, String[] policySpecificationAttributeValues + , String policyAttribute, String[] policyAttributeValues) { + + super(format("Specified %s %s, doesn't match with policy's specified %s %s" + , policySpecificationAttribute, formatArrayToString(policySpecificationAttributeValues) + , policyAttribute, formatArrayToString(policyAttributeValues))); + + this.policySpecificationAttribute = policySpecificationAttribute; + this.policySpecificationAttributeValues = policySpecificationAttributeValues; + this.policyAttribute = policyAttribute; + this.policyAttributeValues = policyAttributeValues; + } + } + + public static class UndeterminedCertIdException extends ConnectorException { + + private static final long serialVersionUID = 1L; + + public UndeterminedCertIdException() { + super("It wasn't possible to determine the certificate Id using the pickupId " + + "or the thumbprint from the CertificateRequest."); + } + } + + public static class PickupIdOrThumbprintNotSetToGetCertIdException extends ConnectorException { + + private static final long serialVersionUID = 1L; + + public PickupIdOrThumbprintNotSetToGetCertIdException() { + super("It's not being provided neither the pickupId or thumbprint " + + "in the CertificateRequest to determine the certificate Id."); + } + } } diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/cloud/Cloud.java b/src/main/java/com/venafi/vcert/sdk/connectors/cloud/Cloud.java index 6c7c2e4..79c450d 100644 --- a/src/main/java/com/venafi/vcert/sdk/connectors/cloud/Cloud.java +++ b/src/main/java/com/venafi/vcert/sdk/connectors/cloud/Cloud.java @@ -12,6 +12,7 @@ import com.venafi.vcert.sdk.connectors.cloud.domain.Application; import com.venafi.vcert.sdk.connectors.cloud.domain.CertificateDetails; import com.venafi.vcert.sdk.connectors.cloud.domain.CertificateIssuingTemplate; +import com.venafi.vcert.sdk.connectors.cloud.domain.EdgeEncryptionKey; import com.venafi.vcert.sdk.connectors.cloud.domain.UserDetails; import com.venafi.vcert.sdk.connectors.cloud.endpoint.*; import com.venafi.vcert.sdk.utils.FeignUtils; @@ -58,7 +59,7 @@ CloudConnector.CertificateRequestsResponse certificateRequest(@Param("apiKey") S @Headers("tppl-api-key: {apiKey}") @RequestLine("GET /outagedetection/v1/certificates/{id}/contents?chainOrder={chainOrder}&format=PEM") - Response certificateViaCSR(@Param("id") String id, @Param("apiKey") String apiKey, + Response retrieveCertificate(@Param("id") String id, @Param("apiKey") String apiKey, @Param("chainOrder") String chainOrder); @Headers({"tppl-api-key: {apiKey}"}) @@ -100,6 +101,14 @@ Response certificateViaCSR(@Param("id") String id, @Param("apiKey") String apiKe @Headers({"tppl-api-key: {apiKey}", "Content-Type: application/json"}) @RequestLine("PUT /outagedetection/v1/applications/{id}") Application updateApplication(Application application, @Param("id") String id, @Param("apiKey") String apiKey); + + @Headers({"tppl-api-key: {apiKey}"}) + @RequestLine("GET /v1/edgeencryptionkeys/{id}") + EdgeEncryptionKey retrieveEdgeEncryptionKey(@Param("id") String id, @Param("apiKey") String apiKey); + + @Headers({"tppl-api-key: {apiKey}", "Content-Type: application/json"}) + @RequestLine("POST /outagedetection/v1/certificates/{id}/keystore") + Response retrieveKeystore(@Param("id") String id, KeystoreRequest keystoreRequest, @Param("apiKey") String apiKey); static Cloud connect(String baseUrl) { return FeignUtils.client(Cloud.class, diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/cloud/CloudConnector.java b/src/main/java/com/venafi/vcert/sdk/connectors/cloud/CloudConnector.java index 0c90606..37578a2 100644 --- a/src/main/java/com/venafi/vcert/sdk/connectors/cloud/CloudConnector.java +++ b/src/main/java/com/venafi/vcert/sdk/connectors/cloud/CloudConnector.java @@ -7,9 +7,11 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.io.IOException; +import java.io.InputStream; import java.time.Instant; import java.time.OffsetDateTime; import java.util.ArrayList; +import java.util.Base64; import java.util.Collection; import java.util.List; import java.util.Objects; @@ -20,6 +22,7 @@ import java.util.stream.Collectors; import com.venafi.vcert.sdk.connectors.cloud.domain.*; +import com.venafi.vcert.sdk.connectors.cloud.endpoint.KeystoreRequest; 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; @@ -31,7 +34,6 @@ 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; @@ -54,6 +56,8 @@ import lombok.Getter; public class CloudConnector implements Connector { + + private static String APPLICATION_SERVER_TYPE_ID = "784938d1-ef0d-11eb-9461-7bb533ba575b"; private Cloud cloud; @@ -135,7 +139,7 @@ public ZoneConfiguration readZoneConfiguration(String zone) throws VCertExceptio ZoneConfiguration zoneConfig = cit.toZoneConfig(); zoneConfig.policy(cit.toPolicy()); - zoneConfig.zoneId(zoneId); + zoneConfig.zoneId(zone); zoneConfig.applicationId(appId); zoneConfig.certificateIssuingTemplateId(cit.id()); @@ -177,163 +181,141 @@ public String requestCertificate(CertificateRequest request, String zone) throws @Override public String requestCertificate(CertificateRequest request, ZoneConfiguration zoneConfiguration) - throws VCertException { + throws VCertException { - if (isBlank(zoneConfiguration.zoneId())) { - zoneConfiguration.zoneId(this.zone); - } + if (isBlank(zoneConfiguration.zoneId())) { + zoneConfiguration.zoneId(this.zone); + } + + if (user == null || user.company() == null) { + throw new UserNotAuthenticatedException("Must be authenticated to request a certificate"); + } + + CertificateRequestsPayload payload = buildRequestCertificatePayload(request, zoneConfiguration); + + CertificateRequestsResponse response = + cloud.certificateRequest( auth.apiKey(), payload ); - 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()); + String requestId = response.certificateRequests().get(0).id(); + request.pickupId(requestId); + return requestId; + } + + private CertificateRequestsPayload buildRequestCertificatePayload(CertificateRequest request, ZoneConfiguration zoneConfiguration) throws VCertException { + + CertificateRequestsPayload payload = new CertificateRequestsPayload(); + + if (CsrOriginOption.ServiceGeneratedCSR == request.csrOrigin()) { + payload.isVaaSGenerated(true); + payload.applicationServerTypeId(APPLICATION_SERVER_TYPE_ID); + PolicySpecification policySpecification = getPolicy(zoneConfiguration.zoneId(), false); + payload.csrAttributes( CloudConnectorUtils.buildCsrAttributes(request, policySpecification)); + + } else { + payload.csr(new String(request.csr())); + } + + //support for validity hours begins + if( request.validityHours() > 0 ) { + String validityHours = "PT" + request.validityHours() + "H"; + payload.validityPeriod(validityHours); - //add client information - VCertUtils.addApiClientInformation(payload); + } + //support for validity hours ends - - CertificateRequestsResponse response = - cloud.certificateRequest( auth.apiKey(), payload ); + //add certificateIssuingTemplate and applicationId + payload.applicationId(zoneConfiguration.applicationId()); + payload.certificateIssuingTemplateId(zoneConfiguration.certificateIssuingTemplateId()); - String requestId = response.certificateRequests().get(0).id(); - request.pickupId(requestId); - return requestId; - } + //add client information + VCertUtils.addApiClientInformation(payload); + return payload; + } + @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 CertificateNotFoundByThumbprintException(request.thumbprint()); - } + //The next logic is to try to ensure that the request.certId exists. + if (isBlank(request.certId())) + setCertId(request); - 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); - } - } + //At this point,it's sure that the request.certId exists + //Only it's required to determine whether the certificates was generated with a CSR generated on the server + //or if that was generated with a CSR Local generated + return getCertificateAsPEMCollection(request); + } + + /** + * This method determines the certId and set it to the {@link CertificateRequest} object passed as argument. + * For that it will be used the {@link CertificateRequest#pickupId} to determine the certId. If that is not set + * then it will be used the {@link CertificateRequest#thumbprint}. + * @param request + * @throws VCertException if it was not possible to determine the certId using the values of picukpId or the thumbprint. + */ + private void setCertId(CertificateRequest request) throws VCertException { + + if (isBlank(request.pickupId())) { + if (isNotBlank(request.thumbprint())) { + setIdentifiersUsingThumbprint(request); + if (isBlank(request.certId()) && isBlank(request.pickupId())) { + throw new UndeterminedCertIdException(); + } else { + if (isBlank(request.certId()) && isNotBlank(request.pickupId())) { + request.certId(getCertificateIdFromPickupId(request)); + } + } + } else { + throw new PickupIdOrThumbprintNotSetToGetCertIdException(); + } + } else + request.certId(getCertificateIdFromPickupId(request)); + } + + private void setIdentifiersUsingThumbprint(CertificateRequest request) throws VCertException { + String pickupId = null; + String certId = null; + + Cloud.CertificateSearchResponse certificateSearchResponse = + searchCertificatesByFingerprint(request.thumbprint()); + if (certificateSearchResponse.certificates().size() == 0) { + throw new CertificateNotFoundByThumbprintException(request.thumbprint()); + } - 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()); - } + List reqIds = new ArrayList<>(); + boolean isOnlyOneCertificateRequestId = true; + for (Cloud.Certificate certificate : certificateSearchResponse.certificates()) { + reqIds.add(certificate.certificateRequestId()); + if (isNotBlank(pickupId) + && !pickupId.equals(certificate.certificateRequestId())) { + isOnlyOneCertificateRequestId = false; + break; + } + if (isNotBlank(certificate.certificateRequestId())) { + pickupId = certificate.certificateRequestId(); + } + + if (isNotBlank(certificate.id())) { + certId = certificate.id(); + } + } + if (!isOnlyOneCertificateRequestId) { + throw new MoreThanOneCertificateRequestIdException(reqIds); + } + request.pickupId(pickupId); + request.certId(certId); } - 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. + private String getCertificateIdFromPickupId(CertificateRequest request) throws VCertException { + CertificateStatus certificateStatus = null; + 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); - } + 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 @@ -352,19 +334,55 @@ private String certificateViaCSR(String certificateId, String chainOrder, Certif 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()); + } + + return certificateStatus.certificateIds().get(0); } - private String certificateAsPem(String certificateId, CertificateRequest request) throws VCertException{ + private PEMCollection getCertificateAsPEMCollection(CertificateRequest request) throws VCertException { + String vaasChainOption = CloudConnectorUtils.getVaaSChainOption(request.chainOption()); + + PEMCollection pemCollection = getCertificateAsPEMCollection(request, vaasChainOption); + + request.checkCertificate(pemCollection.certificate()); + + return pemCollection; + } + + private PEMCollection getCertificateAsPEMCollection(CertificateRequest request, String vaasChainOption) throws VCertException { + + CertificateDetails certificateDetails = cloud.certificateDetails(request.certId(), auth.apiKey()); + EdgeEncryptionKey edgeEncryptionKey = cloud.retrieveEdgeEncryptionKey(certificateDetails.dekHash(), auth.apiKey()); + + if(isNotBlank(edgeEncryptionKey.key())) { + byte[] serverPublicKey = Base64.getDecoder().decode(edgeEncryptionKey.key()); + return retrieveCertificateAsPemCollectionFromCSRServiceGenerated(request, serverPublicKey, vaasChainOption); + } else + return retrieveCertificateAsPemCollectionFromCSRProvided(request, vaasChainOption); + } + + private PEMCollection retrieveCertificateAsPemCollectionFromCSRProvided(CertificateRequest request, String chainOrder) throws VCertException { + String certificateAsPemString = ""; + // 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.certificateAsPem(certificateId, auth.apiKey()); + Response response = cloud.retrieveCertificate(request.certId(), auth.apiKey(), chainOrder); if (response.status() == 200) { try { - return CharStreams.toString(response.body().asReader()); + certificateAsPemString = CharStreams.toString(response.body().asReader()); + break; } catch (IOException e) { - throw new UnableToReadPEMCertificateException(certificateId); + throw new UnableToReadPEMCertificateException(request.certId()); } } @@ -384,6 +402,39 @@ private String certificateAsPem(String certificateId, CertificateRequest request throw new AttemptToRetryException(e); } } + + return PEMCollection.fromResponse( + certificateAsPemString, + request.chainOption(), + request.privateKey(), + request.keyPassword()); + } + + private PEMCollection retrieveCertificateAsPemCollectionFromCSRServiceGenerated(CertificateRequest request, byte[] serverPublicKey, String chainOption) throws VCertException { + + String encodedMessage = null; + try { + byte[] cipherText = SealedBoxUtility.cryptoBoxSeal(serverPublicKey, request.keyPassword().getBytes()); + encodedMessage = Base64.getEncoder().encodeToString(cipherText); + } catch (Exception e) { + throw new VCertException(e); + } + + KeystoreRequest keystoreRequest = new KeystoreRequest() + .exportFormat("PEM") + .encryptedPrivateKeyPassphrase(encodedMessage) + .encryptedKeystorePassphrase("") + .certificateLabel(""); + + InputStream keyStoreAsStream = null; + try { + Response response = cloud.retrieveKeystore(request.certId(), keystoreRequest, auth.apiKey()); + keyStoreAsStream = response.body().asInputStream(); + } catch (IOException e) { + throw new VCertException(e); + } + + return CloudConnectorUtils.getPEMCollectionFromKeyStoreAsStream(keyStoreAsStream, request.chainOption(), request.keyPassword()); } /** @@ -504,15 +555,20 @@ public void setPolicy(String policyName, PolicySpecification policySpecification @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 getPolicy(policyName, true); + } + + private PolicySpecification getPolicy(String policyName, boolean removeRegexFromSubjectCN) throws VCertException { + PolicySpecification policySpecification; + try { + CloudPolicy cloudPolicy = CloudConnectorUtils.getCloudPolicy( policyName, auth.apiKey(), cloud ); + cloudPolicy.removeRegexesFromSubjectCN(removeRegexFromSubjectCN); + policySpecification = CloudPolicySpecificationConverter.INSTANCE.convertToPolicySpecification( cloudPolicy ); + }catch (Exception e){ + throw new VCertException(e); + } - return policySpecification; + return policySpecification; } @Override @@ -581,6 +637,9 @@ public static class CertificateRequestsPayload { private String certificateIssuingTemplateId; private String existingCertificateId; private ApiClientInformation apiClientInformation; + private boolean isVaaSGenerated; + private CsrAttributes csrAttributes; + private String applicationServerTypeId; } @Data @@ -607,4 +666,20 @@ public static class ApiClientInformation{ String type; String identifier; } + + @Data + public static class CsrAttributes { + private String commonName; + private String organization; + private String[] organizationalUnits; + private String locality; + private String state; + private String country; + private SubjectAlternativeNamesByType subjectAlternativeNamesByType; + } + + @Data + public static class SubjectAlternativeNamesByType { + private String[] dnsNames; + } } diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/cloud/CloudConnectorUtils.java b/src/main/java/com/venafi/vcert/sdk/connectors/cloud/CloudConnectorUtils.java index ad42a89..95f2506 100644 --- a/src/main/java/com/venafi/vcert/sdk/connectors/cloud/CloudConnectorUtils.java +++ b/src/main/java/com/venafi/vcert/sdk/connectors/cloud/CloudConnectorUtils.java @@ -1,17 +1,35 @@ package com.venafi.vcert.sdk.connectors.cloud; 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.PEMCollection; +import com.venafi.vcert.sdk.connectors.ConnectorException.PolicyMatchException; +import com.venafi.vcert.sdk.connectors.cloud.CloudConnector.CsrAttributes; +import com.venafi.vcert.sdk.connectors.cloud.CloudConnector.SubjectAlternativeNamesByType; import com.venafi.vcert.sdk.connectors.cloud.domain.Application; import com.venafi.vcert.sdk.connectors.cloud.domain.CertificateIssuingTemplate; import com.venafi.vcert.sdk.connectors.cloud.domain.CloudZone; import com.venafi.vcert.sdk.connectors.cloud.domain.UserDetails; import com.venafi.vcert.sdk.connectors.cloud.endpoint.*; import com.venafi.vcert.sdk.policy.api.domain.CloudPolicy; +import com.venafi.vcert.sdk.policy.domain.PolicySpecification; + import feign.FeignException; import lombok.AllArgsConstructor; import lombok.Data; +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.security.PrivateKey; import java.util.*; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.bouncycastle.openssl.PEMParser; public class CloudConnectorUtils { @@ -204,6 +222,207 @@ private static String getProductName(CAAccount caAccount, CertificateIssuingTemp .get() .productName(); } + + public static CsrAttributes buildCsrAttributes(CertificateRequest request, PolicySpecification policySpecification) throws VCertException { + CsrAttributes csrAttributes = new CsrAttributes(); + + //computing the commonName + String reqCN = request.subject()!=null && isNotBlank(request.subject().commonName()) ? request.subject().commonName() : null; + + if( reqCN!=null ) { + //validating that the request.subject.cn matches with the policy domains + String[] policyDomains = Optional.ofNullable(policySpecification).map(ps -> ps.policy()).map(p -> p.domains()).orElse(null); + + if (policyDomains!=null && !matchRegexes(reqCN, policyDomains)) + throw new PolicyMatchException("CN", reqCN, "domains", policyDomains); + + csrAttributes.commonName(reqCN); + } + + //computing the organization + List reqOrganizations = Optional.ofNullable(request).map(req -> req.subject()).map(s -> s.organization()).orElse(null); + + if( reqOrganizations!=null && reqOrganizations.size() > 0) { + String[] reqOrgsArray = reqOrganizations.toArray(new String[0]); + + //validating that the req.subject.organization matches with the policy orgs + String[] policyOrgs = Optional.ofNullable(policySpecification).map(ps -> ps.policy()).map(p -> p.subject()).map(s -> s.orgs()).orElse(null); + + if (policyOrgs!=null && !matchRegexes(reqOrgsArray, policyOrgs)) + throw new PolicyMatchException("organization", reqOrgsArray, "organization", policyOrgs); + + csrAttributes.organization(reqOrgsArray[0]); + } else { + String defaultOrg = Optional.ofNullable(policySpecification).map(ps -> ps.defaults()).map(d -> d.subject()).map(s -> s.org()).orElse(null); + + if(isNotBlank(defaultOrg)) + csrAttributes.organization(defaultOrg); + } + + //computing the organizational Units + List reqOrgUnits = Optional.ofNullable(request).map(req -> req.subject()).map(s -> s.organizationalUnit()).orElse(null); + + if( reqOrgUnits!=null && reqOrgUnits.size() > 0) { + String[] reqOrgUnitsArray = reqOrgUnits.toArray(new String[0]); + + //validating that the req.subject.organizationalUnit matches with the policy orgUnits + String[] policyOrgUnits = Optional.ofNullable(policySpecification).map(ps -> ps.policy()).map(p -> p.subject()).map(s -> s.orgUnits()).orElse(null); + + if (policyOrgUnits!=null && !matchRegexes(reqOrgUnitsArray, policyOrgUnits)) + throw new PolicyMatchException("org unit", reqOrgUnitsArray, "org unit", policyOrgUnits); + + csrAttributes.organizationalUnits(reqOrgUnitsArray); + } else { + String[] defaultOrgUnits = Optional.ofNullable(policySpecification).map(ps -> ps.defaults()).map(d -> d.subject()).map(s -> s.orgUnits()).orElse(null); + + if(defaultOrgUnits!=null && defaultOrgUnits.length>0) + csrAttributes.organizationalUnits(defaultOrgUnits); + } + + //computing the localities + List reqLocalities = Optional.ofNullable(request).map(req -> req.subject()).map(s -> s.locality()).orElse(null); + + if( reqLocalities!=null && reqLocalities.size() > 0) { + String[] reqLocalitiesArray = reqLocalities.toArray(new String[0]); + + //validating that the req.subject.locality matches with the policy localities + String[] policyLocalities = Optional.ofNullable(policySpecification).map(ps -> ps.policy()).map(p -> p.subject()).map(s -> s.localities()).orElse(null); + + if (policyLocalities!=null && !matchRegexes(reqLocalitiesArray, policyLocalities)) + throw new PolicyMatchException("locality", reqLocalitiesArray, "localities", policyLocalities); + + csrAttributes.locality(reqLocalitiesArray[0]); + } else { + String defaultLocality = Optional.ofNullable(policySpecification).map(ps -> ps.defaults()).map(d -> d.subject()).map(s -> s.locality()).orElse(null); + + if(isNotBlank(defaultLocality)) + csrAttributes.locality(defaultLocality); + } + + //computing the province + List reqProvince = Optional.ofNullable(request).map(req -> req.subject()).map(s -> s.province()).orElse(null); + + if( reqProvince!=null && reqProvince.size() > 0) { + String[] reqProvinceArray = reqProvince.toArray(new String[0]); + + //validating that the req.subject.province matches with the policy states + String[] policyStates = Optional.ofNullable(policySpecification).map(ps -> ps.policy()).map(p -> p.subject()).map(s -> s.states()).orElse(null); + + if (policyStates!=null && !matchRegexes(reqProvinceArray, policyStates)) + throw new PolicyMatchException("state", reqProvinceArray, "states", policyStates); + + csrAttributes.state(reqProvinceArray[0]); + } else { + String defaultState = Optional.ofNullable(policySpecification).map(ps -> ps.defaults()).map(d -> d.subject()).map(s -> s.state()).orElse(null); + + if(isNotBlank(defaultState)) + csrAttributes.state(defaultState); + } + + //computing the country + List reqCountries = Optional.ofNullable(request).map(req -> req.subject()).map(s -> s.country()).orElse(null); + + if( reqCountries!=null && reqCountries.size() > 0) { + String[] reqCountriesArray = reqCountries.toArray(new String[0]); + + //validating that the req.subject.country matches with the policy countries + String[] policyCountries = Optional.ofNullable(policySpecification).map(ps -> ps.policy()).map(p -> p.subject()).map(s -> s.countries()).orElse(null); + + if (policyCountries!=null && !matchRegexes(reqCountriesArray, policyCountries)) + throw new PolicyMatchException("state", reqCountriesArray, "states", policyCountries); + + csrAttributes.country(reqCountriesArray[0]); + } else { + String defaultCountry = Optional.ofNullable(policySpecification).map(ps -> ps.defaults()).map(d -> d.subject()).map(s -> s.country()).orElse(null); + + if(isNotBlank(defaultCountry)) + csrAttributes.country(defaultCountry); + } + + if(request.dnsNames()!=null && request.dnsNames().size()>0) { + SubjectAlternativeNamesByType subjectAlternativeNamesByType = new SubjectAlternativeNamesByType().dnsNames(request.dnsNames().toArray(new String[0])); + csrAttributes.subjectAlternativeNamesByType(subjectAlternativeNamesByType); + } + + return csrAttributes; + } + + public static boolean matchRegexes(String subject, String[] regexes) { + return matchRegexes(new String[] {subject}, regexes); + } + + public static boolean matchRegexes(String[] subjects, String[] regexes) { + boolean allSubjectsMatched = true; + + List patterns = new ArrayList(); + + for (String regex : regexes) { + patterns.add(Pattern.compile(regex)); + } + + for (String subject : subjects) { + boolean subjectMatched = false; + for (Pattern pattern : patterns) { + if(pattern.matcher(subject).matches()) { + subjectMatched = true; + break; + } + } + + if(!subjectMatched) { + allSubjectsMatched = false; + break; + } + } + + return allSubjectsMatched; + } + + public static String getVaaSChainOption(ChainOption chainOption) { + switch (chainOption) { + case ChainOptionRootFirst: + return "ROOT_FIRST"; + case ChainOptionRootLast: + case ChainOptionIgnore: + default: + return "EE_FIRST"; + } + } + + public static PEMCollection getPEMCollectionFromKeyStoreAsStream(InputStream keyStoreAsInputStream, ChainOption chainOption, String keyPassword) throws VCertException { + String certificateAsPem = null; + + String pemFileSuffix = null; + if(chainOption == ChainOption.ChainOptionRootFirst) + pemFileSuffix = "_root-first.pem"; + else + pemFileSuffix = "_root-last.pem"; + + PrivateKey privateKey = null; + + try (ZipInputStream zis = new ZipInputStream(keyStoreAsInputStream)) { + + ZipEntry zipEntry; + while ((zipEntry = zis.getNextEntry())!= null) { + String fileName = zipEntry.getName(); + if(fileName.endsWith(".key")) { + PEMParser pemParser = new PEMParser(new InputStreamReader(zis)); + privateKey = PEMCollection.decryptPKCS8PrivateKey(pemParser, keyPassword); + } else { + if(fileName.endsWith(pemFileSuffix)) + certificateAsPem = new String(zis.readAllBytes()); + } + } + } catch (Exception e) { + throw new VCertException(e); + } + + return PEMCollection.fromResponse( + certificateAsPem, + chainOption, + privateKey, + keyPassword); + } @Data @AllArgsConstructor diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/cloud/SealedBoxUtility.java b/src/main/java/com/venafi/vcert/sdk/connectors/cloud/SealedBoxUtility.java new file mode 100644 index 0000000..e89b616 --- /dev/null +++ b/src/main/java/com/venafi/vcert/sdk/connectors/cloud/SealedBoxUtility.java @@ -0,0 +1,93 @@ +/** + * + */ +package com.venafi.vcert.sdk.connectors.cloud; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.GeneralSecurityException; + +import org.bouncycastle.crypto.digests.Blake2bDigest; + +import com.iwebpp.crypto.TweetNaclFast; + + +/** + * The following utility is based on the SealBoxUtility code shared in the stackoverflow question + * + * How can I create or open a libsodium compatible sealed box in pure Java. + *

+ * The main difference is on this version is being used the + * org.bouncycastle.crypto.digests.Blake2bDigest from The Bouncy Castle Crypto Package For Java + * instead of Blake2b to get the Blake2b hash. + *

+ * Has also a dependency on TweetNaclFast from https://github.com/InstantWebP2P/tweetnacl-java. + * + */ +public class SealedBoxUtility { + + + public static final int CRYPTO_BOX_NONCEBYTES = 24; + //public static final int crypto_box_PUBLICKEYBYTES = 32; + //public static final int crypto_box_MACBYTES = 16; + //public static final int crypto_box_SEALBYTES = (crypto_box_PUBLICKEYBYTES + crypto_box_MACBYTES); + + // libsodium + // int crypto_box_seal(unsigned char *c, const unsigned char *m, + // unsigned long long mlen, const unsigned char *pk); + /** + * Encrypt in a sealed box + * + * @param receiverPubKey receiver public key + * @param clearText clear text + * @return encrypted message + * @throws GeneralSecurityException + */ + public static byte[] cryptoBoxSeal(byte[] receiverPubKey, byte[] clearText) throws GeneralSecurityException { + + // create ephemeral keypair for sender + TweetNaclFast.Box.KeyPair ephkeypair = TweetNaclFast.Box.keyPair(); + // create nonce + byte[] nonce = cryptoBoxSealNonce(ephkeypair.getPublicKey(), receiverPubKey); + TweetNaclFast.Box box = new TweetNaclFast.Box(receiverPubKey, ephkeypair.getSecretKey()); + byte[] ciphertext = box.box(clearText, nonce); + if (ciphertext == null) + throw new GeneralSecurityException("Could not create the crypto box"); + + byte[] sealedbox = null; + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { + byteArrayOutputStream.write(ephkeypair.getPublicKey()); + byteArrayOutputStream.write(ciphertext); + sealedbox = byteArrayOutputStream.toByteArray(); + } catch (IOException e) { + throw new GeneralSecurityException("Could not create the sealed crypto box", e); + } + return sealedbox; + } + + /** + * hash the combination of senderpk + mypk into nonce using blake2b hash + * @param senderpk the senders public key + * @param mypk my own public key + * @return the nonce computed using Blake2b generic hash + */ + public static byte[] cryptoBoxSealNonce(byte[] senderpk, byte[] mypk){ + // C source ported from libsodium + // crypto_generichash_state st; + // + // crypto_generichash_init(&st, NULL, 0U, CRYPTO_BOX_NONCEBYTES); + // crypto_generichash_update(&st, pk1, crypto_box_PUBLICKEYBYTES); + // crypto_generichash_update(&st, pk2, crypto_box_PUBLICKEYBYTES); + // crypto_generichash_final(&st, nonce, CRYPTO_BOX_NONCEBYTES); + // + // return 0; + final Blake2bDigest blake2b = new Blake2bDigest( CRYPTO_BOX_NONCEBYTES*8 ); + blake2b.update(senderpk, 0, senderpk.length); + blake2b.update(mypk, 0, mypk.length); + byte[] nonce = new byte[CRYPTO_BOX_NONCEBYTES]; + blake2b.doFinal(nonce, 0); + if (nonce == null || nonce.length!=CRYPTO_BOX_NONCEBYTES) throw new IllegalArgumentException("Blake2b hashing failed"); + return nonce; + } + +} \ No newline at end of file diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/cloud/domain/CertificateDetails.java b/src/main/java/com/venafi/vcert/sdk/connectors/cloud/domain/CertificateDetails.java index 6feab86..51c1b6e 100644 --- a/src/main/java/com/venafi/vcert/sdk/connectors/cloud/domain/CertificateDetails.java +++ b/src/main/java/com/venafi/vcert/sdk/connectors/cloud/domain/CertificateDetails.java @@ -40,6 +40,7 @@ public class CertificateDetails { private String versionType; private int totalInstanceCount; private int totalActiveInstanceCount; + private String dekHash; diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/cloud/domain/EdgeEncryptionKey.java b/src/main/java/com/venafi/vcert/sdk/connectors/cloud/domain/EdgeEncryptionKey.java new file mode 100644 index 0000000..22ceb20 --- /dev/null +++ b/src/main/java/com/venafi/vcert/sdk/connectors/cloud/domain/EdgeEncryptionKey.java @@ -0,0 +1,14 @@ +package com.venafi.vcert.sdk.connectors.cloud.domain; + +import java.time.OffsetDateTime; +import lombok.Data; + +@Data +public class EdgeEncryptionKey { + + private String id; + private String companyId; + private String key; + private String keyAlgorithm; + private OffsetDateTime lastBackupDate; +} diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/cloud/endpoint/KeystoreRequest.java b/src/main/java/com/venafi/vcert/sdk/connectors/cloud/endpoint/KeystoreRequest.java new file mode 100644 index 0000000..5d4b7de --- /dev/null +++ b/src/main/java/com/venafi/vcert/sdk/connectors/cloud/endpoint/KeystoreRequest.java @@ -0,0 +1,11 @@ +package com.venafi.vcert.sdk.connectors.cloud.endpoint; + +import lombok.Data; + +@Data +public class KeystoreRequest { + private String exportFormat; + private String encryptedPrivateKeyPassphrase; + private String encryptedKeystorePassphrase; + private String certificateLabel; +} diff --git a/src/main/java/com/venafi/vcert/sdk/policy/api/domain/CloudPolicy.java b/src/main/java/com/venafi/vcert/sdk/policy/api/domain/CloudPolicy.java index e6c62f2..97d9cd3 100644 --- a/src/main/java/com/venafi/vcert/sdk/policy/api/domain/CloudPolicy.java +++ b/src/main/java/com/venafi/vcert/sdk/policy/api/domain/CloudPolicy.java @@ -9,6 +9,11 @@ public class CloudPolicy { private CertificateIssuingTemplate certificateIssuingTemplate; private CAInfo caInfo; + + //this attribute is not corresponding to any VaaS attribute. It only exists to indicate to the + // CloudPolicyToPolicyConverter class that when the domains of the PolicySpecification which is + //being built then they should be cleaned up of regexes or not. + private boolean removeRegexesFromSubjectCN; @Data @AllArgsConstructor diff --git a/src/main/java/com/venafi/vcert/sdk/policy/converter/cloud/CloudPolicyToPolicyConverter.java b/src/main/java/com/venafi/vcert/sdk/policy/converter/cloud/CloudPolicyToPolicyConverter.java index fe87aaa..2648548 100644 --- a/src/main/java/com/venafi/vcert/sdk/policy/converter/cloud/CloudPolicyToPolicyConverter.java +++ b/src/main/java/com/venafi/vcert/sdk/policy/converter/cloud/CloudPolicyToPolicyConverter.java @@ -37,7 +37,7 @@ private void processPolicy( PolicySpecification policySpecification, CloudPolicy CertificateIssuingTemplate cit = cloudPolicy.certificateIssuingTemplate(); - processDomainsAndWildcard( policySpecification, cit); + processDomainsAndWildcard( policySpecification, cit, cloudPolicy.removeRegexesFromSubjectCN()); processMaxValidDays( policySpecification, cit); processCertificateAuthority( policySpecification, cloudPolicy.caInfo()); @@ -48,23 +48,24 @@ private void processPolicy( PolicySpecification policySpecification, CloudPolicy processSubjectAltNames( policySpecification, cloudPolicy); } - private void processDomainsAndWildcard( PolicySpecification policySpecification, CertificateIssuingTemplate cit ) throws Exception{ + private void processDomainsAndWildcard( PolicySpecification policySpecification, CertificateIssuingTemplate cit, boolean removeRegexesFromDomains ) throws Exception{ List subjectCNRegexes = cit.subjectCNRegexes; if ( subjectCNRegexes != null && subjectCNRegexes.size() > 0 && !subjectCNRegexes.get(0).equals(".*") ) { Policy policy = getPolicyFromPolicySpecification( policySpecification ); - processDomains(policy, subjectCNRegexes); - processWildcard(policy, subjectCNRegexes); + + if(removeRegexesFromDomains) + removeRegexesFromDomains(policy, subjectCNRegexes); } else { //domains will not set } } - private void processDomains( Policy policy, List subjectCNRegexes) { + private void removeRegexesFromDomains( Policy policy, List subjectCNRegexes) { //converting the subjectCNRegexes to domains List domains = new ArrayList(); for (String domain : subjectCNRegexes) { diff --git a/src/test/java/com/venafi/vcert/sdk/connectors/cloud/CloudConnectorAT.java b/src/test/java/com/venafi/vcert/sdk/connectors/cloud/CloudConnectorAT.java index b69f74f..016f441 100644 --- a/src/test/java/com/venafi/vcert/sdk/connectors/cloud/CloudConnectorAT.java +++ b/src/test/java/com/venafi/vcert/sdk/connectors/cloud/CloudConnectorAT.java @@ -38,6 +38,7 @@ import com.venafi.vcert.sdk.VCertClient; import com.venafi.vcert.sdk.VCertException; import com.venafi.vcert.sdk.certificate.CertificateRequest; +import com.venafi.vcert.sdk.certificate.CsrOriginOption; import com.venafi.vcert.sdk.certificate.ImportRequest; import com.venafi.vcert.sdk.certificate.KeyType; import com.venafi.vcert.sdk.certificate.PEMCollection; @@ -138,11 +139,12 @@ void requestCertificateUnrestricted() throws VCertException, UnknownHostExceptio } @Test - void retrieveCertificate() throws VCertException, UnknownHostException { + void retrieveCertificateCSRProvided() throws VCertException, UnknownHostException { String zoneName = System.getenv("CLOUDZONE"); ZoneConfiguration zoneConfiguration = classUnderTest.readZoneConfiguration(zoneName); + String cn = TestUtils.randomCN().toLowerCase(); CertificateRequest certificateRequest = new CertificateRequest() - .subject(new CertificateRequest.PKIXName().commonName(TestUtils.randomCN()) + .subject(new CertificateRequest.PKIXName().commonName(cn) .organization(Collections.singletonList("Venafi, Inc.")) .organizationalUnit(Arrays.asList("Engineering", "Automated Tests")) .country(Collections.singletonList("US")).locality(Collections.singletonList("SLC")) @@ -155,6 +157,35 @@ void retrieveCertificate() throws VCertException, UnknownHostException { assertThat(certificateId).isNotNull(); certificateRequest.pickupId(certificateId); + //certificateRequest.certId("b7a38570-48aa-11ec-9c4e-b91163bb1407"); + PEMCollection pemCollection = classUnderTest.retrieveCertificate(certificateRequest); + + assertThat(pemCollection.certificate()).isNotNull(); + assertThat(pemCollection.chain()).hasSize(2); + assertThat(pemCollection.privateKey()).isNotNull(); + } + + @Test + void retrieveCertificateServiceGeneratedCSR() throws VCertException, UnknownHostException { + String zoneName = System.getenv("CLOUDZONE"); + ZoneConfiguration zoneConfiguration = classUnderTest.readZoneConfiguration(zoneName); + String cn = TestUtils.randomCN().toLowerCase(); + CertificateRequest certificateRequest = new CertificateRequest() + .subject(new CertificateRequest.PKIXName().commonName(cn) + .organization(Collections.singletonList("Venafi, Inc.")) + .organizationalUnit(Arrays.asList("Engineering", "Automated Tests")) + .country(Collections.singletonList("US")).locality(Collections.singletonList("SLC")) + .province(Collections.singletonList("Utah"))) + .dnsNames( Arrays.asList(new String[] {cn})) + .csrOrigin(CsrOriginOption.ServiceGeneratedCSR) + .keyPassword("tirano"); + + //For CSR Service Generated Request is not needed to call to generateRequest() method + //certificateRequest = classUnderTest.generateRequest(zoneConfiguration, certificateRequest); + String pickupId = classUnderTest.requestCertificate(certificateRequest, zoneConfiguration); + assertThat(pickupId).isNotNull(); + + certificateRequest.pickupId(pickupId); PEMCollection pemCollection = classUnderTest.retrieveCertificate(certificateRequest); assertThat(pemCollection.certificate()).isNotNull(); diff --git a/src/test/java/com/venafi/vcert/sdk/connectors/cloud/CloudConnectorTest.java b/src/test/java/com/venafi/vcert/sdk/connectors/cloud/CloudConnectorTest.java index 7a29b39..f513066 100644 --- a/src/test/java/com/venafi/vcert/sdk/connectors/cloud/CloudConnectorTest.java +++ b/src/test/java/com/venafi/vcert/sdk/connectors/cloud/CloudConnectorTest.java @@ -48,6 +48,7 @@ import com.venafi.vcert.sdk.connectors.cloud.domain.CertificateIssuingTemplate; import com.venafi.vcert.sdk.connectors.cloud.domain.CertificateIssuingTemplate.AllowedKeyType; import com.venafi.vcert.sdk.connectors.cloud.domain.Company; +import com.venafi.vcert.sdk.connectors.cloud.domain.EdgeEncryptionKey; import com.venafi.vcert.sdk.connectors.cloud.domain.User; import com.venafi.vcert.sdk.connectors.cloud.domain.UserDetails; import com.venafi.vcert.sdk.endpoint.Authentication; @@ -170,16 +171,23 @@ void retrieveCertificate() throws VCertException, IOException { list.add("jackpot"); CertificateStatus status = new CertificateStatus().status("ISSUED") .certificateIds(list); + + CertificateDetails certificateDetails = new CertificateDetails().dekHash("12345"); + EdgeEncryptionKey edgeEncryptionKey = new EdgeEncryptionKey(); + + cloud.certificateDetails(eq("jackpot"), eq(apiKey)); when(cloud.certificateStatus(eq("jackpot"), eq(apiKey))) .thenReturn(status); - when(cloud.certificateViaCSR(eq("jackpot"), eq(apiKey), eq("ROOT_FIRST"))) + when(cloud.retrieveCertificate(eq("jackpot"), eq(apiKey), eq("ROOT_FIRST"))) .thenReturn(Response.builder() .request(Request.create(Request.HttpMethod.GET, "http://localhost", new HashMap>(), null, null)) .status(200) .body(body, Charset.forName("UTF-8")) .build()); + when(cloud.certificateDetails(eq("jackpot"), eq(apiKey))).thenReturn(certificateDetails); + when(cloud.retrieveEdgeEncryptionKey(eq("12345"), eq(apiKey))).thenReturn(edgeEncryptionKey); PEMCollection pemCollection2 = classUnderTest.retrieveCertificate(request); assertThat(pemCollection2).isNotNull();