You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by co...@apache.org on 2015/10/02 19:22:21 UTC

[4/4] directory-kerby git commit: Adding ability to encrypt and sign using non-RSA keys

Adding ability to encrypt and sign using non-RSA keys


Project: http://git-wip-us.apache.org/repos/asf/directory-kerby/repo
Commit: http://git-wip-us.apache.org/repos/asf/directory-kerby/commit/b283decc
Tree: http://git-wip-us.apache.org/repos/asf/directory-kerby/tree/b283decc
Diff: http://git-wip-us.apache.org/repos/asf/directory-kerby/diff/b283decc

Branch: refs/heads/master
Commit: b283deccef2d44b6695e48fafae4192a2a2ad41d
Parents: eff5d0c
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Wed Sep 30 12:21:37 2015 +0200
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Wed Sep 30 14:54:31 2015 +0200

----------------------------------------------------------------------
 .../provider/token/JwtTokenDecoder.java         |  69 ++++++++--
 .../provider/token/JwtTokenEncoder.java         | 125 ++++++++++++++++---
 .../kerberos/provider/token/TokenTest.java      |  95 +++++++++++++-
 3 files changed, 260 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b283decc/kerby-provider/token-provider/src/main/java/org/apache/kerby/kerberos/provider/token/JwtTokenDecoder.java
----------------------------------------------------------------------
diff --git a/kerby-provider/token-provider/src/main/java/org/apache/kerby/kerberos/provider/token/JwtTokenDecoder.java b/kerby-provider/token-provider/src/main/java/org/apache/kerby/kerberos/provider/token/JwtTokenDecoder.java
index ff9469d..4da2b93 100644
--- a/kerby-provider/token-provider/src/main/java/org/apache/kerby/kerberos/provider/token/JwtTokenDecoder.java
+++ b/kerby-provider/token-provider/src/main/java/org/apache/kerby/kerberos/provider/token/JwtTokenDecoder.java
@@ -20,7 +20,11 @@
 package org.apache.kerby.kerberos.provider.token;
 
 import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWEDecrypter;
 import com.nimbusds.jose.JWSVerifier;
+import com.nimbusds.jose.crypto.DirectDecrypter;
+import com.nimbusds.jose.crypto.ECDSAVerifier;
+import com.nimbusds.jose.crypto.MACVerifier;
 import com.nimbusds.jose.crypto.RSADecrypter;
 import com.nimbusds.jose.crypto.RSASSAVerifier;
 import com.nimbusds.jwt.EncryptedJWT;
@@ -28,11 +32,16 @@ import com.nimbusds.jwt.JWT;
 import com.nimbusds.jwt.JWTParser;
 import com.nimbusds.jwt.PlainJWT;
 import com.nimbusds.jwt.SignedJWT;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
 import org.apache.kerby.kerberos.kerb.provider.TokenDecoder;
 import org.apache.kerby.kerberos.kerb.spec.base.AuthToken;
 
 import java.io.IOException;
 import java.nio.charset.Charset;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.ECPublicKey;
 import java.security.interfaces.RSAPrivateKey;
 import java.security.interfaces.RSAPublicKey;
 import java.text.ParseException;
@@ -43,8 +52,8 @@ import java.util.List;
  * JWT token decoder, implemented using Nimbus JWT library.
  */
 public class JwtTokenDecoder implements TokenDecoder {
-    private RSAPrivateKey decryptionKey;
-    private RSAPublicKey verifyKey;
+    private Object decryptionKey;
+    private Object verifyKey;
     private List<String> audiences = null;
 
     /**
@@ -133,20 +142,39 @@ public class JwtTokenDecoder implements TokenDecoder {
      * @param encryptedJWT an encrypted JWT
      */
     public void decryptEncryptedJWT(EncryptedJWT encryptedJWT) throws IOException {
-        RSADecrypter decrypter = new RSADecrypter(decryptionKey);
         try {
+            JWEDecrypter decrypter = getDecrypter();
             encryptedJWT.decrypt(decrypter);
-        } catch (JOSEException e) {
+        } catch (JOSEException | KrbException e) {
             throw new IOException("Failed to decrypt the encrypted JWT", e);
         }
     }
+    
+    private JWEDecrypter getDecrypter() throws JOSEException, KrbException {
+        if (decryptionKey instanceof RSAPrivateKey) {
+            return new RSADecrypter((RSAPrivateKey) decryptionKey);
+        } else if (decryptionKey instanceof byte[]) {
+            return new DirectDecrypter((byte[]) decryptionKey);
+        }
+        
+        throw new KrbException("An unknown decryption key was specified");
+    }
 
     /**
      * Set the decryption key
      *
      * @param key a private key
      */
-    public void setDecryptionKey(RSAPrivateKey key) {
+    public void setDecryptionKey(PrivateKey key) {
+        decryptionKey = key;
+    }
+    
+    /**
+     * Set the decryption key
+     *
+     * @param key a secret key
+     */
+    public void setDecryptionKey(byte[] key) {
         decryptionKey = key;
     }
 
@@ -158,20 +186,43 @@ public class JwtTokenDecoder implements TokenDecoder {
      * @return whether verify success
      */
     public boolean verifySignedJWT(SignedJWT signedJWT) throws IOException {
-        JWSVerifier verifier = new RSASSAVerifier(verifyKey);
         try {
+            JWSVerifier verifier = getVerifier();
             return signedJWT.verify(verifier);
-        } catch (JOSEException e) {
+        } catch (JOSEException | KrbException e) {
             throw new IOException("Failed to verify the signed JWT", e);
         }
     }
+    
+    private JWSVerifier getVerifier() throws JOSEException, KrbException {
+        if (verifyKey instanceof RSAPublicKey) {
+            return new RSASSAVerifier((RSAPublicKey) verifyKey);
+        } else if (verifyKey instanceof ECPublicKey) {
+            ECPublicKey ecPublicKey = (ECPublicKey) verifyKey;
+            return new ECDSAVerifier(ecPublicKey.getW().getAffineX(),
+                                     ecPublicKey.getW().getAffineY());
+        } else if (verifyKey instanceof byte[]) {
+            return new MACVerifier((byte[]) verifyKey);
+        }
+        
+        throw new KrbException("An unknown verify key was specified");
+    }
 
     /**
      * set the verify key
      *
      * @param key a public key
      */
-    public void setVerifyKey(RSAPublicKey key) {
+    public void setVerifyKey(PublicKey key) {
+        verifyKey = key;
+    }
+    
+    /**
+     * set the verify key
+     *
+     * @param key a byte[] key
+     */
+    public void setVerifyKey(byte[] key) {
         verifyKey = key;
     }
 
@@ -187,7 +238,7 @@ public class JwtTokenDecoder implements TokenDecoder {
     private boolean verifyToken(JWT jwtToken) throws IOException {
         boolean audValid = verifyAudiences(jwtToken);
         boolean expValid = verifyExpiration(jwtToken);
-        return  audValid && expValid;
+        return audValid && expValid;
     }
 
     private boolean verifyAudiences(JWT jwtToken) throws IOException {

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b283decc/kerby-provider/token-provider/src/main/java/org/apache/kerby/kerberos/provider/token/JwtTokenEncoder.java
----------------------------------------------------------------------
diff --git a/kerby-provider/token-provider/src/main/java/org/apache/kerby/kerberos/provider/token/JwtTokenEncoder.java b/kerby-provider/token-provider/src/main/java/org/apache/kerby/kerberos/provider/token/JwtTokenEncoder.java
index 707b231..44ef6e5 100644
--- a/kerby-provider/token-provider/src/main/java/org/apache/kerby/kerberos/provider/token/JwtTokenEncoder.java
+++ b/kerby-provider/token-provider/src/main/java/org/apache/kerby/kerberos/provider/token/JwtTokenEncoder.java
@@ -19,38 +19,47 @@
  */
 package org.apache.kerby.kerberos.provider.token;
 
+import java.nio.charset.Charset;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.text.ParseException;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.provider.TokenEncoder;
+import org.apache.kerby.kerberos.kerb.spec.base.AuthToken;
+
 import com.nimbusds.jose.EncryptionMethod;
 import com.nimbusds.jose.JOSEException;
 import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.JWEEncrypter;
 import com.nimbusds.jose.JWEHeader;
 import com.nimbusds.jose.JWEObject;
 import com.nimbusds.jose.JWSAlgorithm;
 import com.nimbusds.jose.JWSHeader;
 import com.nimbusds.jose.JWSSigner;
 import com.nimbusds.jose.Payload;
+import com.nimbusds.jose.crypto.DirectEncrypter;
+import com.nimbusds.jose.crypto.ECDSASigner;
+import com.nimbusds.jose.crypto.MACSigner;
 import com.nimbusds.jose.crypto.RSAEncrypter;
 import com.nimbusds.jose.crypto.RSASSASigner;
 import com.nimbusds.jwt.EncryptedJWT;
 import com.nimbusds.jwt.JWT;
 import com.nimbusds.jwt.SignedJWT;
-import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.provider.TokenEncoder;
-import org.apache.kerby.kerberos.kerb.spec.base.AuthToken;
-
-import java.nio.charset.Charset;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.text.ParseException;
 
 /**
  * JWT token encoder, implemented using Nimbus JWT library.
  */
 public class JwtTokenEncoder implements TokenEncoder {
-    private static JWEAlgorithm jweAlgorithm = JWEAlgorithm.RSA_OAEP;
-    private static EncryptionMethod encryptionMethod = EncryptionMethod.A128GCM;
-    private static JWSAlgorithm jwsAlgorithm = JWSAlgorithm.RS256;
-    private RSAPublicKey encryptionKey;
-    private RSAPrivateKey signKey;
+    private JWEAlgorithm jweAlgorithm = JWEAlgorithm.RSA_OAEP;
+    private EncryptionMethod encryptionMethod = EncryptionMethod.A128GCM;
+    private JWSAlgorithm jwsAlgorithm = JWSAlgorithm.RS256;
+
+    private Object encryptionKey;
+    private Object signKey;
 
     /**
      * {@inheritDoc}
@@ -76,7 +85,7 @@ public class JwtTokenEncoder implements TokenEncoder {
         String tokenStr = null;
         if (signKey != null) {
             // Create signer with the private key
-            JWSSigner signer = new RSASSASigner(signKey);
+            JWSSigner signer = createSigner();
             SignedJWT signedJWT = null;
             try {
                 signedJWT = new SignedJWT(new JWSHeader(jwsAlgorithm), jwt.getJWTClaimsSet());
@@ -95,7 +104,7 @@ public class JwtTokenEncoder implements TokenEncoder {
                         new JWEHeader.Builder(jweAlgorithm, encryptionMethod).contentType("JWT").build(),
                         new Payload(signedJWT));
                 try {
-                    jweObject.encrypt(new RSAEncrypter(encryptionKey));
+                    jweObject.encrypt(createEncryptor());
                 } catch (JOSEException e) {
                     throw new KrbException("Failed to encrypt the JWE object", e);
                 }
@@ -112,7 +121,7 @@ public class JwtTokenEncoder implements TokenEncoder {
                 throw new KrbException("Failed to get JWT claims set", e);
             }
             try {
-                encryptedJWT.encrypt(new RSAEncrypter(encryptionKey));
+                encryptedJWT.encrypt(createEncryptor());
             } catch (JOSEException e) {
                 throw new KrbException("Failed to encrypt the encrypted JWT", e);
             }
@@ -123,13 +132,60 @@ public class JwtTokenEncoder implements TokenEncoder {
         }
         return tokenStr;
     }
+    
+    private JWSSigner createSigner() throws KrbException {
+        // Create signer with the private key
+        if (RSASSASigner.SUPPORTED_ALGORITHMS.contains(jwsAlgorithm)) {
+            if (!(signKey instanceof RSAPrivateKey)) {
+                throw new KrbException("An RSAPrivateKey key must be specified for signature");
+            }
+            return new RSASSASigner((RSAPrivateKey) signKey);
+        } else if (ECDSASigner.SUPPORTED_ALGORITHMS.contains(jwsAlgorithm)) {
+            if (!(signKey instanceof ECPrivateKey)) {
+                throw new KrbException("A ECPrivateKey key must be specified for signature");
+            }
+            return new ECDSASigner(((ECPrivateKey) signKey).getS());
+        } else if (MACSigner.SUPPORTED_ALGORITHMS.contains(jwsAlgorithm)) {
+            if (!(signKey instanceof byte[])) {
+                throw new KrbException("A byte[] key must be specified for signature");
+            }
+            return new MACSigner((byte[]) signKey);
+        }
+
+        throw new KrbException("An unknown signature algorithm was specified");
+    }
+    
+    private JWEEncrypter createEncryptor() throws KrbException, JOSEException {
+        if (RSAEncrypter.SUPPORTED_ALGORITHMS.contains(jweAlgorithm)) {
+            if (!(encryptionKey instanceof RSAPublicKey)) {
+                throw new KrbException("An RSAPublicKey key must be specified for encryption");
+            }
+            return new RSAEncrypter((RSAPublicKey) encryptionKey);
+        } else if (DirectEncrypter.SUPPORTED_ALGORITHMS.contains(jweAlgorithm)) {
+            if (!(encryptionKey instanceof byte[])) {
+                throw new KrbException("A byte[] key must be specified for encryption");
+            }
+            return new DirectEncrypter((byte[]) encryptionKey);
+        }
+        
+        throw new KrbException("An unknown encryption algorithm was specified");
+    }
 
     /**
      * set the encryption key
      *
      * @param key a public key
      */
-    public void setEncryptionKey(RSAPublicKey key) {
+    public void setEncryptionKey(PublicKey key) {
+        encryptionKey = key;
+    }
+    
+    /**
+     * set the encryption key
+     *
+     * @param key a secret key
+     */
+    public void setEncryptionKey(byte[] key) {
         encryptionKey = key;
     }
 
@@ -138,7 +194,40 @@ public class JwtTokenEncoder implements TokenEncoder {
      *
      * @param key a private key
      */
-    public void setSignKey(RSAPrivateKey key) {
+    public void setSignKey(PrivateKey key) {
         signKey = key;
     }
+    
+    /**
+     * set the sign key
+     *
+     * @param key a secret key
+     */
+    public void setSignKey(byte[] key) {
+        signKey = key;
+    }
+    
+    public JWEAlgorithm getJweAlgorithm() {
+        return jweAlgorithm;
+    }
+
+    public void setJweAlgorithm(JWEAlgorithm jweAlgorithm) {
+        this.jweAlgorithm = jweAlgorithm;
+    }
+
+    public JWSAlgorithm getJwsAlgorithm() {
+        return jwsAlgorithm;
+    }
+
+    public void setJwsAlgorithm(JWSAlgorithm jwsAlgorithm) {
+        this.jwsAlgorithm = jwsAlgorithm;
+    }
+    
+    public EncryptionMethod getEncryptionMethod() {
+        return encryptionMethod;
+    }
+
+    public void setEncryptionMethod(EncryptionMethod encryptionMethod) {
+        this.encryptionMethod = encryptionMethod;
+    }
 }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b283decc/kerby-provider/token-provider/src/test/java/org/apache/kerby/kerberos/provider/token/TokenTest.java
----------------------------------------------------------------------
diff --git a/kerby-provider/token-provider/src/test/java/org/apache/kerby/kerberos/provider/token/TokenTest.java b/kerby-provider/token-provider/src/test/java/org/apache/kerby/kerberos/provider/token/TokenTest.java
index 6cb9a9c..0f15a50 100644
--- a/kerby-provider/token-provider/src/test/java/org/apache/kerby/kerberos/provider/token/TokenTest.java
+++ b/kerby-provider/token-provider/src/test/java/org/apache/kerby/kerberos/provider/token/TokenTest.java
@@ -27,6 +27,10 @@ import org.assertj.core.api.Assertions;
 import org.junit.Before;
 import org.junit.Test;
 
+import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.JWSAlgorithm;
+
+import java.io.IOException;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.security.NoSuchAlgorithmException;
@@ -36,6 +40,8 @@ import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
+import javax.crypto.KeyGenerator;
+
 public class TokenTest {
 
     static {
@@ -65,7 +71,7 @@ public class TokenTest {
         authToken.setAudiences(auds);
 
         // Set expiration in 60 minutes
-        final Date now =  new Date(new Date().getTime() / 1000 * 1000);
+        final Date now = new Date();
         Date exp = new Date(now.getTime() + 1000 * 60 * 60);
         authToken.setExpirationTime(exp);
 
@@ -121,6 +127,41 @@ public class TokenTest {
         Assertions.assertThat(token2.getSubject()).isEqualTo(SUBJECT);
         Assertions.assertThat(token2.getIssuer()).isEqualTo(ISSUER);
     }
+    
+    @Test
+    public void testTokenWithDirectEncryptedJWT() throws Exception {
+        TokenEncoder tokenEncoder = KrbRuntime.getTokenProvider().createTokenEncoder();
+        TokenDecoder tokenDecoder = KrbRuntime.getTokenProvider().createTokenDecoder();
+
+        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
+        keyGenerator.init(128);
+        byte[] secretKey = keyGenerator.generateKey().getEncoded();
+        
+        ((JwtTokenEncoder) tokenEncoder).setEncryptionKey(secretKey);
+        ((JwtTokenEncoder) tokenEncoder).setJweAlgorithm(JWEAlgorithm.DIR);
+        ((JwtTokenDecoder) tokenDecoder).setDecryptionKey(secretKey);
+        setAudience((JwtTokenDecoder) tokenDecoder, auds);
+
+        String tokenStr = tokenEncoder.encodeAsString(authToken);
+        Assertions.assertThat(tokenStr).isNotNull();
+
+        AuthToken token2 = tokenDecoder.decodeFromString(tokenStr);
+        Assertions.assertThat(token2.getSubject()).isEqualTo(SUBJECT);
+        Assertions.assertThat(token2.getIssuer()).isEqualTo(ISSUER);
+        
+        // Now try with a different secret key
+        secretKey = keyGenerator.generateKey().getEncoded();
+        ((JwtTokenDecoder) tokenDecoder).setDecryptionKey(secretKey);
+        
+        try {
+            tokenDecoder.decodeFromString(tokenStr);
+            Assertions.fail("Failure expected on a bad secret key");
+        } catch (IOException ex) {
+            String expectedError = "Failed to decrypt the encrypted JWT";
+            Assertions.assertThat(ex.getMessage().contains(expectedError));
+            // expected
+        }
+    }
 
     @Test
     public void testTokenWithSignedJWT() throws Exception {
@@ -137,9 +178,59 @@ public class TokenTest {
         Assertions.assertThat(token2.getSubject()).isEqualTo(SUBJECT);
         Assertions.assertThat(token2.getIssuer()).isEqualTo(ISSUER);
     }
+    
+    @Test
+    public void testTokenWithHMACSignedJWT() throws Exception {
+        TokenEncoder tokenEncoder = KrbRuntime.getTokenProvider().createTokenEncoder();
+        TokenDecoder tokenDecoder = KrbRuntime.getTokenProvider().createTokenDecoder();
+
+        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
+        keyGenerator.init(256);
+        byte[] secretKey = keyGenerator.generateKey().getEncoded();
+        
+        ((JwtTokenEncoder) tokenEncoder).setSignKey(secretKey);
+        ((JwtTokenEncoder) tokenEncoder).setJwsAlgorithm(JWSAlgorithm.HS256);
+        ((JwtTokenDecoder) tokenDecoder).setVerifyKey(secretKey);
+        setAudience((JwtTokenDecoder) tokenDecoder, auds);
 
+        String tokenStr = tokenEncoder.encodeAsString(authToken);
+        Assertions.assertThat(tokenStr).isNotNull();
+
+        AuthToken token2 = tokenDecoder.decodeFromString(tokenStr);
+        Assertions.assertThat(token2.getSubject()).isEqualTo(SUBJECT);
+        Assertions.assertThat(token2.getIssuer()).isEqualTo(ISSUER);
+        
+        // Now try with a different secret key
+        secretKey = keyGenerator.generateKey().getEncoded();
+        ((JwtTokenDecoder) tokenDecoder).setVerifyKey(secretKey);
+        
+       token2 = tokenDecoder.decodeFromString(tokenStr);
+       Assertions.assertThat(token2).isNull();
+    }
+
+    @Test
+    public void testTokenWithECDSASignedJWT() throws Exception {
+        TokenEncoder tokenEncoder = KrbRuntime.getTokenProvider().createTokenEncoder();
+        TokenDecoder tokenDecoder = KrbRuntime.getTokenProvider().createTokenDecoder();
+
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
+        KeyPair keyPair = kpg.generateKeyPair();
+        
+        ((JwtTokenEncoder) tokenEncoder).setSignKey(keyPair.getPrivate());
+        ((JwtTokenEncoder) tokenEncoder).setJwsAlgorithm(JWSAlgorithm.ES256);
+        ((JwtTokenDecoder) tokenDecoder).setVerifyKey(keyPair.getPublic());
+        setAudience((JwtTokenDecoder) tokenDecoder, auds);
+
+        String tokenStr = tokenEncoder.encodeAsString(authToken);
+        Assertions.assertThat(tokenStr).isNotNull();
+
+        AuthToken token2 = tokenDecoder.decodeFromString(tokenStr);
+        Assertions.assertThat(token2.getSubject()).isEqualTo(SUBJECT);
+        Assertions.assertThat(token2.getIssuer()).isEqualTo(ISSUER);
+    }
+    
     @Test
-    public void testTokenWithSingedAndEncryptedJWT() throws Exception {
+    public void testTokenWithSignedAndEncryptedJWT() throws Exception {
         TokenEncoder tokenEncoder = KrbRuntime.getTokenProvider().createTokenEncoder();
         TokenDecoder tokenDecoder = KrbRuntime.getTokenProvider().createTokenDecoder();