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