You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by na...@apache.org on 2015/04/02 11:26:27 UTC

[3/3] jclouds-labs git commit: [JCLOUDS-837] added service certificate support

[JCLOUDS-837] added service certificate support


Project: http://git-wip-us.apache.org/repos/asf/jclouds-labs/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds-labs/commit/e6a3ce33
Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs/tree/e6a3ce33
Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs/diff/e6a3ce33

Branch: refs/heads/master
Commit: e6a3ce338cb90bd53ae8166b24e498d379a04f09
Parents: 4e3f19b
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Mar 12 14:10:51 2015 +0100
Committer: Ignasi Barrera <na...@apache.org>
Committed: Thu Apr 2 11:19:49 2015 +0200

----------------------------------------------------------------------
 .../jclouds/azurecompute/AzureComputeApi.java   |   9 ++
 .../binders/ServiceCertificateParamsToXML.java  |  47 ++++++
 .../azurecompute/domain/ServiceCertificate.java |  74 +++++++++
 .../domain/ServiceCertificateParams.java        | 100 ++++++++++++
 .../features/ServiceCertificatesApi.java        | 123 +++++++++++++++
 .../xml/ListServiceCertificatesHandler.java     |  78 ++++++++++
 .../xml/ServiceCertificateHandler.java          |  80 ++++++++++
 .../ServiceCertificatesApiLiveTest.java         | 117 ++++++++++++++
 .../ServiceCertificatesApiMockTest.java         | 151 +++++++++++++++++++
 .../ListServiceCertificatessHandlerTest.java    |  90 +++++++++++
 .../xml/ServiceCertificateHandlerTest.java      |  55 +++++++
 .../src/test/resources/servicecertificate.xml   |   4 +
 .../test/resources/servicecertificateparams.xml |   1 +
 .../src/test/resources/servicecertificates.xml  |  14 ++
 14 files changed, 943 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e6a3ce33/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeApi.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeApi.java b/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeApi.java
index 19c364e..189f061 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeApi.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeApi.java
@@ -28,6 +28,7 @@ import org.jclouds.azurecompute.features.LocationApi;
 import org.jclouds.azurecompute.features.NetworkSecurityGroupApi;
 import org.jclouds.azurecompute.features.OSImageApi;
 import org.jclouds.azurecompute.features.OperationApi;
+import org.jclouds.azurecompute.features.ServiceCertificatesApi;
 import org.jclouds.azurecompute.features.StorageAccountApi;
 import org.jclouds.azurecompute.features.SubscriptionApi;
 import org.jclouds.azurecompute.features.TrafficManagerApi;
@@ -149,4 +150,12 @@ public interface AzureComputeApi extends Closeable {
     */
    @Delegate
    TrafficManagerApi getTrafficManaerApi();
+
+   /**
+    * The Service Management API includes operations for managing service certificates in your subscription.
+    *
+    * @see <a href="https://msdn.microsoft.com/en-us/library/azure/ee795178.aspx">docs</a>
+    */
+   @Delegate
+   ServiceCertificatesApi getServiceCertificatesApi();
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e6a3ce33/azurecompute/src/main/java/org/jclouds/azurecompute/binders/ServiceCertificateParamsToXML.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/binders/ServiceCertificateParamsToXML.java b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/ServiceCertificateParamsToXML.java
new file mode 100644
index 0000000..49c18ff
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/ServiceCertificateParamsToXML.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.binders;
+
+import static com.google.common.base.Throwables.propagate;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.Binder;
+
+import com.jamesmurty.utils.XMLBuilder;
+import org.jclouds.azurecompute.domain.ServiceCertificateParams;
+
+public final class ServiceCertificateParamsToXML implements Binder {
+
+   @Override
+   @SuppressWarnings("unchecked")
+   public <R extends HttpRequest> R bindToRequest(final R request, final Object input) {
+      final ServiceCertificateParams params = ServiceCertificateParams.class.cast(input);
+      try {
+         final XMLBuilder bld = XMLBuilder.create("CertificateFile", "http://schemas.microsoft.com/windowsazure")
+                 .e("Data").t(params.data()).up()
+                 .e("CertificateFormat").t(params.format()).up();
+
+         if (params.password() != null) {
+            bld.e("Password").t(params.password()).up();
+         }
+
+         return (R) request.toBuilder().payload(bld.up().asString()).build();
+      } catch (Exception e) {
+         throw propagate(e);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e6a3ce33/azurecompute/src/main/java/org/jclouds/azurecompute/domain/ServiceCertificate.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/domain/ServiceCertificate.java b/azurecompute/src/main/java/org/jclouds/azurecompute/domain/ServiceCertificate.java
new file mode 100644
index 0000000..1fe0081
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/domain/ServiceCertificate.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.domain;
+
+import com.google.auto.value.AutoValue;
+import java.net.URI;
+import org.jclouds.javax.annotation.Nullable;
+
+/**
+ * Cloud service certifcate.
+ *
+ * @see <a href="https://msdn.microsoft.com/en-us/library/azure/ee795178.aspx" >ServiceCertificate</a>
+ */
+@AutoValue
+public abstract class ServiceCertificate {
+
+   ServiceCertificate() {
+   } // For AutoValue only!
+
+   /**
+    * The Service Management API request URI used to perform Get Service Certificate requests against the certificate
+    * store.
+    *
+    * @return service certificate URL.
+    */
+   @Nullable
+   public abstract URI url();
+
+   /**
+    * The X509 certificate thumb print property of the service certificate.
+    *
+    * @return thumbprint of the service certificate.
+    */
+   @Nullable
+   public abstract String thumbprint();
+
+   /**
+    * The algorithm that was used to hash the service certificate. Currently SHA-1 is the only supported algorithm.
+    *
+    * @return thumbprint algorithm of the service certificate.
+    */
+   @Nullable
+   public abstract String thumbprintAlgorithm();
+
+   /**
+    * The public part of the service certificate as a base-64 encoded .cer file.
+    *
+    * @return base-64 encoded certificate.
+    */
+   public abstract String data();
+
+   public static ServiceCertificate create(
+           final URI url,
+           final String thumbprint,
+           final String thumbprintAlgorithm,
+           final String data) {
+
+      return new AutoValue_ServiceCertificate(url, thumbprint, thumbprintAlgorithm, data);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e6a3ce33/azurecompute/src/main/java/org/jclouds/azurecompute/domain/ServiceCertificateParams.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/domain/ServiceCertificateParams.java b/azurecompute/src/main/java/org/jclouds/azurecompute/domain/ServiceCertificateParams.java
new file mode 100644
index 0000000..e6c9ee1
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/domain/ServiceCertificateParams.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.domain;
+
+import com.google.auto.value.AutoValue;
+import org.jclouds.javax.annotation.Nullable;
+
+/**
+ * To create a new service certifcate.
+ *
+ * @see <a href="https://msdn.microsoft.com/en-us/library/azure/ee460817.aspx">docs</a>
+ */
+@AutoValue
+public abstract class ServiceCertificateParams {
+
+   ServiceCertificateParams() {
+   } // For AutoValue only!
+
+   /**
+    * The public part of the service certificate as a base-64 encoded .cer file.
+    *
+    * @return base-64 encoded certificate.
+    */
+   public abstract String data();
+
+   /**
+    * Required. Specifies the format of the service certificate. Possible values are: pfx, cer.
+    *
+    * @return certificate format.
+    */
+   public abstract String format();
+
+   /**
+    * Specifies the password for a .pfx certificate. A .cer certificate does not require a password.
+    *
+    * @return certificate password.
+    */
+   @Nullable
+   public abstract String password();
+
+   public Builder toBuilder() {
+      return builder().fromImageParams(this);
+   }
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static final class Builder {
+
+      private String data;
+
+      private String format;
+
+      private String password;
+
+      public Builder data(final String data) {
+         this.data = data;
+         return this;
+      }
+
+      public Builder format(final String format) {
+         this.format = format;
+         return this;
+      }
+
+      public Builder password(final String password) {
+         this.password = password;
+         return this;
+      }
+
+      public ServiceCertificateParams build() {
+         return ServiceCertificateParams.create(data, format, password);
+      }
+
+      public Builder fromImageParams(final ServiceCertificateParams in) {
+         return data(in.data())
+                 .format(in.format())
+                 .password(in.password());
+      }
+   }
+
+   private static ServiceCertificateParams create(final String data, final String format, final String password) {
+      return new AutoValue_ServiceCertificateParams(data, format, password);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e6a3ce33/azurecompute/src/main/java/org/jclouds/azurecompute/features/ServiceCertificatesApi.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/features/ServiceCertificatesApi.java b/azurecompute/src/main/java/org/jclouds/azurecompute/features/ServiceCertificatesApi.java
new file mode 100644
index 0000000..15d940b
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/features/ServiceCertificatesApi.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.features;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_XML;
+import static org.jclouds.Fallbacks.EmptyListOnNotFoundOr404;
+
+import java.util.List;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import org.jclouds.Fallbacks.NullOnNotFoundOr404;
+import org.jclouds.azurecompute.binders.ServiceCertificateParamsToXML;
+import org.jclouds.azurecompute.domain.ServiceCertificate;
+import org.jclouds.azurecompute.domain.ServiceCertificateParams;
+import org.jclouds.azurecompute.functions.ParseRequestIdHeader;
+
+import org.jclouds.azurecompute.xml.ListServiceCertificatesHandler;
+import org.jclouds.azurecompute.xml.ServiceCertificateHandler;
+import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.Headers;
+import org.jclouds.rest.annotations.ResponseParser;
+import org.jclouds.rest.annotations.XMLResponseParser;
+
+/**
+ * The Service Management API includes operations for managing service certificates in your subscription.
+ *
+ * @see <a href="https://msdn.microsoft.com/en-us/library/azure/ee795178.aspx">docs</a>
+ */
+@Path("/services/hostedservices")
+@Headers(keys = "x-ms-version", values = "{jclouds.api-version}")
+@Consumes(APPLICATION_XML)
+@Produces(APPLICATION_XML)
+public interface ServiceCertificatesApi {
+
+   /**
+    * The List Service Certificates operation lists all of the service certificates associated with the specified cloud
+    * service.
+    *
+    * @param service service name.
+    * @return list of cloud service certificates.
+    */
+   @Named("ListServiceCertificates")
+   @GET
+   @Path("/{service}/certificates")
+   @XMLResponseParser(ListServiceCertificatesHandler.class)
+   @Fallback(EmptyListOnNotFoundOr404.class)
+   List<ServiceCertificate> list(@PathParam("service") String service);
+
+   /**
+    * The Get Service Certificate operation returns the public data for the specified X.509 certificate associated with
+    * a cloud service.
+    *
+    * @param service service name.
+    * @param thumbprintAlgorithm thumbprint algorithm.
+    * @param thumbprintInHexadecimal thumbprint hexadecimal format.
+    * @return service certificate including data only.
+    */
+   @Named("GetServiceCertificate")
+   @GET
+   @Path("/{service}/certificates/{thumbprintAlgorithm}-{thumbprintInHexadecimal}")
+   @XMLResponseParser(ServiceCertificateHandler.class)
+   @Fallback(NullOnNotFoundOr404.class)
+   ServiceCertificate get(
+           @PathParam("service") String service,
+           @PathParam("thumbprintAlgorithm") String thumbprintAlgorithm,
+           @PathParam("thumbprintInHexadecimal") String thumbprintInHexadecimal);
+
+   /**
+    * The Delete Service Certificate asynchronous operation deletes a service certificate from the certificate store of
+    * a cloud service.
+    *
+    * @param service service name.
+    * @param thumbprintAlgorithm thumbprint algorithm.
+    * @param thumbprintInHexadecimal thumbprint hexadecimal format.
+    * @return request id.
+    */
+   @Named("DeleteServiceCertificate")
+   @DELETE
+   @Path("/{service}/certificates/{thumbprintAlgorithm}-{thumbprintInHexadecimal}")
+   @Fallback(NullOnNotFoundOr404.class)
+   @ResponseParser(ParseRequestIdHeader.class)
+   String delete(
+           @PathParam("service") String service,
+           @PathParam("thumbprintAlgorithm") String thumbprintAlgorithm,
+           @PathParam("thumbprintInHexadecimal") String thumbprintInHexadecimal);
+
+   /**
+    * The Add Service Certificate asynchronous operation adds a certificate to a cloud service.
+    *
+    * @param service service name.
+    * @param params service certificate details to be sent as request body.
+    * @return request id.
+    */
+   @Named("AddServiceCertificate")
+   @POST
+   @Path("/{service}/certificates")
+   @ResponseParser(ParseRequestIdHeader.class)
+   String add(
+           @PathParam("service") String service,
+           @BinderParam(ServiceCertificateParamsToXML.class) ServiceCertificateParams params);
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e6a3ce33/azurecompute/src/main/java/org/jclouds/azurecompute/xml/ListServiceCertificatesHandler.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/xml/ListServiceCertificatesHandler.java b/azurecompute/src/main/java/org/jclouds/azurecompute/xml/ListServiceCertificatesHandler.java
new file mode 100644
index 0000000..9056d5a
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/xml/ListServiceCertificatesHandler.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.xml;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.jclouds.http.functions.ParseSax;
+import org.xml.sax.Attributes;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import org.jclouds.azurecompute.domain.ServiceCertificate;
+
+/**
+ * @see <a href="https://msdn.microsoft.com/en-us/library/azure/ee460788.aspx">Response body description</a>
+ */
+public final class ListServiceCertificatesHandler extends
+        ParseSax.HandlerForGeneratedRequestWithResult<List<ServiceCertificate>> {
+
+   private boolean inCertificate;
+
+   private final ServiceCertificateHandler serviceCertificateHandler;
+
+   private final Builder<ServiceCertificate> certificates = ImmutableList.builder();
+
+   @Inject
+   ListServiceCertificatesHandler(ServiceCertificateHandler serviceCertificateHandler) {
+      this.serviceCertificateHandler = serviceCertificateHandler;
+   }
+
+   @Override
+   public List<ServiceCertificate> getResult() {
+      return certificates.build();
+   }
+
+   @Override
+   public void startElement(final String url, final String name, final String qName, final Attributes attributes) {
+      if (qName.equals("Certificate")) {
+         inCertificate = true;
+      }
+      if (inCertificate) {
+         serviceCertificateHandler.startElement(url, name, qName, attributes);
+      }
+   }
+
+   @Override
+   public void endElement(final String uri, final String name, final String qName) {
+      if (qName.equals("Certificate")) {
+         inCertificate = false;
+         certificates.add(serviceCertificateHandler.getResult());
+      } else if (inCertificate) {
+         serviceCertificateHandler.endElement(uri, name, qName);
+      }
+   }
+
+   @Override
+   public void characters(final char ch[], final int start, final int length) {
+      if (inCertificate) {
+         serviceCertificateHandler.characters(ch, start, length);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e6a3ce33/azurecompute/src/main/java/org/jclouds/azurecompute/xml/ServiceCertificateHandler.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/xml/ServiceCertificateHandler.java b/azurecompute/src/main/java/org/jclouds/azurecompute/xml/ServiceCertificateHandler.java
new file mode 100644
index 0000000..b2594b2
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/xml/ServiceCertificateHandler.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.xml;
+
+import static org.jclouds.util.SaxUtils.currentOrNull;
+
+import org.jclouds.http.functions.ParseSax;
+import org.xml.sax.Attributes;
+
+import java.net.URI;
+import org.jclouds.azurecompute.domain.ServiceCertificate;
+
+/**
+ * @see <a href="http://msdn.microsoft.com/en-us/library/gg441293" >Response body description</a>
+ */
+public final class ServiceCertificateHandler extends ParseSax.HandlerForGeneratedRequestWithResult<ServiceCertificate> {
+
+   private URI url;
+
+   private String thumbprint;
+
+   private String thumbprintAlgorithm;
+
+   private String data;
+
+   private final StringBuilder currentText = new StringBuilder();
+
+   @Override
+   public ServiceCertificate getResult() {
+      final ServiceCertificate result = ServiceCertificate.create(url, thumbprint, thumbprintAlgorithm, data);
+      resetState(); // handler is called in a loop.
+      return result;
+   }
+
+   private void resetState() {
+      url = null;
+      thumbprint = thumbprintAlgorithm = data = null;
+   }
+
+   @Override
+   public void startElement(
+           final String ignoredUri,
+           final String ignoredLocalName,
+           final String qName,
+           final Attributes ignoredAttributes) {
+   }
+
+   @Override
+   public void endElement(final String ignoredUri, final String ignoredName, final String qName) {
+      if (qName.equals("CertificateUrl")) {
+         url = URI.create(currentOrNull(currentText));
+      } else if (qName.equals("Thumbprint")) {
+         thumbprint = currentOrNull(currentText);
+      } else if (qName.equals("ThumbprintAlgorithm")) {
+         thumbprintAlgorithm = currentOrNull(currentText);
+      } else if (qName.equals("Data")) {
+         data = currentOrNull(currentText);
+      }
+      currentText.setLength(0);
+   }
+
+   @Override
+   public void characters(final char ch[], final int start, final int length) {
+      currentText.append(ch, start, length);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e6a3ce33/azurecompute/src/test/java/org/jclouds/azurecompute/features/ServiceCertificatesApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/java/org/jclouds/azurecompute/features/ServiceCertificatesApiLiveTest.java b/azurecompute/src/test/java/org/jclouds/azurecompute/features/ServiceCertificatesApiLiveTest.java
new file mode 100644
index 0000000..42acab6
--- /dev/null
+++ b/azurecompute/src/test/java/org/jclouds/azurecompute/features/ServiceCertificatesApiLiveTest.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.features;
+
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.jclouds.azurecompute.domain.ServiceCertificate;
+import org.jclouds.azurecompute.domain.ServiceCertificateParams;
+import org.jclouds.azurecompute.internal.BaseAzureComputeApiLiveTest;
+import static org.jclouds.azurecompute.internal.BaseAzureComputeApiLiveTest.LOCATION;
+import org.testng.Assert;
+import static org.testng.Assert.assertTrue;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+@Test(groups = "live", testName = "ServiceCertificatesApiLivTest", singleThreaded = true)
+public class ServiceCertificatesApiLiveTest extends BaseAzureComputeApiLiveTest {
+
+   private static final String CLOUD_SERVICE = String.format("%s%d-%s",
+           System.getProperty("user.name"), RAND, CloudServiceApiLiveTest.class.getSimpleName()).toLowerCase();
+
+   private static final String FORMAT = "pfx";
+   private static final String PASSWORD = "password";
+
+   private static final String THUMBPRINT = "8D6ED1395205C57D23E518672903FDAF144EE8AE";
+   private static final String THUMBPRINT_ALGO = "sha1";
+   private static final String DATA
+           = "MIIDyzCCArOgAwIBAgICEAcwDQYJKoZIhvcNAQELBQAwfzELMAkGA1UEBhMCSVQxDjAMBgNVBAgMBUl0YWx5MRAwDgYDVQQ"
+           + "HDAdQZXNjYXJhMQ8wDQYDVQQKDAZUaXJhc2ExDDAKBgNVBAsMA0lUQzEPMA0GA1UEAwwGVGlyYXNhMR4wHAYJKoZIh"
+           + "vcNAQkBFg9pbmZvQHRpcmFzYS5uZXQwHhcNMTUwMzA0MTQ1MzQwWhcNMTYwMzAzMTQ1MzQwWjBVMQswCQYDVQQGEwJ"
+           + "JVDEQMA4GA1UECAwHUGVzY2FyYTEPMA0GA1UECgwGVGlyYXNhMQswCQYDVQQLDAJBTTEWMBQGA1UEAwwNYW0udGlyY"
+           + "XNhLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMzqTZFbzahmEqp60txg8aUYw4Y7PL44A7rzHVn"
+           + "b5cb01/4VVjNeijmROOL8o5ZEbkNkQly43zjoZcrkw4bpvOz95OP8/NH/ZgyYKR42VqcTlxcj/22iq2Ie1XhWsKARm"
+           + "ObdnNUcFCsdqXWXBo0bLF+WuUYh4ZoMxFMlP7YYl7WOCCgekE8E9sL02RuLZgq7v2M6fsxhT5rEG81jzUlmY5c/jXZ"
+           + "KbUIBaltKtzC3DnBpuk9u+S87WseqTeuhUzF6VvgwmiQ+zeHdr5Hjqxrvmq445DPz+2U3PYN1vFoB/6QzjtZVakSfO"
+           + "SZ0YAtFhZFHmR11kJTNMfVQ5k5oIQPtHksCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblN"
+           + "TTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFOIYM6WyNdc4odsThFVtOefT/xg1MB8GA1UdIwQYMBaAF"
+           + "DqheOl4dpXYelhPC/bM+VdN1AXpMA0GCSqGSIb3DQEBCwUAA4IBAQB33qLYghIYa2j1ycHBpeZVadsb8xb4AnfnAW9"
+           + "g5dYfZP1eIvmKzOxN3CjpuCRKNI4vyKHiLbucfFDl5zi9BdYwwdduPbYTgE8F8Ilyit3irSRJFk1wHICX0sBPq5ulz"
+           + "39MPZsP2Fmzbrphr9BrRZOc1RJdHnj8C7phrfBneGSfwoY+qH5H6/h5A5rS8oDAraeklR2RJK4ztK+yDvp8orRDJQq"
+           + "5LAALQtWDhdW8Qj7WoIbGUeB77aJLluLOgriJLK+kKaGoUuAaKFRJXPyTmtUC17CJUJbapmtDwivILhU/dSdz6+1YX"
+           + "Tg0ddNNlug3I6L5VVRnlwJJc/hIna1VjQJO";
+
+   @BeforeClass
+   @Override
+   public void setup() {
+      super.setup();
+      final String requestId
+              = api.getCloudServiceApi().createWithLabelInLocation(CLOUD_SERVICE, CLOUD_SERVICE, LOCATION);
+      assertTrue(operationSucceeded.apply(requestId), requestId);
+      Logger.getAnonymousLogger().log(Level.INFO, "operation succeeded: {0}", requestId);
+   }
+
+   @Test
+   public void testAdd() {
+
+      final String requestId = api().add(CLOUD_SERVICE,
+              ServiceCertificateParams.builder().data(DATA).format(FORMAT).password(PASSWORD).build());
+
+      assertTrue(operationSucceeded.apply(requestId), requestId);
+      Logger.getAnonymousLogger().log(Level.INFO, "operation succeeded: {0}", requestId);
+   }
+
+   @Test(dependsOnMethods = "testAdd")
+   public void testList() {
+      final List<ServiceCertificate> res = api().list(CLOUD_SERVICE);
+      Assert.assertEquals(res.size(), 1);
+      Assert.assertEquals(res.get(0).data(), DATA);
+      Assert.assertEquals(res.get(0).thumbprintAlgorithm(), THUMBPRINT_ALGO);
+      Assert.assertEquals(res.get(0).thumbprint(), THUMBPRINT);
+   }
+
+   @Test(dependsOnMethods = "testList")
+   public void testGet() {
+      Assert.assertNotNull(api().get(CLOUD_SERVICE, THUMBPRINT_ALGO, THUMBPRINT));
+   }
+
+   @Test(dependsOnMethods = "testGet")
+   public void testDelete() {
+      final String requestId = api().delete(CLOUD_SERVICE, THUMBPRINT_ALGO, THUMBPRINT);
+      assertTrue(operationSucceeded.apply(requestId), requestId);
+      Logger.getAnonymousLogger().log(Level.INFO, "operation succeeded: {0}", requestId);
+
+      Assert.assertNull(api().get(CLOUD_SERVICE, THUMBPRINT_ALGO, THUMBPRINT));
+   }
+
+   @Override
+   @AfterClass(alwaysRun = true)
+   protected void tearDown() {
+      final String requestId = api.getCloudServiceApi().delete(CLOUD_SERVICE);
+      if (requestId != null) {
+         operationSucceeded.apply(requestId);
+      }
+
+      super.tearDown();
+   }
+
+   private ServiceCertificatesApi api() {
+      return api.getServiceCertificatesApi();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e6a3ce33/azurecompute/src/test/java/org/jclouds/azurecompute/features/ServiceCertificatesApiMockTest.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/java/org/jclouds/azurecompute/features/ServiceCertificatesApiMockTest.java b/azurecompute/src/test/java/org/jclouds/azurecompute/features/ServiceCertificatesApiMockTest.java
new file mode 100644
index 0000000..29d5177
--- /dev/null
+++ b/azurecompute/src/test/java/org/jclouds/azurecompute/features/ServiceCertificatesApiMockTest.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.features;
+
+import static org.testng.Assert.assertTrue;
+
+import org.jclouds.azurecompute.internal.BaseAzureComputeApiMockTest;
+import org.testng.annotations.Test;
+
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+import org.jclouds.azurecompute.domain.ServiceCertificateParams;
+import org.jclouds.azurecompute.xml.ListServiceCertificatessHandlerTest;
+import org.jclouds.azurecompute.xml.ServiceCertificateHandlerTest;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+@Test(groups = "unit", testName = "ServiceCertificatesApiMockTest")
+public class ServiceCertificatesApiMockTest extends BaseAzureComputeApiMockTest {
+
+   public void listWhenFound() throws Exception {
+      final MockWebServer server = mockAzureManagementServer();
+      server.enqueue(xmlResponse("/servicecertificates.xml"));
+
+      try {
+         final ServiceCertificatesApi api = api(server.getUrl("/")).getServiceCertificatesApi();
+         assertEquals(api.list("myservice"), ListServiceCertificatessHandlerTest.expected());
+         assertSent(server, "GET", "/services/hostedservices/myservice/certificates");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void listWhenNotFound() throws Exception {
+      final MockWebServer server = mockAzureManagementServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      try {
+         final ServiceCertificatesApi api = api(server.getUrl("/")).getServiceCertificatesApi();
+         assertTrue(api.list("myservice").isEmpty());
+         assertSent(server, "GET", "/services/hostedservices/myservice/certificates");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void getWhenFound() throws Exception {
+      final MockWebServer server = mockAzureManagementServer();
+      server.enqueue(xmlResponse("/servicecertificate.xml"));
+
+      try {
+         final ServiceCertificatesApi api = api(server.getUrl("/")).getServiceCertificatesApi();
+         assertEquals(api.get("myservice", "sha1", "8D6ED1395205C57D23E518672903FDAF144EE8AE"),
+                 ServiceCertificateHandlerTest.expected());
+         assertSent(server, "GET",
+                 "/services/hostedservices/myservice/certificates/sha1-8D6ED1395205C57D23E518672903FDAF144EE8AE");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void getWhenNotFound() throws Exception {
+      final MockWebServer server = mockAzureManagementServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      try {
+         final ServiceCertificatesApi api = api(server.getUrl("/")).getServiceCertificatesApi();
+         assertNull(api.get("myservice", "sha1", "8D6ED1395205C57D23E518672903FDAF144EE8AE"));
+         assertSent(server, "GET",
+                 "/services/hostedservices/myservice/certificates/sha1-8D6ED1395205C57D23E518672903FDAF144EE8AE");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void deleteWhenFound() throws Exception {
+      final MockWebServer server = mockAzureManagementServer();
+      server.enqueue(requestIdResponse("request-1"));
+
+      try {
+         final ServiceCertificatesApi api = api(server.getUrl("/")).getServiceCertificatesApi();
+         assertEquals(api.delete("myservice", "sha1", "8D6ED1395205C57D23E518672903FDAF144EE8AE"), "request-1");
+         assertSent(server, "DELETE",
+                 "/services/hostedservices/myservice/certificates/sha1-8D6ED1395205C57D23E518672903FDAF144EE8AE");
+
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void deleteWhenNotFound() throws Exception {
+      final MockWebServer server = mockAzureManagementServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      try {
+         final ServiceCertificatesApi api = api(server.getUrl("/")).getServiceCertificatesApi();
+         assertNull(api.delete("myservice", "sha1", "8D6ED1395205C57D23E518672903FDAF144EE8AE"));
+         assertSent(server, "DELETE",
+                 "/services/hostedservices/myservice/certificates/sha1-8D6ED1395205C57D23E518672903FDAF144EE8AE");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void add() throws Exception {
+      final MockWebServer server = mockAzureManagementServer();
+      server.enqueue(requestIdResponse("request-1"));
+
+      try {
+         final ServiceCertificatesApi api = api(server.getUrl("/")).getServiceCertificatesApi();
+
+         final ServiceCertificateParams params = ServiceCertificateParams.builder().
+                 data("MIIDyzCCArOgAwIBAgICEAcwDQYJKoZIhvcNAQELBQAwfzELMAkGA1UEBhMCSVQxDjAMBgNVBAgMBUl0YWx5MRAwDgYDVQQ"
+                         + "HDAdQZXNjYXJhMQ8wDQYDVQQKDAZUaXJhc2ExDDAKBgNVBAsMA0lUQzEPMA0GA1UEAwwGVGlyYXNhMR4wHAYJKoZIh"
+                         + "vcNAQkBFg9pbmZvQHRpcmFzYS5uZXQwHhcNMTUwMzA0MTQ1MzQwWhcNMTYwMzAzMTQ1MzQwWjBVMQswCQYDVQQGEwJ"
+                         + "JVDEQMA4GA1UECAwHUGVzY2FyYTEPMA0GA1UECgwGVGlyYXNhMQswCQYDVQQLDAJBTTEWMBQGA1UEAwwNYW0udGlyY"
+                         + "XNhLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMzqTZFbzahmEqp60txg8aUYw4Y7PL44A7rzHVn"
+                         + "b5cb01/4VVjNeijmROOL8o5ZEbkNkQly43zjoZcrkw4bpvOz95OP8/NH/ZgyYKR42VqcTlxcj/22iq2Ie1XhWsKARm"
+                         + "ObdnNUcFCsdqXWXBo0bLF+WuUYh4ZoMxFMlP7YYl7WOCCgekE8E9sL02RuLZgq7v2M6fsxhT5rEG81jzUlmY5c/jXZ"
+                         + "KbUIBaltKtzC3DnBpuk9u+S87WseqTeuhUzF6VvgwmiQ+zeHdr5Hjqxrvmq445DPz+2U3PYN1vFoB/6QzjtZVakSfO"
+                         + "SZ0YAtFhZFHmR11kJTNMfVQ5k5oIQPtHksCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblN"
+                         + "TTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFOIYM6WyNdc4odsThFVtOefT/xg1MB8GA1UdIwQYMBaAF"
+                         + "DqheOl4dpXYelhPC/bM+VdN1AXpMA0GCSqGSIb3DQEBCwUAA4IBAQB33qLYghIYa2j1ycHBpeZVadsb8xb4AnfnAW9"
+                         + "g5dYfZP1eIvmKzOxN3CjpuCRKNI4vyKHiLbucfFDl5zi9BdYwwdduPbYTgE8F8Ilyit3irSRJFk1wHICX0sBPq5ulz"
+                         + "39MPZsP2Fmzbrphr9BrRZOc1RJdHnj8C7phrfBneGSfwoY+qH5H6/h5A5rS8oDAraeklR2RJK4ztK+yDvp8orRDJQq"
+                         + "5LAALQtWDhdW8Qj7WoIbGUeB77aJLluLOgriJLK+kKaGoUuAaKFRJXPyTmtUC17CJUJbapmtDwivILhU/dSdz6+1YX"
+                         + "Tg0ddNNlug3I6L5VVRnlwJJc/hIna1VjQJO").
+                 format("pfx").
+                 password("password").build();
+
+         assertEquals(api.add("myservice", params), "request-1");
+         assertSent(server, "POST", "/services/hostedservices/myservice/certificates", "/servicecertificateparams.xml");
+      } finally {
+         server.shutdown();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e6a3ce33/azurecompute/src/test/java/org/jclouds/azurecompute/xml/ListServiceCertificatessHandlerTest.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/java/org/jclouds/azurecompute/xml/ListServiceCertificatessHandlerTest.java b/azurecompute/src/test/java/org/jclouds/azurecompute/xml/ListServiceCertificatessHandlerTest.java
new file mode 100644
index 0000000..a7e59df
--- /dev/null
+++ b/azurecompute/src/test/java/org/jclouds/azurecompute/xml/ListServiceCertificatessHandlerTest.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.xml;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+import java.util.List;
+
+import org.jclouds.http.functions.BaseHandlerTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import java.net.URI;
+import org.jclouds.azurecompute.domain.ServiceCertificate;
+
+@Test(groups = "unit", testName = "ListServiceCertificatessHandlerTest")
+public class ListServiceCertificatessHandlerTest extends BaseHandlerTest {
+
+   public void test() {
+      final InputStream is = getClass().getResourceAsStream("/servicecertificates.xml");
+      final ListServiceCertificatesHandler handler
+              = new ListServiceCertificatesHandler(new ServiceCertificateHandler());
+      final List<ServiceCertificate> result = factory.create(handler).parse(is);
+
+      assertEquals(result, expected());
+   }
+
+   public static List<ServiceCertificate> expected() {
+      return ImmutableList.of(
+              ServiceCertificate.create(
+                      URI.create("https://management.core.windows.net/d6769fbe-4649-453f-8435-c07f0cc0709d/services/"
+                              + "hostedservices/prova/certificates/sha1-8D6ED1395205C57D23E518672903FDAF144EE8AE"),
+                      "8D6ED1395205C57D23E518672903FDAF144EE8AE",
+                      "sha1",
+                      "MIIDyzCCArOgAwIBAgICEAcwDQYJKoZIhvcNAQELBQAwfzELMAkGA1UEBhMCSVQxDjAMBgNVBAgMBUl0YWx5MRAwDgYDVQQ"
+                      + "HDAdQZXNjYXJhMQ8wDQYDVQQKDAZUaXJhc2ExDDAKBgNVBAsMA0lUQzEPMA0GA1UEAwwGVGlyYXNhMR4wHAYJ"
+                      + "KoZIhvcNAQkBFg9pbmZvQHRpcmFzYS5uZXQwHhcNMTUwMzA0MTQ1MzQwWhcNMTYwMzAzMTQ1MzQwWjBVMQswC"
+                      + "QYDVQQGEwJJVDEQMA4GA1UECAwHUGVzY2FyYTEPMA0GA1UECgwGVGlyYXNhMQswCQYDVQQLDAJBTTEWMBQGA1"
+                      + "UEAwwNYW0udGlyYXNhLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMzqTZFbzahmEqp60tx"
+                      + "g8aUYw4Y7PL44A7rzHVnb5cb01/4VVjNeijmROOL8o5ZEbkNkQly43zjoZcrkw4bpvOz95OP8/NH/ZgyYKR42"
+                      + "VqcTlxcj/22iq2Ie1XhWsKARmObdnNUcFCsdqXWXBo0bLF+WuUYh4ZoMxFMlP7YYl7WOCCgekE8E9sL02RuLZ"
+                      + "gq7v2M6fsxhT5rEG81jzUlmY5c/jXZKbUIBaltKtzC3DnBpuk9u+S87WseqTeuhUzF6VvgwmiQ+zeHdr5Hjqx"
+                      + "rvmq445DPz+2U3PYN1vFoB/6QzjtZVakSfOSZ0YAtFhZFHmR11kJTNMfVQ5k5oIQPtHksCAwEAAaN7MHkwCQY"
+                      + "DVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE"
+                      + "FOIYM6WyNdc4odsThFVtOefT/xg1MB8GA1UdIwQYMBaAFDqheOl4dpXYelhPC/bM+VdN1AXpMA0GCSqGSIb3D"
+                      + "QEBCwUAA4IBAQB33qLYghIYa2j1ycHBpeZVadsb8xb4AnfnAW9g5dYfZP1eIvmKzOxN3CjpuCRKNI4vyKHiLb"
+                      + "ucfFDl5zi9BdYwwdduPbYTgE8F8Ilyit3irSRJFk1wHICX0sBPq5ulz39MPZsP2Fmzbrphr9BrRZOc1RJdHnj"
+                      + "8C7phrfBneGSfwoY+qH5H6/h5A5rS8oDAraeklR2RJK4ztK+yDvp8orRDJQq5LAALQtWDhdW8Qj7WoIbGUeB7"
+                      + "7aJLluLOgriJLK+kKaGoUuAaKFRJXPyTmtUC17CJUJbapmtDwivILhU/dSdz6+1YXTg0ddNNlug3I6L5VVRnl"
+                      + "wJJc/hIna1VjQJO"
+              ),
+              ServiceCertificate.create(
+                      URI.create("https://management.core.windows.net/d6769fbe-4649-453f-8435-c07f0cc0709d/services/"
+                              + "hostedservices/prova/certificates/sha1-CCA59C5AA24866BB292F01B81E6A77FAF8FCDC73"),
+                      "CCA59C5AA24866BB292F01B81E6A77FAF8FCDC73",
+                      "sha1",
+                      "MIIDzTCCArWgAwIBAgICEAYwDQYJKoZIhvcNAQELBQAwfzELMAkGA1UEBhMCSVQxDjAMBgNVBAgMBUl0YWx5MRAwDgYDVQ"
+                      + "QHDAdQZXNjYXJhMQ8wDQYDVQQKDAZUaXJhc2ExDDAKBgNVBAsMA0lUQzEPMA0GA1UEAwwGVGlyYXNhMR4wHAY"
+                      + "JKoZIhvcNAQkBFg9pbmZvQHRpcmFzYS5uZXQwHhcNMTUwMzA0MTQ1MTI2WhcNMTYwMzAzMTQ1MTI2WjBXMQsw"
+                      + "CQYDVQQGEwJJVDEQMA4GA1UECAwHUGVzY2FyYTEPMA0GA1UECgwGVGlyYXNhMQwwCgYDVQQLDANJZE0xFzAVB"
+                      + "gNVBAMMDmlkbS50aXJhc2EubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1G1ejg5eHZzhVc"
+                      + "a8OOD6pq0fj5VDEYIxG7CGDl4I8N3NE+svIGTYumdKUn1+FFyfgxsPCh7zfoSq0hnelRpBI3w9wfic1856/wN"
+                      + "z4ZsLQgcrm6wwwVXEfWGKYF1r8pTBzAYqNzQFqypSL9kU/YJfeY8XR3eJ3vQersAiKUQVQqk1H10R2aURqlCF"
+                      + "s1xc/ta9INNS+SLgWEBmQNnpwHfb7IsIYmPfqvbZsfAJ9KDqIdA5mjPz1elHNLLMi4phGPpbAH7AszZbrRaFt"
+                      + "bI0o5nAL6tS37f3iEV1L7cWo/am6MGg0PF4T9GRdL8D0gl9BDskMUHD+n8cJOEO2sQVJBKszwIDAQABo3sweT"
+                      + "AJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4"
+                      + "EFgQU4GbbmOK+5WNBil8edbwKYHIhCJMwHwYDVR0jBBgwFoAUOqF46Xh2ldh6WE8L9sz5V03UBekwDQYJKoZI"
+                      + "hvcNAQELBQADggEBAMqr+GoBBf7UPo4dBoQef4OhrNGCcYz6E0B8WID7ZG6KLv6TyTi5iEna/bbc60HisVqUo"
+                      + "FIMyMa+bHiLNwObNQ/+edklNyPe48OHYX2421x3H//M/n6SpRxP5i1NSlqtcw6WnXzaTHUwV8v+5ctG1QAKrJ"
+                      + "+nwwDRWzALxPJvw4TDeggRIkzAqIySrN+nRkVNliGlnZEI8NjJdelS/83E02LAxj3sPJp1yS5lWia88eNg6UX"
+                      + "Y2vQf9CwrXjOz0aOvVOwHJxXBxS0tqv+bg0D5B640WdcZOhgzDxte6DDkiSU+P7nZUW1Bwtk0WD9GKN2+YPg/"
+                      + "ElLLB5nSEGZSVN6Xfn8="
+              ));
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e6a3ce33/azurecompute/src/test/java/org/jclouds/azurecompute/xml/ServiceCertificateHandlerTest.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/java/org/jclouds/azurecompute/xml/ServiceCertificateHandlerTest.java b/azurecompute/src/test/java/org/jclouds/azurecompute/xml/ServiceCertificateHandlerTest.java
new file mode 100644
index 0000000..b98b2a3
--- /dev/null
+++ b/azurecompute/src/test/java/org/jclouds/azurecompute/xml/ServiceCertificateHandlerTest.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.xml;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+import org.jclouds.azurecompute.domain.ServiceCertificate;
+import org.jclouds.http.functions.BaseHandlerTest;
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "ServiceCertificateHandlerTest")
+public class ServiceCertificateHandlerTest extends BaseHandlerTest {
+
+   public void test() {
+      final InputStream is = getClass().getResourceAsStream("/servicecertificate.xml");
+      final ServiceCertificate result = factory.create(new ServiceCertificateHandler()).parse(is);
+      assertEquals(result, expected());
+   }
+
+   public static ServiceCertificate expected() {
+      return ServiceCertificate.create(
+              null,
+              null,
+              null,
+              "MIIDyzCCArOgAwIBAgICEAcwDQYJKoZIhvcNAQELBQAwfzELMAkGA1UEBhMCSVQxDjAMBgNVBAgMBUl0YWx5MRAwDgYDVQQHDAdQZXN"
+              + "jYXJhMQ8wDQYDVQQKDAZUaXJhc2ExDDAKBgNVBAsMA0lUQzEPMA0GA1UEAwwGVGlyYXNhMR4wHAYJKoZIhvcNAQkBFg9pbmZvQHRp"
+              + "cmFzYS5uZXQwHhcNMTUwMzA0MTQ1MzQwWhcNMTYwMzAzMTQ1MzQwWjBVMQswCQYDVQQGEwJJVDEQMA4GA1UECAwHUGVzY2FyYTEPM"
+              + "A0GA1UECgwGVGlyYXNhMQswCQYDVQQLDAJBTTEWMBQGA1UEAwwNYW0udGlyYXNhLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPAD"
+              + "CCAQoCggEBAMzqTZFbzahmEqp60txg8aUYw4Y7PL44A7rzHVnb5cb01/4VVjNeijmROOL8o5ZEbkNkQly43zjoZcrkw4bpvOz95OP"
+              + "8/NH/ZgyYKR42VqcTlxcj/22iq2Ie1XhWsKARmObdnNUcFCsdqXWXBo0bLF+WuUYh4ZoMxFMlP7YYl7WOCCgekE8E9sL02RuLZgq7"
+              + "v2M6fsxhT5rEG81jzUlmY5c/jXZKbUIBaltKtzC3DnBpuk9u+S87WseqTeuhUzF6VvgwmiQ+zeHdr5Hjqxrvmq445DPz+2U3PYN1v"
+              + "FoB/6QzjtZVakSfOSZ0YAtFhZFHmR11kJTNMfVQ5k5oIQPtHksCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3"
+              + "BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFOIYM6WyNdc4odsThFVtOefT/xg1MB8GA1UdIwQYMBaAFDqheOl"
+              + "4dpXYelhPC/bM+VdN1AXpMA0GCSqGSIb3DQEBCwUAA4IBAQB33qLYghIYa2j1ycHBpeZVadsb8xb4AnfnAW9g5dYfZP1eIvmKzOxN"
+              + "3CjpuCRKNI4vyKHiLbucfFDl5zi9BdYwwdduPbYTgE8F8Ilyit3irSRJFk1wHICX0sBPq5ulz39MPZsP2Fmzbrphr9BrRZOc1RJdH"
+              + "nj8C7phrfBneGSfwoY+qH5H6/h5A5rS8oDAraeklR2RJK4ztK+yDvp8orRDJQq5LAALQtWDhdW8Qj7WoIbGUeB77aJLluLOgriJLK"
+              + "+kKaGoUuAaKFRJXPyTmtUC17CJUJbapmtDwivILhU/dSdz6+1YXTg0ddNNlug3I6L5VVRnlwJJc/hIna1VjQJO"
+      );
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e6a3ce33/azurecompute/src/test/resources/servicecertificate.xml
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/resources/servicecertificate.xml b/azurecompute/src/test/resources/servicecertificate.xml
new file mode 100644
index 0000000..82b28e4
--- /dev/null
+++ b/azurecompute/src/test/resources/servicecertificate.xml
@@ -0,0 +1,4 @@
+<Certificate xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
+  <Data>MIIDyzCCArOgAwIBAgICEAcwDQYJKoZIhvcNAQELBQAwfzELMAkGA1UEBhMCSVQxDjAMBgNVBAgMBUl0YWx5MRAwDgYDVQQHDAdQZXNjYXJhMQ8wDQYDVQQKDAZUaXJhc2ExDDAKBgNVBAsMA0lUQzEPMA0GA1UEAwwGVGlyYXNhMR4wHAYJKoZIhvcNAQkBFg9pbmZvQHRpcmFzYS5uZXQwHhcNMTUwMzA0MTQ1MzQwWhcNMTYwMzAzMTQ1MzQwWjBVMQswCQYDVQQGEwJJVDEQMA4GA1UECAwHUGVzY2FyYTEPMA0GA1UECgwGVGlyYXNhMQswCQYDVQQLDAJBTTEWMBQGA1UEAwwNYW0udGlyYXNhLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMzqTZFbzahmEqp60txg8aUYw4Y7PL44A7rzHVnb5cb01/4VVjNeijmROOL8o5ZEbkNkQly43zjoZcrkw4bpvOz95OP8/NH/ZgyYKR42VqcTlxcj/22iq2Ie1XhWsKARmObdnNUcFCsdqXWXBo0bLF+WuUYh4ZoMxFMlP7YYl7WOCCgekE8E9sL02RuLZgq7v2M6fsxhT5rEG81jzUlmY5c/jXZKbUIBaltKtzC3DnBpuk9u+S87WseqTeuhUzF6VvgwmiQ+zeHdr5Hjqxrvmq445DPz+2U3PYN1vFoB/6QzjtZVakSfOSZ0YAtFhZFHmR11kJTNMfVQ5k5oIQPtHksCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFOIYM6WyNdc4odsThFVtOefT/xg1MB8GA1UdIwQYMBaAFDqheOl4dpXYelhPC/bM+VdN1AXpMA0GCSqGSIb3DQEBCwUAA4IBAQB33qLYghIYa2j1ycHBpeZVadsb8xb4A
 nfnAW9g5dYfZP1eIvmKzOxN3CjpuCRKNI4vyKHiLbucfFDl5zi9BdYwwdduPbYTgE8F8Ilyit3irSRJFk1wHICX0sBPq5ulz39MPZsP2Fmzbrphr9BrRZOc1RJdHnj8C7phrfBneGSfwoY+qH5H6/h5A5rS8oDAraeklR2RJK4ztK+yDvp8orRDJQq5LAALQtWDhdW8Qj7WoIbGUeB77aJLluLOgriJLK+kKaGoUuAaKFRJXPyTmtUC17CJUJbapmtDwivILhU/dSdz6+1YXTg0ddNNlug3I6L5VVRnlwJJc/hIna1VjQJO</Data>
+</Certificate>
+

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e6a3ce33/azurecompute/src/test/resources/servicecertificateparams.xml
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/resources/servicecertificateparams.xml b/azurecompute/src/test/resources/servicecertificateparams.xml
new file mode 100644
index 0000000..aed8229
--- /dev/null
+++ b/azurecompute/src/test/resources/servicecertificateparams.xml
@@ -0,0 +1 @@
+<CertificateFile xmlns="http://schemas.microsoft.com/windowsazure"><Data>MIIDyzCCArOgAwIBAgICEAcwDQYJKoZIhvcNAQELBQAwfzELMAkGA1UEBhMCSVQxDjAMBgNVBAgMBUl0YWx5MRAwDgYDVQQHDAdQZXNjYXJhMQ8wDQYDVQQKDAZUaXJhc2ExDDAKBgNVBAsMA0lUQzEPMA0GA1UEAwwGVGlyYXNhMR4wHAYJKoZIhvcNAQkBFg9pbmZvQHRpcmFzYS5uZXQwHhcNMTUwMzA0MTQ1MzQwWhcNMTYwMzAzMTQ1MzQwWjBVMQswCQYDVQQGEwJJVDEQMA4GA1UECAwHUGVzY2FyYTEPMA0GA1UECgwGVGlyYXNhMQswCQYDVQQLDAJBTTEWMBQGA1UEAwwNYW0udGlyYXNhLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMzqTZFbzahmEqp60txg8aUYw4Y7PL44A7rzHVnb5cb01/4VVjNeijmROOL8o5ZEbkNkQly43zjoZcrkw4bpvOz95OP8/NH/ZgyYKR42VqcTlxcj/22iq2Ie1XhWsKARmObdnNUcFCsdqXWXBo0bLF+WuUYh4ZoMxFMlP7YYl7WOCCgekE8E9sL02RuLZgq7v2M6fsxhT5rEG81jzUlmY5c/jXZKbUIBaltKtzC3DnBpuk9u+S87WseqTeuhUzF6VvgwmiQ+zeHdr5Hjqxrvmq445DPz+2U3PYN1vFoB/6QzjtZVakSfOSZ0YAtFhZFHmR11kJTNMfVQ5k5oIQPtHksCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFOIYM6WyNdc4odsThFVtOefT/xg1MB8GA1UdIwQYMBaAFDqheOl4dpXYelhPC/bM
 +VdN1AXpMA0GCSqGSIb3DQEBCwUAA4IBAQB33qLYghIYa2j1ycHBpeZVadsb8xb4AnfnAW9g5dYfZP1eIvmKzOxN3CjpuCRKNI4vyKHiLbucfFDl5zi9BdYwwdduPbYTgE8F8Ilyit3irSRJFk1wHICX0sBPq5ulz39MPZsP2Fmzbrphr9BrRZOc1RJdHnj8C7phrfBneGSfwoY+qH5H6/h5A5rS8oDAraeklR2RJK4ztK+yDvp8orRDJQq5LAALQtWDhdW8Qj7WoIbGUeB77aJLluLOgriJLK+kKaGoUuAaKFRJXPyTmtUC17CJUJbapmtDwivILhU/dSdz6+1YXTg0ddNNlug3I6L5VVRnlwJJc/hIna1VjQJO</Data><CertificateFormat>pfx</CertificateFormat><Password>password</Password></CertificateFile>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e6a3ce33/azurecompute/src/test/resources/servicecertificates.xml
----------------------------------------------------------------------
diff --git a/azurecompute/src/test/resources/servicecertificates.xml b/azurecompute/src/test/resources/servicecertificates.xml
new file mode 100644
index 0000000..ca47e6e
--- /dev/null
+++ b/azurecompute/src/test/resources/servicecertificates.xml
@@ -0,0 +1,14 @@
+<Certificates xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
+  <Certificate>
+    <CertificateUrl>https://management.core.windows.net/d6769fbe-4649-453f-8435-c07f0cc0709d/services/hostedservices/prova/certificates/sha1-8D6ED1395205C57D23E518672903FDAF144EE8AE</CertificateUrl>
+    <Thumbprint>8D6ED1395205C57D23E518672903FDAF144EE8AE</Thumbprint>
+    <ThumbprintAlgorithm>sha1</ThumbprintAlgorithm>
+    <Data>MIIDyzCCArOgAwIBAgICEAcwDQYJKoZIhvcNAQELBQAwfzELMAkGA1UEBhMCSVQxDjAMBgNVBAgMBUl0YWx5MRAwDgYDVQQHDAdQZXNjYXJhMQ8wDQYDVQQKDAZUaXJhc2ExDDAKBgNVBAsMA0lUQzEPMA0GA1UEAwwGVGlyYXNhMR4wHAYJKoZIhvcNAQkBFg9pbmZvQHRpcmFzYS5uZXQwHhcNMTUwMzA0MTQ1MzQwWhcNMTYwMzAzMTQ1MzQwWjBVMQswCQYDVQQGEwJJVDEQMA4GA1UECAwHUGVzY2FyYTEPMA0GA1UECgwGVGlyYXNhMQswCQYDVQQLDAJBTTEWMBQGA1UEAwwNYW0udGlyYXNhLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMzqTZFbzahmEqp60txg8aUYw4Y7PL44A7rzHVnb5cb01/4VVjNeijmROOL8o5ZEbkNkQly43zjoZcrkw4bpvOz95OP8/NH/ZgyYKR42VqcTlxcj/22iq2Ie1XhWsKARmObdnNUcFCsdqXWXBo0bLF+WuUYh4ZoMxFMlP7YYl7WOCCgekE8E9sL02RuLZgq7v2M6fsxhT5rEG81jzUlmY5c/jXZKbUIBaltKtzC3DnBpuk9u+S87WseqTeuhUzF6VvgwmiQ+zeHdr5Hjqxrvmq445DPz+2U3PYN1vFoB/6QzjtZVakSfOSZ0YAtFhZFHmR11kJTNMfVQ5k5oIQPtHksCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFOIYM6WyNdc4odsThFVtOefT/xg1MB8GA1UdIwQYMBaAFDqheOl4dpXYelhPC/bM+VdN1AXpMA0GCSqGSIb3DQEBCwUAA4IBAQB33qLYghIYa2j1ycHBpeZVadsb8xb
 4AnfnAW9g5dYfZP1eIvmKzOxN3CjpuCRKNI4vyKHiLbucfFDl5zi9BdYwwdduPbYTgE8F8Ilyit3irSRJFk1wHICX0sBPq5ulz39MPZsP2Fmzbrphr9BrRZOc1RJdHnj8C7phrfBneGSfwoY+qH5H6/h5A5rS8oDAraeklR2RJK4ztK+yDvp8orRDJQq5LAALQtWDhdW8Qj7WoIbGUeB77aJLluLOgriJLK+kKaGoUuAaKFRJXPyTmtUC17CJUJbapmtDwivILhU/dSdz6+1YXTg0ddNNlug3I6L5VVRnlwJJc/hIna1VjQJO</Data>
+  </Certificate>
+  <Certificate>
+    <CertificateUrl>https://management.core.windows.net/d6769fbe-4649-453f-8435-c07f0cc0709d/services/hostedservices/prova/certificates/sha1-CCA59C5AA24866BB292F01B81E6A77FAF8FCDC73</CertificateUrl>
+    <Thumbprint>CCA59C5AA24866BB292F01B81E6A77FAF8FCDC73</Thumbprint>
+    <ThumbprintAlgorithm>sha1</ThumbprintAlgorithm>
+    <Data>MIIDzTCCArWgAwIBAgICEAYwDQYJKoZIhvcNAQELBQAwfzELMAkGA1UEBhMCSVQxDjAMBgNVBAgMBUl0YWx5MRAwDgYDVQQHDAdQZXNjYXJhMQ8wDQYDVQQKDAZUaXJhc2ExDDAKBgNVBAsMA0lUQzEPMA0GA1UEAwwGVGlyYXNhMR4wHAYJKoZIhvcNAQkBFg9pbmZvQHRpcmFzYS5uZXQwHhcNMTUwMzA0MTQ1MTI2WhcNMTYwMzAzMTQ1MTI2WjBXMQswCQYDVQQGEwJJVDEQMA4GA1UECAwHUGVzY2FyYTEPMA0GA1UECgwGVGlyYXNhMQwwCgYDVQQLDANJZE0xFzAVBgNVBAMMDmlkbS50aXJhc2EubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1G1ejg5eHZzhVca8OOD6pq0fj5VDEYIxG7CGDl4I8N3NE+svIGTYumdKUn1+FFyfgxsPCh7zfoSq0hnelRpBI3w9wfic1856/wNz4ZsLQgcrm6wwwVXEfWGKYF1r8pTBzAYqNzQFqypSL9kU/YJfeY8XR3eJ3vQersAiKUQVQqk1H10R2aURqlCFs1xc/ta9INNS+SLgWEBmQNnpwHfb7IsIYmPfqvbZsfAJ9KDqIdA5mjPz1elHNLLMi4phGPpbAH7AszZbrRaFtbI0o5nAL6tS37f3iEV1L7cWo/am6MGg0PF4T9GRdL8D0gl9BDskMUHD+n8cJOEO2sQVJBKszwIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU4GbbmOK+5WNBil8edbwKYHIhCJMwHwYDVR0jBBgwFoAUOqF46Xh2ldh6WE8L9sz5V03UBekwDQYJKoZIhvcNAQELBQADggEBAMqr+GoBBf7UPo4dBoQef4OhrNG
 CcYz6E0B8WID7ZG6KLv6TyTi5iEna/bbc60HisVqUoFIMyMa+bHiLNwObNQ/+edklNyPe48OHYX2421x3H//M/n6SpRxP5i1NSlqtcw6WnXzaTHUwV8v+5ctG1QAKrJ+nwwDRWzALxPJvw4TDeggRIkzAqIySrN+nRkVNliGlnZEI8NjJdelS/83E02LAxj3sPJp1yS5lWia88eNg6UXY2vQf9CwrXjOz0aOvVOwHJxXBxS0tqv+bg0D5B640WdcZOhgzDxte6DDkiSU+P7nZUW1Bwtk0WD9GKN2+YPg/ElLLB5nSEGZSVN6Xfn8=</Data>
+  </Certificate>
+</Certificates>
\ No newline at end of file