You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by se...@apache.org on 2014/12/30 15:56:54 UTC
cxf git commit: Prototyping the code for ECDH,
the last unsupported JWE algorithm
Repository: cxf
Updated Branches:
refs/heads/master 5ec2f2fa4 -> a3bf2a80b
Prototyping the code for ECDH, the last unsupported JWE algorithm
Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/a3bf2a80
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/a3bf2a80
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/a3bf2a80
Branch: refs/heads/master
Commit: a3bf2a80bafa95ec2ccdd2b28ead26c13866acd7
Parents: 5ec2f2f
Author: Sergey Beryozkin <sb...@talend.com>
Authored: Tue Dec 30 14:56:30 2014 +0000
Committer: Sergey Beryozkin <sb...@talend.com>
Committed: Tue Dec 30 14:56:30 2014 +0000
----------------------------------------------------------------------
.../cxf/common/util/crypto/CryptoUtils.java | 58 +++++----
.../cxf/rs/security/jose/JoseConstants.java | 3 +
.../cxf/rs/security/jose/JoseHeaders.java | 11 +-
.../cxf/rs/security/jose/jwk/JwkUtils.java | 18 +++
.../jose/jwe/JweCompactReaderWriterTest.java | 129 +++++++++++++++++++
5 files changed, 195 insertions(+), 24 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cxf/blob/a3bf2a80/core/src/main/java/org/apache/cxf/common/util/crypto/CryptoUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/cxf/common/util/crypto/CryptoUtils.java b/core/src/main/java/org/apache/cxf/common/util/crypto/CryptoUtils.java
index 2408a0b..1b732e5 100644
--- a/core/src/main/java/org/apache/cxf/common/util/crypto/CryptoUtils.java
+++ b/core/src/main/java/org/apache/cxf/common/util/crypto/CryptoUtils.java
@@ -115,8 +115,8 @@ public final class CryptoUtils {
public static RSAPublicKey getRSAPublicKey(KeyFactory factory,
byte[] modulusBytes,
byte[] publicExponentBytes) {
- BigInteger modulus = new BigInteger(1, modulusBytes);
- BigInteger publicExponent = new BigInteger(1, publicExponentBytes);
+ BigInteger modulus = toBigInteger(modulusBytes);
+ BigInteger publicExponent = toBigInteger(publicExponentBytes);
try {
return (RSAPublicKey)factory.generatePublic(
new RSAPublicKeySpec(modulus, publicExponent));
@@ -137,8 +137,8 @@ public final class CryptoUtils {
public static RSAPrivateKey getRSAPrivateKey(byte[] modulusBytes,
byte[] privateExponentBytes) {
- BigInteger modulus = new BigInteger(1, modulusBytes);
- BigInteger privateExponent = new BigInteger(1, privateExponentBytes);
+ BigInteger modulus = toBigInteger(modulusBytes);
+ BigInteger privateExponent = toBigInteger(privateExponentBytes);
try {
KeyFactory factory = KeyFactory.getInstance("RSA");
return (RSAPrivateKey)factory.generatePrivate(
@@ -180,14 +180,14 @@ public final class CryptoUtils {
byte[] primeExpQBytes,
byte[] crtCoefficientBytes) {
//CHECKSTYLE:ON
- BigInteger modulus = new BigInteger(1, modulusBytes);
- BigInteger publicExponent = new BigInteger(1, publicExponentBytes);
- BigInteger privateExponent = new BigInteger(1, privateExponentBytes);
- BigInteger primeP = new BigInteger(1, primePBytes);
- BigInteger primeQ = new BigInteger(1, primeQBytes);
- BigInteger primeExpP = new BigInteger(1, primeExpPBytes);
- BigInteger primeExpQ = new BigInteger(1, primeExpQBytes);
- BigInteger crtCoefficient = new BigInteger(1, crtCoefficientBytes);
+ BigInteger modulus = toBigInteger(modulusBytes);
+ BigInteger publicExponent = toBigInteger(publicExponentBytes);
+ BigInteger privateExponent = toBigInteger(privateExponentBytes);
+ BigInteger primeP = toBigInteger(primePBytes);
+ BigInteger primeQ = toBigInteger(primeQBytes);
+ BigInteger primeExpP = toBigInteger(primeExpPBytes);
+ BigInteger primeExpQ = toBigInteger(primeExpQBytes);
+ BigInteger crtCoefficient = toBigInteger(crtCoefficientBytes);
try {
KeyFactory factory = KeyFactory.getInstance("RSA");
return (RSAPrivateKey)factory.generatePrivate(
@@ -215,7 +215,7 @@ public final class CryptoUtils {
try {
ECParameterSpec params = getECParameterSpec(curve, true);
ECPrivateKeySpec keySpec = new ECPrivateKeySpec(
- new BigInteger(1, privateKey), params);
+ toBigInteger(privateKey), params);
KeyFactory kf = KeyFactory.getInstance("EC");
return (ECPrivateKey) kf.generatePrivate(keySpec);
@@ -225,16 +225,24 @@ public final class CryptoUtils {
}
private static ECParameterSpec getECParameterSpec(String curve, boolean isPrivate)
throws Exception {
- KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
- ECGenParameterSpec kpgparams = new ECGenParameterSpec("sec"
- + curve.toLowerCase().replace("-", "")
- + "r1");
- kpg.initialize(kpgparams);
- KeyPair pair = kpg.generateKeyPair();
+ KeyPair pair = generateECKeyPair(curve);
return isPrivate ? ((ECPublicKey) pair.getPublic()).getParams()
: ((ECPrivateKey) pair.getPrivate()).getParams();
}
+ public static KeyPair generateECKeyPair(String curve) {
+ try {
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
+ ECGenParameterSpec kpgparams = new ECGenParameterSpec("sec"
+ + curve.toLowerCase().replace("-", "")
+ + "r1");
+ kpg.initialize(kpgparams);
+ return kpg.generateKeyPair();
+ } catch (Exception ex) {
+ throw new SecurityException(ex);
+ }
+ }
+
public static ECPublicKey getECPublicKey(String curve, String encodedXPoint, String encodedYPoint) {
try {
return getECPublicKey(curve,
@@ -248,8 +256,8 @@ public final class CryptoUtils {
try {
ECParameterSpec params = getECParameterSpec(curve, false);
- ECPoint ecPoint = new ECPoint(new BigInteger(1, xPoint),
- new BigInteger(1, yPoint));
+ ECPoint ecPoint = new ECPoint(toBigInteger(xPoint),
+ toBigInteger(yPoint));
ECPublicKeySpec keySpec = new ECPublicKeySpec(ecPoint, params);
KeyFactory kf = KeyFactory.getInstance("EC");
return (ECPublicKey) kf.generatePublic(keySpec);
@@ -258,7 +266,13 @@ public final class CryptoUtils {
throw new SecurityException(ex);
}
}
-
+ private static BigInteger toBigInteger(byte[] bytes) {
+ if (bytes[0] == -128) {
+ return new BigInteger(bytes);
+ } else {
+ return new BigInteger(1, bytes);
+ }
+ }
public static AlgorithmParameterSpec getContentEncryptionCipherSpec(int authTagLength, byte[] iv) {
if (authTagLength > 0) {
return CryptoUtils.getGCMParameterSpec(authTagLength, iv);
http://git-wip-us.apache.org/repos/asf/cxf/blob/a3bf2a80/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseConstants.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseConstants.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseConstants.java
index cd719d4..39b86c7 100644
--- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseConstants.java
+++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseConstants.java
@@ -68,6 +68,9 @@ public final class JoseConstants {
public static final String A128GCMKW_ALGO = "A128GCMKW";
public static final String A192GCMKW_ALGO = "A192GCMKW";
public static final String A256GCMKW_ALGO = "A256GCMKW";
+
+ public static final String ECDH_ES_DIRECT_ALGO = "ECDH-ES";
+
public static final String PBES2_HS256_A128KW_ALGO = "PBES2-HS256+A128KW";
public static final String PBES2_HS384_A192KW_ALGO = "PBES2-HS384+A192KW";
public static final String PBES2_HS512_A256KW_ALGO = "PBES2-HS512+A256KW";
http://git-wip-us.apache.org/repos/asf/cxf/blob/a3bf2a80/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeaders.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeaders.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeaders.java
index ac186d2..819e408 100644
--- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeaders.java
+++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeaders.java
@@ -111,11 +111,18 @@ public class JoseHeaders extends JsonMapObject {
}
public void setJsonWebKey(JsonWebKey key) {
- setHeader(JoseConstants.HEADER_JSON_WEB_KEY, key);
+ setJsonWebKey(JoseConstants.HEADER_JSON_WEB_KEY, key);
+ }
+
+ public void setJsonWebKey(String headerName, JsonWebKey key) {
+ setHeader(headerName, key);
}
public JsonWebKey getJsonWebKey() {
- Object jsonWebKey = getHeader(JoseConstants.HEADER_JSON_WEB_KEY);
+ return getJsonWebKey(JoseConstants.HEADER_JSON_WEB_KEY);
+ }
+ public JsonWebKey getJsonWebKey(String headerName) {
+ Object jsonWebKey = getHeader(headerName);
if (jsonWebKey == null || jsonWebKey instanceof JsonWebKey) {
return (JsonWebKey)jsonWebKey;
}
http://git-wip-us.apache.org/repos/asf/cxf/blob/a3bf2a80/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java
index 8d1f107..2f6cf77 100644
--- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java
+++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java
@@ -319,6 +319,24 @@ public final class JwkUtils {
List<String> base64EncodedChain = jwk.getX509Chain();
return KeyManagementUtils.toX509CertificateChain(base64EncodedChain);
}
+ public static JsonWebKey fromECPublicKey(ECPublicKey pk, String curve) {
+ JsonWebKey jwk = new JsonWebKey();
+ jwk.setKeyType(JsonWebKey.KEY_TYPE_ELLIPTIC);
+ jwk.setProperty(JsonWebKey.EC_CURVE, curve);
+ jwk.setProperty(JsonWebKey.EC_X_COORDINATE,
+ Base64UrlUtility.encode(pk.getW().getAffineX().toByteArray()));
+ jwk.setProperty(JsonWebKey.EC_Y_COORDINATE,
+ Base64UrlUtility.encode(pk.getW().getAffineY().toByteArray()));
+ return jwk;
+ }
+ public static JsonWebKey fromECPrivateKey(ECPrivateKey pk, String curve) {
+ JsonWebKey jwk = new JsonWebKey();
+ jwk.setKeyType(JsonWebKey.KEY_TYPE_ELLIPTIC);
+ jwk.setProperty(JsonWebKey.EC_CURVE, curve);
+ jwk.setProperty(JsonWebKey.EC_PRIVATE_KEY,
+ Base64UrlUtility.encode(pk.getS().toByteArray()));
+ return jwk;
+ }
public static JsonWebKey fromRSAPublicKey(RSAPublicKey pk, String algo) {
JsonWebKey jwk = prepareRSAJwk(pk.getModulus(), algo);
String encodedPublicExponent = Base64UrlUtility.encode(pk.getPublicExponent().toByteArray());
http://git-wip-us.apache.org/repos/asf/cxf/blob/a3bf2a80/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java
index b3be0a6..9204f3a 100644
--- a/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java
+++ b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java
@@ -18,17 +18,28 @@
*/
package org.apache.cxf.rs.security.jose.jwe;
+import java.nio.ByteBuffer;
import java.security.Security;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
+import java.util.Arrays;
import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import org.apache.cxf.common.util.Base64UrlUtility;
+import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.common.util.crypto.CryptoUtils;
+import org.apache.cxf.common.util.crypto.MessageDigestUtils;
import org.apache.cxf.rs.security.jose.JoseConstants;
+import org.apache.cxf.rs.security.jose.JoseHeaders;
+import org.apache.cxf.rs.security.jose.JoseUtils;
import org.apache.cxf.rs.security.jose.jwa.Algorithm;
+import org.apache.cxf.rs.security.jose.jwk.JsonWebKey;
+import org.apache.cxf.rs.security.jose.jwk.JwkUtils;
import org.apache.cxf.rs.security.jose.jws.JwsCompactReaderWriterTest;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
@@ -116,6 +127,124 @@ public class JweCompactReaderWriterTest extends Assert {
assertEquals(specPlainText, decryptedText);
}
@Test
+ public void testECDHESDirectKeyEncryption() throws Exception {
+ ECPrivateKey alicePrivateKey =
+ CryptoUtils.getECPrivateKey(JsonWebKey.EC_CURVE_P256,
+ "0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo");
+ ECPublicKey alicePublicKey =
+ CryptoUtils.getECPublicKey(JsonWebKey.EC_CURVE_P256,
+ "gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0",
+ "SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps");
+
+ ECPublicKey bobPublicKey =
+ CryptoUtils.getECPublicKey(JsonWebKey.EC_CURVE_P256,
+ "weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ",
+ "e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck");
+
+ byte[] keyZ = generateKeyZ(alicePrivateKey, bobPublicKey);
+ byte[] apuBytes = StringUtils.toBytesUTF8("Alice");
+ byte[] apvBytes = StringUtils.toBytesUTF8("Bob");
+
+ byte[] derivedKey = calculateDerivedKey(keyZ,
+ Algorithm.A128GCM.getJwtName(),
+ apuBytes,
+ apvBytes,
+ Algorithm.A128GCM.getKeySizeBits());
+ assertEquals("VqqN6vgjbSBcIijNcacQGg", Base64UrlUtility.encode(derivedKey));
+
+ JweHeaders headers = new JweHeaders();
+ headers.setAlgorithm(JoseConstants.ECDH_ES_DIRECT_ALGO);
+ headers.setContentEncryptionAlgorithm(Algorithm.A128GCM.getJwtName());
+ headers.setHeader("apu", Base64UrlUtility.encode(apuBytes));
+ headers.setHeader("apv", Base64UrlUtility.encode(apvBytes));
+ headers.setJsonWebKey("epv", JwkUtils.fromECPublicKey(alicePublicKey, JsonWebKey.EC_CURVE_P256));
+
+ ECPrivateKey bobPrivateKey =
+ CryptoUtils.getECPrivateKey(JsonWebKey.EC_CURVE_P256,
+ "VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw");
+ byte[] derivedKey2 = calculateDerivedKeyFromHeaders(bobPrivateKey,
+ headers,
+ headers.getContentEncryptionAlgorithm(),
+ Algorithm.A128GCM.getKeySizeBits());
+ assertTrue(Arrays.equals(derivedKey2, derivedKey));
+ }
+ private static byte[] calculateDerivedKeyFromHeaders(ECPrivateKey privateKey,
+ JoseHeaders headers,
+ String algoName,
+ int algoKeyLen) {
+ JsonWebKey ephemeralJwk = headers.getJsonWebKey("epv");
+ byte[] keyZ = generateKeyZ(privateKey, JwkUtils.toECPublicKey(ephemeralJwk));
+ String apuHeader = (String)headers.getHeader("apu");
+ byte[] apuBytes = apuHeader == null ? null : JoseUtils.decode(apuHeader);
+ String apvHeader = (String)headers.getHeader("apv");
+ byte[] apvBytes = apvHeader == null ? null : JoseUtils.decode(apvHeader);
+
+ return calculateDerivedKey(keyZ,
+ algoName,
+ apuBytes,
+ apvBytes,
+ algoKeyLen);
+ }
+
+ private static byte[] calculateDerivedKey(byte[] keyZ,
+ String algoName,
+ byte[] apuBytes,
+ byte[] apvBytes,
+ int algoKeyBitLen) {
+ final byte[] emptyPartyInfo = new byte[4];
+
+ if (apuBytes != null && apvBytes != null && Arrays.equals(apuBytes, apvBytes)) {
+ throw new SecurityException();
+ }
+ byte[] algorithmId = concatenateDatalenAndData(StringUtils.toBytesASCII(algoName));
+ byte[] partyUInfo = apuBytes == null ? emptyPartyInfo : concatenateDatalenAndData(apuBytes);
+ byte[] partyVInfo = apvBytes == null ? emptyPartyInfo : concatenateDatalenAndData(apvBytes);
+ byte[] suppPubInfo = datalenToBytes(algoKeyBitLen);
+
+ byte[] otherInfo = new byte[algorithmId.length
+ + partyUInfo.length
+ + partyVInfo.length
+ + suppPubInfo.length];
+ System.arraycopy(algorithmId, 0, otherInfo, 0, algorithmId.length);
+ System.arraycopy(partyUInfo, 0, otherInfo, algorithmId.length, partyUInfo.length);
+ System.arraycopy(partyVInfo, 0, otherInfo, algorithmId.length + partyUInfo.length, partyVInfo.length);
+ System.arraycopy(suppPubInfo, 0, otherInfo, algorithmId.length + partyUInfo.length + partyVInfo.length,
+ suppPubInfo.length);
+
+
+ byte[] concatKDF = new byte[36 + otherInfo.length];
+ concatKDF[3] = 1;
+ System.arraycopy(keyZ, 0, concatKDF, 4, keyZ.length);
+ System.arraycopy(otherInfo, 0, concatKDF, 36, otherInfo.length);
+ try {
+ byte[] round1Hash = MessageDigestUtils.createDigest(concatKDF, MessageDigestUtils.ALGO_SHA_256);
+ return Arrays.copyOf(round1Hash, algoKeyBitLen / 8);
+ } catch (Exception ex) {
+ throw new SecurityException(ex);
+ }
+ }
+ private static byte[] generateKeyZ(ECPrivateKey privateKey, ECPublicKey publicKey) {
+ try {
+ KeyAgreement ka = KeyAgreement.getInstance("ECDH");
+ ka.init(privateKey);
+ ka.doPhase(publicKey, true);
+ return ka.generateSecret();
+ } catch (Exception ex) {
+ throw new SecurityException(ex);
+ }
+ }
+ private static byte[] concatenateDatalenAndData(byte[] bytesASCII) {
+ final byte[] datalen = datalenToBytes(bytesASCII.length);
+ byte[] all = new byte[4 + bytesASCII.length];
+ System.arraycopy(datalen, 0, all, 0, 4);
+ System.arraycopy(bytesASCII, 0, all, 4, bytesASCII.length);
+ return all;
+ }
+ private static byte[] datalenToBytes(int len) {
+ ByteBuffer buf = ByteBuffer.allocate(4);
+ return buf.putInt(len).array();
+ }
+ @Test
public void testEncryptDecryptRSA15WrapA128CBCHS256() throws Exception {
final String specPlainText = "Live long and prosper.";
JweHeaders headers = new JweHeaders();