diff --git a/azure-keyvault-core/pom.xml b/azure-keyvault-core/pom.xml
index 649015a..3602f35 100644
--- a/azure-keyvault-core/pom.xml
+++ b/azure-keyvault-core/pom.xml
@@ -8,7 +8,7 @@
com.microsoft.azure
azure-keyvault-parent
- 1.1-alpha-2
+ 1.1-beta-1
../pom.xml
diff --git a/azure-keyvault-cryptography/pom.xml b/azure-keyvault-cryptography/pom.xml
index 4cbe47c..c40de43 100644
--- a/azure-keyvault-cryptography/pom.xml
+++ b/azure-keyvault-cryptography/pom.xml
@@ -9,7 +9,7 @@
com.microsoft.azure
azure-keyvault-parent
- 1.1-alpha-2
+ 1.1-beta-1
../pom.xml
@@ -57,12 +57,12 @@
com.microsoft.azure
azure-keyvault-core
- 1.1-alpha-2
+ 1.1-beta-1
com.microsoft.azure
azure-keyvault-webkey
- 1.1-alpha-2
+ 1.1-beta-1
diff --git a/azure-keyvault-extensions/pom.xml b/azure-keyvault-extensions/pom.xml
index 78269a6..10e252d 100644
--- a/azure-keyvault-extensions/pom.xml
+++ b/azure-keyvault-extensions/pom.xml
@@ -8,7 +8,7 @@
com.microsoft.azure
azure-keyvault-parent
- 1.1-alpha-2
+ 1.1-beta-1
../pom.xml
@@ -68,22 +68,22 @@
com.microsoft.azure
azure-keyvault-core
- 1.1-alpha-2
+ 1.1-beta-1
com.microsoft.azure
azure-keyvault-cryptography
- 1.1-alpha-2
+ 1.1-beta-1
com.microsoft.azure
azure-keyvault
- 1.1-alpha-2
+ 1.1-beta-1
com.microsoft.azure
azure-keyvault-webkey
- 1.1-alpha-2
+ 1.1-beta-1
com.microsoft.azure
diff --git a/azure-keyvault-webkey/pom.xml b/azure-keyvault-webkey/pom.xml
index b430666..3410e1d 100644
--- a/azure-keyvault-webkey/pom.xml
+++ b/azure-keyvault-webkey/pom.xml
@@ -1,117 +1,127 @@
-
-
- 4.0.0
-
- com.microsoft.azure
- azure-keyvault-parent
- 1.1-alpha-2
- ../pom.xml
-
+
+
+ 4.0.0
+
+ com.microsoft.azure
+ azure-keyvault-parent
+ 1.1-beta-1
+ ../pom.xml
+
- azure-keyvault-webkey
- jar
+ azure-keyvault-webkey
+ jar
- Microsoft Azure SDK for Key Vault WebKey
- This package contains Microsoft Azure Key Vault WebKey library.
- https://github.com/Azure/azure-sdk-for-java
+ Microsoft Azure SDK for Key Vault WebKey
+ This package contains Microsoft Azure Key Vault WebKey library.
+ https://github.com/Azure/azure-sdk-for-java
-
-
- The MIT License (MIT)
- http://opensource.org/licenses/MIT
- repo
-
-
+
+
+ The MIT License (MIT)
+ http://opensource.org/licenses/MIT
+ repo
+
+
-
- scm:git:https://github.com/Azure/azure-sdk-for-java
- scm:git:git@github.com:Azure/azure-sdk-for-java.git
- HEAD
-
+
+ scm:git:https://github.com/Azure/azure-sdk-for-java
+ scm:git:git@github.com:Azure/azure-sdk-for-java.git
+ HEAD
+
-
- UTF-8
-
-
+
+ UTF-8
+
+
-
-
- microsoft
- Microsoft
-
-
+
+
+ microsoft
+ Microsoft
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ commons-codec
+ commons-codec
+
+
+ com.google.guava
+ guava
+
+
+ com.microsoft.rest
+ client-runtime
+ 1.3.0
+
+
+ com.microsoft.azure
+ azure-client-authentication
+ 1.3.0
+
+
+ com.microsoft.azure
+ azure-keyvault
+ 1.1-beta-1
+
+
+
+ junit
+ junit
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.0.2
+
+
+
+ true
+ true
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 1.12
+
-
-
- com.fasterxml.jackson.core
- jackson-databind
-
-
- commons-codec
- commons-codec
-
-
- com.google.guava
- guava
- 20.0
-
-
-
-
- junit
- junit
- test
-
-
-
-
-
- org.apache.maven.plugins
- maven-jar-plugin
- 3.0.2
-
-
-
- true
- true
-
-
-
-
-
- org.codehaus.mojo
- build-helper-maven-plugin
- 1.12
-
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.1
+
+ 1.7
+ 1.7
+
+
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 3.1
-
- 1.7
- 1.7
-
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
- 2.8
-
- com.microsoft.schemas._2003._10.serialization;
- /**
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.8
+
+ com.microsoft.schemas._2003._10.serialization;
+ /**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*/]]>
-
-
-
-
+
+
+
+
diff --git a/azure-keyvault-webkey/src/main/java/com/microsoft/azure/keyvault/webkey/JsonWebKey.java b/azure-keyvault-webkey/src/main/java/com/microsoft/azure/keyvault/webkey/JsonWebKey.java
index 4e6ad1f..06b2740 100644
--- a/azure-keyvault-webkey/src/main/java/com/microsoft/azure/keyvault/webkey/JsonWebKey.java
+++ b/azure-keyvault-webkey/src/main/java/com/microsoft/azure/keyvault/webkey/JsonWebKey.java
@@ -9,24 +9,39 @@
import java.io.IOException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.EllipticCurve;
+import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -36,6 +51,8 @@
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableMap;
+import com.microsoft.azure.keyvault.models.JsonWebKeyCurveName;
/**
* As of http://tools.ietf.org/html/draft-ietf-jose-json-web-key-18.
@@ -46,32 +63,38 @@ public class JsonWebKey {
/**
* Key Identifier.
*/
+ @JsonProperty(value = "kid")
private String kid;
/**
- * Key type, usually RSA. Possible values include: 'EC', 'RSA', 'RSA-HSM',
- * 'oct'.
+ * JsonWebKey key type (kty). Possible values include: 'EC', 'EC-HSM',
+ * 'RSA', 'RSA-HSM', 'oct'.
*/
+ @JsonProperty(value = "kty")
private JsonWebKeyType kty;
/**
* The keyOps property.
*/
+ @JsonProperty(value = "key_ops")
private List keyOps;
/**
* RSA modulus.
*/
+ @JsonProperty(value = "n")
private byte[] n;
/**
* RSA public exponent.
*/
+ @JsonProperty(value = "e")
private byte[] e;
/**
- * RSA private exponent.
+ * RSA private exponent, or the D component of an EC private key.
*/
+ @JsonProperty(value = "d")
private byte[] d;
/**
@@ -95,7 +118,7 @@ public class JsonWebKey {
private byte[] p;
/**
- * RSA secret prime, with p < q.
+ * RSA secret prime, with p & q.
*/
private byte[] q;
@@ -107,12 +130,32 @@ public class JsonWebKey {
/**
* HSM Token, used with Bring Your Own Key.
*/
+ @JsonProperty(value = "key_hsm")
private byte[] t;
+
+ /**
+ * Elliptic curve name. For valid values, see JsonWebKeyCurveName. Possible
+ * values include: 'P-256', 'P-384', 'P-521', 'SECP256K1'.
+ */
+ @JsonProperty(value = "crv")
+ private JsonWebKeyCurveName crv;
/**
- * Key Identifier.
+ * X component of an EC public key.
+ */
+ @JsonProperty(value = "x")
+ private byte[] x;
+
+ /**
+ * Y component of an EC public key.
+ */
+ @JsonProperty(value = "y")
+ private byte[] y;
+
+ /**
+ * Get the kid value.
*
- * @return the kid value.
+ * @return the kid value
*/
@JsonProperty("kid")
public String kid() {
@@ -122,7 +165,7 @@ public String kid() {
/**
* Set the key identifier value.
*
- * @param kid the key identifier
+ * @param kid the kid value to set
* @return the JsonWebKey object itself.
*/
public JsonWebKey withKid(String kid) {
@@ -131,10 +174,9 @@ public JsonWebKey withKid(String kid) {
}
/**
- * Key type, usually RSA. Possible values include: 'EC', 'RSA', 'RSA-HSM',
- * 'oct'.
+ * Get the kty value.
*
- * @return the key type.
+ * @return the kty value
*/
@JsonProperty("kty")
public JsonWebKeyType kty() {
@@ -153,9 +195,9 @@ public JsonWebKey withKty(JsonWebKeyType kty) {
}
/**
- * Get the key operations.
+ * Get the keyOps value.
*
- * @return the key operations.
+ * @return the keyOps value
*/
@JsonProperty("key_ops")
public List keyOps() {
@@ -163,9 +205,9 @@ public List keyOps() {
}
/**
- * Set the key operations value.
+ * Set the keyOps value.
*
- * @param keyOps the key operations value to set
+ * @param keyOps the keyOps value to set
* @return the JsonWebKey object itself.
*/
public JsonWebKey withKeyOps(List keyOps) {
@@ -174,9 +216,9 @@ public JsonWebKey withKeyOps(List keyOps) {
}
/**
- * Get the RSA modulus value.
+ * Get the n value.
*
- * @return the RSA modulus value.
+ * @return the n value
*/
@JsonProperty("n")
@JsonSerialize(using = Base64UrlJsonSerializer.class)
@@ -186,9 +228,9 @@ public byte[] n() {
}
/**
- * Set the RSA modulus value.
+ * Set the n value.
*
- * @param n the RSA modulus value to set
+ * @param n the n value to set
* @return the JsonWebKey object itself.
*/
public JsonWebKey withN(byte[] n) {
@@ -197,8 +239,9 @@ public JsonWebKey withN(byte[] n) {
}
/**
- * Get the RSA public exponent value.
- * @return the RSA public exponent value.
+ * Get the e value.
+ *
+ * @return the e value
*/
@JsonProperty("e")
@JsonSerialize(using = Base64UrlJsonSerializer.class)
@@ -208,9 +251,9 @@ public byte[] e() {
}
/**
- * Set the RSA public exponent value.
- *
- * @param e RSA public exponent value to set
+ * Set the e value.
+ *
+ * @param e the e value to set
* @return the JsonWebKey object itself.
*/
public JsonWebKey withE(byte[] e) {
@@ -219,8 +262,9 @@ public JsonWebKey withE(byte[] e) {
}
/**
- * Get the RSA private exponent value.
- * @return the RSA private exponent value.
+ * Get the d value.
+ *
+ * @return the d value
*/
@JsonProperty("d")
@JsonSerialize(using = Base64UrlJsonSerializer.class)
@@ -230,9 +274,9 @@ public byte[] d() {
}
/**
- * Set RSA private exponent value.
- *
- * @param d the RSA private exponent value to set.
+ * Set the d value.
+ *
+ * @param d the d value to set
* @return the JsonWebKey object itself.
*/
public JsonWebKey withD(byte[] d) {
@@ -401,6 +445,73 @@ public String toString() {
}
}
+ /**
+ * Get the crv value.
+ *
+ * @return the crv value
+ */
+ @JsonProperty("crv")
+ public JsonWebKeyCurveName crv() {
+ return this.crv;
+ }
+
+ /**
+ * Set the crv value.
+ *
+ * @param crv the crv value to set
+ * @return the JsonWebKey object itself.
+ */
+ public JsonWebKey withCrv(JsonWebKeyCurveName crv) {
+ this.crv = crv;
+ return this;
+ }
+
+ /**
+ * Get the x value.
+ *
+ * @return the x value
+ */
+ @JsonProperty("x")
+ @JsonSerialize(using = Base64UrlJsonSerializer.class)
+ @JsonDeserialize(using = Base64UrlJsonDeserializer.class)
+ public byte[] x() {
+ return this.x;
+ }
+
+ /**
+ * Set the x value.
+ *
+ * @param x the x value to set
+ * @return the JsonWebKey object itself.
+ */
+ public JsonWebKey withX(byte[] x) {
+ this.x = x;
+ return this;
+ }
+
+ /**
+ * Get the y value.
+ *
+ * @return the y value
+ */
+ @JsonProperty("y")
+ @JsonSerialize(using = Base64UrlJsonSerializer.class)
+ @JsonDeserialize(using = Base64UrlJsonDeserializer.class)
+ public byte[] y() {
+ return this.y;
+ }
+
+ /**
+ * Set the y value.
+ *
+ * @param y the y value to set
+ * @return the JsonWebKey object itself.
+ */
+ public JsonWebKey withY(byte[] y) {
+ this.y = y;
+ return this;
+ }
+
/**
* Get the RSA public key spec value.
*
@@ -457,6 +568,21 @@ private PrivateKey getRSAPrivateKey(Provider provider) {
throw new IllegalStateException(e);
}
}
+
+
+ private static PublicKey getECPublicKey(ECPoint ecPoint, ECParameterSpec curveSpec, Provider provider) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeySpecException {
+ // Create public key spec with given point
+ ECPublicKeySpec pubSpec = new ECPublicKeySpec(ecPoint, curveSpec);
+ KeyFactory kf = provider != null ? KeyFactory.getInstance("EC", provider) : KeyFactory.getInstance("EC");
+ return (ECPublicKey) kf.generatePublic(pubSpec);
+
+ }
+
+ private static PrivateKey getECPrivateKey(byte[] d, ECParameterSpec curveSpec, Provider provider) throws NoSuchAlgorithmException, InvalidKeySpecException {
+ ECPrivateKeySpec priSpec = new ECPrivateKeySpec(new BigInteger(1, d), curveSpec);
+ KeyFactory kf = provider != null ? KeyFactory.getInstance("EC", provider) : KeyFactory.getInstance("EC");
+ return (ECPrivateKey) kf.generatePrivate(priSpec);
+ }
/**
* Verifies if the key is an RSA key.
@@ -564,7 +690,124 @@ public KeyPair toRSA(boolean includePrivateParameters, Provider provider) {
return new KeyPair(getRSAPublicKey(provider), null);
}
}
-
+
+ /**
+ * Converts JSON web key to EC key pair and include the private key if set to true.
+ * @return EC key pair
+ */
+ public KeyPair toEC() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeySpecException {
+ return toEC(false, null);
+ }
+
+ /**
+ * Converts JSON web key to EC key pair and include the private key if set to true.
+ * @param includePrivateParameters true if the EC key pair should include the private key. False otherwise.
+ * @return EC key pair
+ */
+ public KeyPair toEC(boolean includePrivateParameters) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeySpecException {
+ return toEC(includePrivateParameters, null);
+ }
+
+ /**
+ * Converts JSON web key to EC key pair and include the private key if set to true.
+ * @param provider the Java security provider.
+ * @param includePrivateParameters true if the EC key pair should include the private key. False otherwise.
+ * @return EC key pair
+ */
+ public KeyPair toEC(boolean includePrivateParameters, Provider provider) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeySpecException {
+
+ if (provider == null) {
+ //Our default provider for this class
+ provider = new BouncyCastleProvider();
+ }
+
+ if (!JsonWebKeyType.EC.equals(kty) && !JsonWebKeyType.EC_HSM.equals(kty)) {
+ throw new IllegalArgumentException("Not an EC key.");
+ }
+
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", provider);
+ ECGenParameterSpec gps = new ECGenParameterSpec(CURVE_TO_SPEC_NAME.get(crv));
+ kpg.initialize(gps);
+
+ // Generate dummy keypair to get parameter spec.
+ KeyPair apair = kpg.generateKeyPair();
+ ECPublicKey apub = (ECPublicKey) apair.getPublic();
+ ECParameterSpec aspec = apub.getParams();
+
+ ECPoint ecPoint = new ECPoint(new BigInteger(1, x), new BigInteger(1, y));
+
+ KeyPair realKeyPair;
+
+ if (includePrivateParameters) {
+ realKeyPair = new KeyPair(getECPublicKey(ecPoint, aspec, provider), getECPrivateKey(d, aspec, provider));
+ } else {
+ realKeyPair = new KeyPair(getECPublicKey(ecPoint, aspec, provider), null);
+ }
+
+ return realKeyPair;
+ }
+
+ /**
+ * Converts EC key pair to JSON web key.
+ * @param keyPair EC key pair
+ * @provider Java security provider
+ * @return the JSON web key, converted from EC key pair.
+ * @throws InvalidAlgorithmParameterException
+ * @throws NoSuchAlgorithmException
+ */
+ public static JsonWebKey fromEC(KeyPair keyPair, Provider provider) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
+
+ ECPublicKey apub = (ECPublicKey) keyPair.getPublic();
+ ECPoint point = apub.getW();
+ ECPrivateKey apriv = (ECPrivateKey) keyPair.getPrivate();
+
+ if (apriv != null) {
+ return new JsonWebKey()
+ .withKty(JsonWebKeyType.EC)
+ .withCrv(getCurveFromKeyPair(keyPair, provider))
+ .withX(point.getAffineX().toByteArray())
+ .withY(point.getAffineY().toByteArray())
+ .withD(apriv.getS().toByteArray())
+ .withKty(JsonWebKeyType.EC);
+ } else {
+ return new JsonWebKey()
+ .withKty(JsonWebKeyType.EC)
+ .withCrv(getCurveFromKeyPair(keyPair, provider))
+ .withX(point.getAffineX().toByteArray())
+ .withY(point.getAffineY().toByteArray())
+ .withKty(JsonWebKeyType.EC);
+ }
+ }
+
+ // Matches the curve of the keyPair to supported curves.
+ private static JsonWebKeyCurveName getCurveFromKeyPair(KeyPair keyPair, Provider provider) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
+ ECPublicKey key = (ECPublicKey) keyPair.getPublic();
+ ECParameterSpec spec = key.getParams();
+ EllipticCurve crv = spec.getCurve();
+
+ List curveList = Arrays.asList(JsonWebKeyCurveName.P_256, JsonWebKeyCurveName.P_384, JsonWebKeyCurveName.P_521, JsonWebKeyCurveName.SECP256K1);
+
+ for (JsonWebKeyCurveName curve : curveList) {
+ ECGenParameterSpec gps = new ECGenParameterSpec(CURVE_TO_SPEC_NAME.get(curve));
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", provider);
+ kpg.initialize(gps);
+
+ // Generate dummy keypair to get parameter spec.
+ KeyPair apair = kpg.generateKeyPair();
+ ECPublicKey apub = (ECPublicKey) apair.getPublic();
+ ECParameterSpec aspec = apub.getParams();
+ EllipticCurve acurve = aspec.getCurve();
+
+ //Matches the parameter spec
+ if (acurve.equals(crv)) {
+ return curve;
+ }
+ }
+
+ //Did not find a supported curve.
+ throw new NoSuchAlgorithmException("Curve not supported.");
+ }
+
/**
* Converts AES key to JSON web key.
* @param secretKey AES key
@@ -626,6 +869,10 @@ public boolean equals(JsonWebKey jwk) {
return false;
}
+ if (!Objects.equal(crv, jwk.crv)) {
+ return false;
+ }
+
if (!Arrays.equals(k, jwk.k)) {
return false;
}
@@ -657,6 +904,12 @@ public boolean equals(JsonWebKey jwk) {
if (!Arrays.equals(q, jwk.q)) {
return false;
}
+ if (!Arrays.equals(x, jwk.x)) {
+ return false;
+ }
+ if (!Arrays.equals(y, jwk.y)) {
+ return false;
+ }
// HSM token
if (!Arrays.equals(t, jwk.t)) {
@@ -680,6 +933,10 @@ else if (JsonWebKeyType.RSA.equals(kty) || JsonWebKeyType.RSA_HSM.equals(kty)) {
return (d != null && dp != null && dq != null && qi != null && p != null && q != null);
}
+ else if (JsonWebKeyType.EC.equals(kty) || JsonWebKeyType.EC_HSM.equals(kty)){
+ return (d != null);
+ }
+
return false;
}
@@ -713,6 +970,15 @@ else if (JsonWebKeyType.RSA.equals(kty)) {
else if (JsonWebKeyType.RSA_HSM.equals(kty)) {
return isValidRsaHsm();
}
+
+ else if (JsonWebKeyType.EC.equals(kty)) {
+ return isValidEc();
+ }
+
+ else if (JsonWebKeyType.EC_HSM.equals(kty)) {
+ return isValidEcHsm();
+ }
+
return false;
}
@@ -754,6 +1020,38 @@ private boolean isValidRsaHsm() {
return (tokenParameters || publicParameters);
}
+ private boolean isValidEc() {
+ boolean ecPointParameters = (x != null && y != null);
+ if (!ecPointParameters || crv == null) {
+ return false;
+ }
+
+ return hasPrivateKey() || (d == null);
+ }
+
+ private boolean isValidEcHsm() {
+ // MAY have public key parameters
+ boolean ecPointParameters = (x != null && y != null);
+ if ((ecPointParameters && crv == null) || (!ecPointParameters && crv != null)) {
+ return false;
+ }
+
+ // no private key
+ if (hasPrivateKey()) {
+ return false;
+ }
+
+ // MUST have (T || (ecPointParameters && crv))
+ boolean publicParameters = (ecPointParameters && crv != null);
+ boolean tokenParameters = t != null;
+
+ if (tokenParameters && publicParameters) {
+ return false;
+ }
+
+ return (tokenParameters || publicParameters);
+ }
+
/**
* Clear key materials.
*/
@@ -768,6 +1066,8 @@ public void clearMemory() {
zeroArray(p); p = null;
zeroArray(q); q = null;
zeroArray(t); t = null;
+ zeroArray(x); x = null;
+ zeroArray(y); y = null;
}
private static void zeroArray(byte[] bytes) {
@@ -791,7 +1091,13 @@ else if (JsonWebKeyType.RSA.equals(kty)) {
hashCode += hashCode(n);
}
- else if (JsonWebKeyType.RSA_HSM.equals(kty)) {
+ else if (JsonWebKeyType.EC.equals(kty)) {
+ hashCode += hashCode(x);
+ hashCode += hashCode(y);
+ hashCode += crv.hashCode();
+ }
+
+ else if (JsonWebKeyType.RSA_HSM.equals(kty) || JsonWebKeyType.EC_HSM.equals(kty)) {
hashCode += hashCode(t);
}
@@ -810,4 +1116,11 @@ private static int hashCode(byte[] obj) {
}
return hashCode;
}
+
+ private final static Map CURVE_TO_SPEC_NAME = ImmutableMap.builder()
+ .put(JsonWebKeyCurveName.P_256, "secp256r1")
+ .put(JsonWebKeyCurveName.P_384, "secp384r1")
+ .put(JsonWebKeyCurveName.P_521, "secp521r1")
+ .put(JsonWebKeyCurveName.SECP256K1, "secp256k1")
+ .build();
}
\ No newline at end of file
diff --git a/azure-keyvault-webkey/src/main/java/com/microsoft/azure/keyvault/webkey/JsonWebKeyEncryptionAlgorithm.java b/azure-keyvault-webkey/src/main/java/com/microsoft/azure/keyvault/webkey/JsonWebKeyEncryptionAlgorithm.java
index 67e5e99..ad3c888 100644
--- a/azure-keyvault-webkey/src/main/java/com/microsoft/azure/keyvault/webkey/JsonWebKeyEncryptionAlgorithm.java
+++ b/azure-keyvault-webkey/src/main/java/com/microsoft/azure/keyvault/webkey/JsonWebKeyEncryptionAlgorithm.java
@@ -16,8 +16,12 @@
* Defines values for JsonWebKeyEncryptionAlgorithm.
*/
public final class JsonWebKeyEncryptionAlgorithm {
- /** Static value RSA-OAEP for JsonWebKeyEncryptionAlgorithm. */
+
+ /** Static value RSA-OAEP for JsonWebKeyEncryptionAlgorithm. */
public static final JsonWebKeyEncryptionAlgorithm RSA_OAEP = new JsonWebKeyEncryptionAlgorithm("RSA-OAEP");
+
+ /** Static value RSA-OAEP-256 for JsonWebKeyEncryptionAlgorithm. */
+ public static final JsonWebKeyEncryptionAlgorithm RSA_OAEP_256 = new JsonWebKeyEncryptionAlgorithm("RSA-OAEP-256");
/** Static value RSA1_5 for JsonWebKeyEncryptionAlgorithm. */
public static final JsonWebKeyEncryptionAlgorithm RSA1_5 = new JsonWebKeyEncryptionAlgorithm("RSA1_5");
@@ -63,5 +67,5 @@ public boolean equals(Object obj) {
* All the JWK encryption algorithms.
*/
public static final List ALL_ALGORITHMS =
- Collections.unmodifiableList(Arrays.asList(RSA_OAEP, RSA1_5));
+ Collections.unmodifiableList(Arrays.asList(RSA_OAEP, RSA1_5, RSA_OAEP_256));
}
diff --git a/azure-keyvault-webkey/src/main/java/com/microsoft/azure/keyvault/webkey/JsonWebKeySignatureAlgorithm.java b/azure-keyvault-webkey/src/main/java/com/microsoft/azure/keyvault/webkey/JsonWebKeySignatureAlgorithm.java
index edd9692..199b804 100644
--- a/azure-keyvault-webkey/src/main/java/com/microsoft/azure/keyvault/webkey/JsonWebKeySignatureAlgorithm.java
+++ b/azure-keyvault-webkey/src/main/java/com/microsoft/azure/keyvault/webkey/JsonWebKeySignatureAlgorithm.java
@@ -16,7 +16,8 @@
* Defines values for JsonWebKeySignatureAlgorithm.
*/
public final class JsonWebKeySignatureAlgorithm {
- /** Static value PS256 for JsonWebKeySignatureAlgorithm. */
+
+ /** Static value PS256 for JsonWebKeySignatureAlgorithm. */
public static final JsonWebKeySignatureAlgorithm PS256 = new JsonWebKeySignatureAlgorithm("PS256");
/** Static value PS384 for JsonWebKeySignatureAlgorithm. */
@@ -36,6 +37,14 @@ public final class JsonWebKeySignatureAlgorithm {
/** Static value RSNULL for JsonWebKeySignatureAlgorithm. */
public static final JsonWebKeySignatureAlgorithm RSNULL = new JsonWebKeySignatureAlgorithm("RSNULL");
+ /** Static value ES256 for JsonWebKeySignatureAlgorithm. */
+ public static final JsonWebKeySignatureAlgorithm ES256 = new JsonWebKeySignatureAlgorithm("ES256");
+ /** Static value ES384 for JsonWebKeySignatureAlgorithm. */
+ public static final JsonWebKeySignatureAlgorithm ES384 = new JsonWebKeySignatureAlgorithm("ES384");
+ /** Static value ES512 for JsonWebKeySignatureAlgorithm. */
+ public static final JsonWebKeySignatureAlgorithm ES512 = new JsonWebKeySignatureAlgorithm("ES512");
+ /** Static value ECDSA256 for JsonWebKeySignatureAlgorithm. */
+ public static final JsonWebKeySignatureAlgorithm ECDSA256 = new JsonWebKeySignatureAlgorithm("ECDSA256");
private String value;
@@ -78,5 +87,5 @@ public boolean equals(Object obj) {
* All the JWK signature algorithms.
*/
public static final List ALL_ALGORITHMS =
- Collections.unmodifiableList(Arrays.asList(RS256, RS384, RS512, RSNULL));
+ Collections.unmodifiableList(Arrays.asList(RS256, RS384, RS512, RSNULL, PS256, PS384, PS512, ES256, ES384, ES512, ECDSA256));
}
diff --git a/azure-keyvault-webkey/src/main/java/com/microsoft/azure/keyvault/webkey/JsonWebKeyType.java b/azure-keyvault-webkey/src/main/java/com/microsoft/azure/keyvault/webkey/JsonWebKeyType.java
index 8a7c9db..dc5a735 100644
--- a/azure-keyvault-webkey/src/main/java/com/microsoft/azure/keyvault/webkey/JsonWebKeyType.java
+++ b/azure-keyvault-webkey/src/main/java/com/microsoft/azure/keyvault/webkey/JsonWebKeyType.java
@@ -18,6 +18,8 @@
public final class JsonWebKeyType {
/** Static value EC for JsonWebKeyType. */
public static final JsonWebKeyType EC = new JsonWebKeyType("EC");
+ /** Static value EC-HSM for JsonWebKeyType. */
+ public static final JsonWebKeyType EC_HSM = new JsonWebKeyType("EC-HSM");
/** Static value RSA for JsonWebKeyType. */
public static final JsonWebKeyType RSA = new JsonWebKeyType("RSA");
@@ -69,5 +71,5 @@ public boolean equals(Object obj) {
* All the JWK key types.
*/
public static final List ALL_TYPES =
- Collections.unmodifiableList(Arrays.asList(EC, RSA, RSA_HSM, OCT));
+ Collections.unmodifiableList(Arrays.asList(EC, RSA, RSA_HSM, OCT, EC_HSM));
}
diff --git a/azure-keyvault-webkey/src/test/java/com/microsoft/azure/keyvault/webkey/test/EcHsmValidationTests.java b/azure-keyvault-webkey/src/test/java/com/microsoft/azure/keyvault/webkey/test/EcHsmValidationTests.java
new file mode 100644
index 0000000..cb1cb9e
--- /dev/null
+++ b/azure-keyvault-webkey/src/test/java/com/microsoft/azure/keyvault/webkey/test/EcHsmValidationTests.java
@@ -0,0 +1,45 @@
+package com.microsoft.azure.keyvault.webkey.test;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.microsoft.azure.keyvault.webkey.JsonWebKey;
+
+public class EcHsmValidationTests {
+
+ String keyWithoutT = "{\"kid\":\"key_id\",\"kty\":\"EC-HSM\",\"key_ops\":null,\"n\":null,\"e\":null,\"d\":null,\"dp\":null,\"dq\":null,\"qi\":null,\"p\":null,\"q\":null,\"k\":null,\"key_hsm\":null,\"crv\":\"P-256\",\"x\":\"KyjF795jLyVIgswKSQInEGYHNBKSKyPgNojEgYlldMI\",\"y\":\"AIl_ca1ZIKbJ5YGdgGr_7HySldI2aWeBaOImZEYIMpVe\"}";
+ String keyWithT = "{\"kid\":\"key_id\",\"kty\":\"EC-HSM\",\"key_ops\":null,\"key_hsm\":\"T-TOKEN\"}";
+
+ @Test
+ public void ecHsmValidation() throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+ JsonWebKey keyNoT = mapper.readValue(keyWithoutT, JsonWebKey.class);
+ JsonWebKey keyT = mapper.readValue(keyWithT, JsonWebKey.class);
+
+ Assert.assertTrue(keyNoT.isValid());
+ Assert.assertFalse(keyNoT.hasPrivateKey());
+
+ Assert.assertTrue(keyT.isValid());
+ Assert.assertFalse(keyT.hasPrivateKey());
+ }
+
+ @Test
+ public void ecHsmHashCode() throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+ JsonWebKey keyNoT = mapper.readValue(keyWithoutT, JsonWebKey.class);
+ JsonWebKey keyT = mapper.readValue(keyWithT, JsonWebKey.class);
+
+ Assert.assertNotEquals(keyT.hashCode(), keyNoT.hashCode());
+
+ // Compare hash codes for unequal JWK that would not map to the same hash
+ Assert.assertNotEquals(keyT.hashCode(), new JsonWebKey().withKid(keyT.kid()).withT(keyT.t()).hashCode());
+ Assert.assertNotEquals(keyT.hashCode(), new JsonWebKey().withKid(keyT.kid()).withKty(keyT.kty()).hashCode());
+ Assert.assertNotEquals(keyNoT.hashCode(), new JsonWebKey().hashCode());
+
+ // Compare hash codes for unequal JWK that would map to the same hash
+ Assert.assertEquals(keyT.hashCode(),
+ new JsonWebKey().withKid(keyT.kid()).withKty(keyT.kty()).withT(keyT.t()).hashCode());
+ Assert.assertEquals(keyNoT.hashCode(), new JsonWebKey().withKid(keyT.kid()).hashCode());
+ }
+}
diff --git a/azure-keyvault-webkey/src/test/java/com/microsoft/azure/keyvault/webkey/test/EcValidationTests.java b/azure-keyvault-webkey/src/test/java/com/microsoft/azure/keyvault/webkey/test/EcValidationTests.java
new file mode 100644
index 0000000..35a9c28
--- /dev/null
+++ b/azure-keyvault-webkey/src/test/java/com/microsoft/azure/keyvault/webkey/test/EcValidationTests.java
@@ -0,0 +1,114 @@
+package com.microsoft.azure.keyvault.webkey.test;
+
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.util.Map;
+import java.util.Random;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableMap;
+import com.microsoft.azure.keyvault.models.JsonWebKeyCurveName;
+import com.microsoft.azure.keyvault.webkey.JsonWebKey;
+
+public class EcValidationTests {
+
+ @Test
+ public void ecPublicKeyValidation() throws Exception {
+
+ for (String keyStr : keys.values()) {
+ ObjectMapper mapper = new ObjectMapper();
+ JsonWebKey key = mapper.readValue(keyStr, JsonWebKey.class);
+ Assert.assertTrue(key.hasPrivateKey());
+ Assert.assertTrue(key.isValid());
+
+ KeyPair keyPair = key.toEC();
+ validateEcKey(keyPair, key);
+ Assert.assertNull(keyPair.getPrivate());
+
+ // Compare equal JSON web keys
+ JsonWebKey sameKey = mapper.readValue(keyStr, JsonWebKey.class);
+ Assert.assertEquals(key, key);
+ Assert.assertEquals(key, sameKey);
+ Assert.assertEquals(key.hashCode(), sameKey.hashCode());
+ }
+ }
+
+ @Test
+ public void ecPrivateKeyValidation() throws Exception {
+ for (String keyStr : keys.values()) {
+ ObjectMapper mapper = new ObjectMapper();
+ JsonWebKey key = mapper.readValue(keyStr, JsonWebKey.class);
+ KeyPair keyPairWithPrivate = key.toEC(true);
+ validateEcKey(keyPairWithPrivate, key);
+ signVerify(keyPairWithPrivate.getPublic(), keyPairWithPrivate.getPrivate(), key.crv());
+ }
+ }
+
+ private static void validateEcKey(KeyPair keyPair, JsonWebKey key) throws Exception {
+ JsonWebKey jsonWebKey = JsonWebKey.fromEC(keyPair, new BouncyCastleProvider());
+ boolean includePrivateKey = keyPair.getPrivate() != null;
+ KeyPair keyPair2 = jsonWebKey.toEC(includePrivateKey);
+
+ Assert.assertTrue(includePrivateKey == jsonWebKey.hasPrivateKey());
+
+ PublicKey publicKey = keyPair2.getPublic();
+ PrivateKey privateKey = keyPair2.getPrivate();
+
+ if(includePrivateKey) {
+
+ // set the missing properties to compare the keys
+ jsonWebKey.withKid(new String(key.kid()));
+ Assert.assertNotNull(privateKey);
+ Assert.assertEquals(jsonWebKey, key);
+ Assert.assertEquals(key.hashCode(), jsonWebKey.hashCode());
+ signVerify(publicKey, privateKey, jsonWebKey.crv());
+ }
+ }
+
+ private static void signVerify(PublicKey publicKey, PrivateKey privateKey, JsonWebKeyCurveName curve) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
+ Signature signature = Signature.getInstance(CURVE_TO_SIGNATURE.get(curve), new BouncyCastleProvider());
+ signature.initSign(privateKey);
+ MessageDigest digest = MessageDigest.getInstance(algorithm.get(curve));
+ byte[] plaintext = new byte[10];
+ new Random().nextBytes(plaintext);
+ byte[] hash = digest.digest(plaintext);
+ signature.update(hash);
+ byte[] signed_hash = signature.sign();
+
+ signature.initVerify(publicKey);
+ signature.update(hash);
+ Assert.assertTrue(signature.verify(signed_hash));
+
+ }
+
+ static Map algorithm = ImmutableMap.builder()
+ .put(JsonWebKeyCurveName.P_256, "SHA-256")
+ .put(JsonWebKeyCurveName.P_384, "SHA-384")
+ .put(JsonWebKeyCurveName.P_521, "SHA-512")
+ .put(JsonWebKeyCurveName.SECP256K1, "SHA-256")
+ .build();
+
+ Map keys = ImmutableMap.builder()
+ .put(256, "{\"kid\":\"key_id\",\"kty\":\"EC\",\"key_ops\":null,\"n\":null,\"e\":null,\"d\":\"AM_iqldq9VSqlf9v3w7lren4pJvZTG81v6_V5ZBLP7ZI\",\"dp\":null,\"dq\":null,\"qi\":null,\"p\":null,\"q\":null,\"k\":null,\"key_hsm\":null,\"crv\":\"P-256\",\"x\":\"KyjF795jLyVIgswKSQInEGYHNBKSKyPgNojEgYlldMI\",\"y\":\"AIl_ca1ZIKbJ5YGdgGr_7HySldI2aWeBaOImZEYIMpVe\"}")
+ .put(384, "{\"kid\":\"key_id\",\"kty\":\"EC\",\"key_ops\":null,\"n\":null,\"e\":null,\"d\":\"AJEYT00mAfa-_uJ8S9ob0-9uZbPEr56CFebUQW9O-jZQBrtrMSPeqVbjJvTVlzOwbg\",\"dp\":null,\"dq\":null,\"qi\":null,\"p\":null,\"q\":null,\"k\":null,\"key_hsm\":null,\"crv\":\"P-384\",\"x\":\"AKOdkhxTtVkLtaslZIOPQGnsdKRT2xo3Ynk-bnAVvTCf3iGrTpRiMxUmyq_tvzBLEg\",\"y\":\"QoHux2O2XGMh8w7a5sWwskAyCR0g3Lj7kPGuvnDq_bQ_-_VoTvsGMAe9MFexv68I\"}")
+ .put(521, "{\"kid\":\"key_id\",\"kty\":\"EC\",\"key_ops\":null,\"n\":null,\"e\":null,\"d\":\"AVW7TFJVOJ8jY5PqK0nnKyVYQwhkBEGKt0nhSZTS5io7U32dR7xZle77Gq6SpjrdFVa32jvGWgchlSguV3WKy3sj\",\"dp\":null,\"dq\":null,\"qi\":null,\"p\":null,\"q\":null,\"k\":null,\"key_hsm\":null,\"crv\":\"P-521\",\"x\":\"AIDmImOrJNKOjOGp7wD8Dzi_uz-00E7cs8iN5SwBkzBXktyRrLDFS_SMwVdnIWpLcdJQn5sTGDS121DhjQA2i2dO\",\"y\":\"AWRoeIfIoRoEx8V9ijjwaco3V6vUPUYvKMKxtCPvm8iwhB7pZAI7-mODSfkb3rZo3gxuWoM3G7L66FttUlKSLK4w\"}")
+ .put(265, "{\"kid\":\"key_id\",\"kty\":\"EC\",\"key_ops\":null,\"n\":null,\"e\":null,\"d\":\"YKv22AkpwBpKUcDodNhKhvI-bRpiWqoN8l0kNCo-Mds\",\"dp\":null,\"dq\":null,\"qi\":null,\"p\":null,\"q\":null,\"k\":null,\"key_hsm\":null,\"crv\":\"SECP256K1\",\"x\":\"Yw9Sln8gYf_oiFY1anQm0V_WwsRaCIcEccfbhu5hSJo\",\"y\":\"AJq3JT2YldszaohHaS7LkngPWS9y0yAn7HhHb5p0IUDS\"}")
+ .build();
+
+ public static final Map CURVE_TO_SIGNATURE = ImmutableMap.builder()
+ .put(JsonWebKeyCurveName.P_256, "SHA256withECDSA")
+ .put(JsonWebKeyCurveName.P_384, "SHA384withECDSA")
+ .put(JsonWebKeyCurveName.P_521, "SHA512withECDSA")
+ .put(JsonWebKeyCurveName.SECP256K1, "NONEwithECDSA")
+ .build();
+}
diff --git a/azure-keyvault/pom.xml b/azure-keyvault/pom.xml
index 992ea62..4bb6bb7 100644
--- a/azure-keyvault/pom.xml
+++ b/azure-keyvault/pom.xml
@@ -6,7 +6,7 @@
com.microsoft.azure
azure-keyvault-parent
- 1.1-alpha-2
+ 1.1-beta-1
../pom.xml
diff --git a/pom.xml b/pom.xml
index 663fd97..2727959 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
4.0.0
com.microsoft.azure
- 1.1-alpha-2
+ 1.1-beta-1
azure-keyvault-parent
pom
@@ -109,7 +109,7 @@
com.microsoft.azure
azure-client-runtime
- 1.0.0
+ 1.3.0
com.google.guava
@@ -365,8 +365,8 @@
./azure-keyvault
./azure-keyvault-core
+ ./azure-keyvault-webkey
./azure-keyvault-cryptography
./azure-keyvault-extensions
- ./azure-keyvault-webkey