Skip to content

Commit

Permalink
Fixed response code for mergeCertificate operation. (#36563)
Browse files Browse the repository at this point in the history
* Changed the expected response code for the `mergeCertificate` and `mergeCertificateAsync` operations from `200` to the correct `201`.

* Added tests.

* Fixed issue with `maven-resources-plugin`

* Removed unused code.

* Fixed async test.

* Updated recordings tag.

* Removed the use of `sun.security.*` libraries in favor of using Bouncy Castle.

* Removed the use of `sun.security.*` libraries in favor of using Bouncy Castle.

* Updated test recordings.

* Fixed more build issues.

* Fixed issue with external_dependencies.txt

* Fixed CheckStyle issue.
  • Loading branch information
vcolin7 authored Sep 7, 2023
1 parent 56bc1d1 commit 3c084d3
Show file tree
Hide file tree
Showing 12 changed files with 398 additions and 151 deletions.
1 change: 1 addition & 0 deletions eng/versioning/external_dependencies.txt
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ commons-cli:commons-cli;1.3
org.assertj:assertj-core;3.22.0
org.bouncycastle:bcprov-jdk15to18;1.76
org.bouncycastle:bcprov-jdk18on;1.76
org.bouncycastle:bcpkix-lts8on;2.73.3
org.eclipse.jetty:jetty-http;9.4.51.v20230217
org.eclipse.jetty:jetty-server;9.4.51.v20230217
org.eclipse.jetty:jetty-servlet;9.4.51.v20230217
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
### Breaking Changes

### Bugs Fixed
- Fixed response code for certificate merging operations from `200` to the correct `201`.
([#36260]https://github.com/Azure/azure-sdk-for-java/issues/36260))

### Other Changes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "java",
"TagPrefix": "java/keyvault/azure-security-keyvault-certificates",
"Tag": "java/keyvault/azure-security-keyvault-certificates_90c7b7987b"
"Tag": "java/keyvault/azure-security-keyvault-certificates_1e177cf1ed"
}
42 changes: 33 additions & 9 deletions sdk/keyvault/azure-security-keyvault-certificates/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,11 @@
</properties>

<dependencies>

<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-core</artifactId>
<version>1.42.0</version> <!-- {x-version-update;com.azure:azure-core;dependency} -->
</dependency>

<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-core-http-netty</artifactId>
Expand All @@ -64,49 +62,42 @@
<version>5.9.3</version> <!-- {x-version-update;org.junit.jupiter:junit-jupiter-api;external_dependency} -->
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.3</version> <!-- {x-version-update;org.junit.jupiter:junit-jupiter-engine;external_dependency} -->
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.9.3</version> <!-- {x-version-update;org.junit.jupiter:junit-jupiter-params;external_dependency} -->
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>2.2</version> <!-- {x-version-update;org.hamcrest:hamcrest-library;external_dependency} -->
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<version>3.4.31</version> <!-- {x-version-update;io.projectreactor:reactor-test;external_dependency} -->
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-core-test</artifactId>
<version>1.19.0</version> <!-- {x-version-update;com.azure:azure-core-test;dependency} -->
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-core-http-okhttp</artifactId>
<version>1.11.12</version> <!-- {x-version-update;com.azure:azure-core-http-okhttp;dependency} -->
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
Expand All @@ -119,5 +110,38 @@
<version>4.11.0</version> <!-- {x-version-update;org.mockito:mockito-core;external_dependency} -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-lts8on</artifactId>
<version>2.73.3</version> <!-- {x-version-update;org.bouncycastle:bcpkix-lts8on;external_dependency} -->
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.0</version> <!-- {x-version-update;org.apache.maven.plugins:maven-resources-plugin;external_dependency} -->
<executions>
<execution>
<id>copy-resources</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target/test-classes</outputDirectory>
<resources>
<resource>
<directory>${basedir}/src/test/resources</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ Mono<Response<KeyVaultCertificateWithPolicy>> importCertificateAsync(@HostParam(
Context context);

@Post("certificates/{certificate-name}/pending/merge")
@ExpectedResponses({200})
@ExpectedResponses({201})
@UnexpectedResponseExceptionType(HttpResponseException.class)
Mono<Response<KeyVaultCertificateWithPolicy>> mergeCertificateAsync(@HostParam("url") String url,
@PathParam("certificate-name") String certificateName,
Expand Down Expand Up @@ -858,7 +858,7 @@ Response<KeyVaultCertificateWithPolicy> importCertificate(@HostParam("url") Stri
Context context);

@Post("certificates/{certificate-name}/pending/merge")
@ExpectedResponses({200})
@ExpectedResponses({201})
@UnexpectedResponseExceptionType(HttpResponseException.class)
Response<KeyVaultCertificateWithPolicy> mergeCertificate(@HostParam("url") String url,
@PathParam("certificate-name") String certificateName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,38 @@
import com.azure.security.keyvault.certificates.models.DeletedCertificate;
import com.azure.security.keyvault.certificates.models.KeyVaultCertificateWithPolicy;
import com.azure.security.keyvault.certificates.models.MergeCertificateOptions;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.Certificate;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.junit.jupiter.api.condition.DisabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import java.io.IOException;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -944,7 +965,7 @@ public void importCertificate(HttpClient httpClient, CertificateServiceVersion s
X509Certificate x509Certificate = null;

try {
x509Certificate = loadCerToX509Certificate(importedCertificate);
x509Certificate = loadCerToX509Certificate(importedCertificate.getCer());
} catch (CertificateException | IOException e) {
e.printStackTrace();
fail();
Expand All @@ -956,6 +977,71 @@ public void importCertificate(HttpClient httpClient, CertificateServiceVersion s
});
}

@ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS)
@MethodSource("getTestParameters")
@DisabledForJreRange(min = JRE.JAVA_17) // Access to sun.security.* classes used here is not possible on Java 17+.
@SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
public void mergeCertificate(HttpClient httpClient, CertificateServiceVersion serviceVersion) {
createCertificateAsyncClient(httpClient, serviceVersion);

String certificateName = testResourceNamer.randomName("testCert", 25);
String issuer = "Unknown";
String subject = "CN=MyCert";

StepVerifier.create(certificateAsyncClient.beginCreateCertificate(certificateName,
new CertificatePolicy(issuer, subject).setCertificateTransparent(false))
.takeUntil(asyncPollResponse ->
asyncPollResponse.getStatus() == LongRunningOperationStatus.IN_PROGRESS)
.map(asyncPollResponse -> {
MergeCertificateOptions mergeCertificateOptions = null;

try {
CertificateOperation certificateOperation = asyncPollResponse.getValue();
byte[] certificateSignRequest = certificateOperation.getCsr();
PKCS10CertificationRequest pkcs10CertificationRequest =
new PKCS10CertificationRequest(certificateSignRequest);
byte[] certificateToMerge = FakeCredentialsForTests.FAKE_PEM_CERTIFICATE_FOR_MERGE.getBytes();
X509Certificate x509ToMerge = loadCerToX509Certificate(certificateToMerge);
PrivateKey privateKey = loadPrivateKey("priv8.der");
Date notBefore = new Date();
Date notAfter = new Date(notBefore.getTime() + 60 * 86400000L);

X500Name mergeIssuer = new X500Name(x509ToMerge.getSubjectX500Principal().getName());
X500Name mergeSubject = pkcs10CertificationRequest.getSubject();
AlgorithmIdentifier signatureAlgorithmIdentifier =
new DefaultSignatureAlgorithmIdentifierFinder().find("SHA256withRSA");
AlgorithmIdentifier digestAlgorithmIdentifier =
new DefaultDigestAlgorithmIdentifierFinder().find(signatureAlgorithmIdentifier);
AsymmetricKeyParameter asymmetricKeyParameter = PrivateKeyFactory.createKey(privateKey.getEncoded());
SubjectPublicKeyInfo publicKeyInfo = pkcs10CertificationRequest.getSubjectPublicKeyInfo();
X509v3CertificateBuilder x509CertificateBuilder =
new X509v3CertificateBuilder(mergeIssuer, BigInteger.ONE, notBefore, notAfter, mergeSubject,
publicKeyInfo);

ContentSigner contentSigner =
new BcRSAContentSignerBuilder(signatureAlgorithmIdentifier, digestAlgorithmIdentifier)
.build(asymmetricKeyParameter);

Certificate certificate = x509CertificateBuilder.build(contentSigner).toASN1Structure();

mergeCertificateOptions =
new MergeCertificateOptions(certificateName, Collections.singletonList(certificate.getEncoded()));
} catch (CertificateException | InvalidKeySpecException | IOException
| NoSuchAlgorithmException | OperatorCreationException e) {

fail(e);
}

return mergeCertificateOptions;
})
.flatMap(mergeCertificateOptions -> certificateAsyncClient.mergeCertificate(mergeCertificateOptions))
.flatMap(mergedCertificate -> certificateAsyncClient.getCertificateOperation(mergedCertificate.getName()))
.last())
.assertNext(pollResponse ->
assertEquals(LongRunningOperationStatus.SUCCESSFULLY_COMPLETED, pollResponse.getStatus()))
.verifyComplete();
}

@ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS)
@MethodSource("getTestParameters")
@SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,36 @@
import com.azure.security.keyvault.certificates.models.KeyVaultCertificate;
import com.azure.security.keyvault.certificates.models.KeyVaultCertificateWithPolicy;
import com.azure.security.keyvault.certificates.models.MergeCertificateOptions;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.Certificate;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.junit.jupiter.api.condition.DisabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import java.io.IOException;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -887,7 +908,7 @@ public void importCertificate(HttpClient httpClient, CertificateServiceVersion s
X509Certificate x509Certificate = null;

try {
x509Certificate = loadCerToX509Certificate(importedCertificate);
x509Certificate = loadCerToX509Certificate(importedCertificate.getCer());
} catch (CertificateException | IOException e) {
e.printStackTrace();
fail();
Expand All @@ -898,6 +919,66 @@ public void importCertificate(HttpClient httpClient, CertificateServiceVersion s
});
}

@ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS)
@MethodSource("getTestParameters")
@DisabledForJreRange(min = JRE.JAVA_17) // Access to sun.security.* classes used here is not possible on Java 17+.
@SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
public void mergeCertificate(HttpClient httpClient, CertificateServiceVersion serviceVersion) {
try {
createCertificateClient(httpClient, serviceVersion);

String certificateName = testResourceNamer.randomName("testCert", 25);
String issuer = "Unknown";
String subject = "CN=MyCert";
SyncPoller<CertificateOperation, KeyVaultCertificateWithPolicy> createCertificatePoller =
certificateClient.beginCreateCertificate(certificateName,
new CertificatePolicy(issuer, subject).setCertificateTransparent(false));

createCertificatePoller.waitUntil(LongRunningOperationStatus.IN_PROGRESS);

CertificateOperation certificateOperation = createCertificatePoller.poll().getValue();
byte[] certificateSignRequest = certificateOperation.getCsr();
PKCS10CertificationRequest pkcs10CertificationRequest =
new PKCS10CertificationRequest(certificateSignRequest);
byte[] certificateToMerge = FakeCredentialsForTests.FAKE_PEM_CERTIFICATE_FOR_MERGE.getBytes();
X509Certificate x509ToMerge = loadCerToX509Certificate(certificateToMerge);
PrivateKey privateKey = loadPrivateKey("priv8.der");
Date notBefore = new Date();
Date notAfter = new Date(notBefore.getTime() + 60 * 86400000L);

X500Name mergeIssuer = new X500Name(x509ToMerge.getSubjectX500Principal().getName());
X500Name mergeSubject = pkcs10CertificationRequest.getSubject();
AlgorithmIdentifier signatureAlgorithmIdentifier =
new DefaultSignatureAlgorithmIdentifierFinder().find("SHA256withRSA");
AlgorithmIdentifier digestAlgorithmIdentifier =
new DefaultDigestAlgorithmIdentifierFinder().find(signatureAlgorithmIdentifier);
AsymmetricKeyParameter asymmetricKeyParameter = PrivateKeyFactory.createKey(privateKey.getEncoded());
SubjectPublicKeyInfo publicKeyInfo = pkcs10CertificationRequest.getSubjectPublicKeyInfo();
X509v3CertificateBuilder x509CertificateBuilder =
new X509v3CertificateBuilder(mergeIssuer, BigInteger.ONE, notBefore, notAfter, mergeSubject,
publicKeyInfo);

ContentSigner contentSigner =
new BcRSAContentSignerBuilder(signatureAlgorithmIdentifier, digestAlgorithmIdentifier)
.build(asymmetricKeyParameter);

Certificate certificate = x509CertificateBuilder.build(contentSigner).toASN1Structure();

MergeCertificateOptions mergeCertificateOptions =
new MergeCertificateOptions(certificateName, Collections.singletonList(certificate.getEncoded()));

certificateClient.mergeCertificate(mergeCertificateOptions);

PollResponse<CertificateOperation> pollResponse = createCertificatePoller.poll();

assertEquals(LongRunningOperationStatus.SUCCESSFULLY_COMPLETED, pollResponse.getStatus());
} catch (CertificateException | InvalidKeySpecException | IOException
| NoSuchAlgorithmException | OperatorCreationException e) {

fail(e);
}
}

@ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS)
@MethodSource("getTestParameters")
@SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
Expand Down
Loading

0 comments on commit 3c084d3

Please sign in to comment.