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();