refactored algorithms out to their own separate Enum

pull/59/head
Justin Richer 2012-04-02 13:13:13 -04:00
parent fec6a3a876
commit 3dfe6df410
6 changed files with 102 additions and 223 deletions

View File

@ -0,0 +1,64 @@
package org.mitre.jwt.signer;
import org.apache.commons.lang.StringUtils;
/**
* Enum to translate between the JWS defined algorithm names and the JSE algorithm names
*
* @author jricher
*
*/
public enum JwsAlgorithm {
// HMAC
HS256("HMACSHA256"),
HS384("HMACSHA384"),
HS512("HMACSHA512"),
// RSA
RS256("SHA256withRSA"),
RS384("SHA384withRSA"),
RS512("SHA512withRSA"),
// ECDSA
ES256("SHA256withECDSA"),
ES384("SHA384withECDSA"),
ES512("SHA512withECDSA");
/**
* Returns the Algorithm for the name
*
* @param name
* @return
*/
public static JwsAlgorithm getByName(String name) {
for (JwsAlgorithm correspondingType : JwsAlgorithm.values()) {
if (correspondingType.toString().equals(name)) {
return correspondingType;
}
}
// corresponding type not found
throw new IllegalArgumentException(
"JwsAlgorithm name " + name + " does not have a corresponding JwsAlgorithm: expected one of [" + StringUtils.join(JwsAlgorithm.values(), ", ") + "]");
}
private final String standardName;
/**
* Constructor of JwsAlgorithm
*
* @param standardName
*/
JwsAlgorithm(String standardName) {
this.standardName = standardName;
}
/**
* Return the Java standard JwsAlgorithm name
*
* @return
*/
public String getStandardName() {
return standardName;
}
}

View File

@ -9,10 +9,10 @@ import java.security.Signature;
import java.util.List; import java.util.List;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.mitre.jwt.signer.AbstractJwtSigner; import org.mitre.jwt.signer.AbstractJwtSigner;
import org.mitre.jwt.signer.JwsAlgorithm;
import org.mitre.jwt.signer.service.impl.KeyStore; import org.mitre.jwt.signer.service.impl.KeyStore;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -30,60 +30,6 @@ import com.google.common.collect.Lists;
*/ */
public class EcdsaSigner extends AbstractJwtSigner implements InitializingBean { public class EcdsaSigner extends AbstractJwtSigner implements InitializingBean {
/**
* an enum for mapping a JWS name to standard algorithm name
*
* @author nemonik
*
*/
public enum Algorithm {
// Algorithm constants
ES256("SHA256withECDSA"), ES384("SHA384withECDSA"), ES512(
"SHA512withECDSA");
public static final String DEFAULT = Algorithm.ES256.toString();
public static final String PREPEND = "ES";
/**
* Returns the Algorithm for the name
*
* @param name
* @return
*/
public static Algorithm getByName(String name) {
for (Algorithm correspondingType : Algorithm.values()) {
if (correspondingType.toString().equals(name)) {
return correspondingType;
}
}
// corresponding type not found
throw new IllegalArgumentException(
"Algorithm name " + name + " does not have a corresponding Algorithm: expected one of [" + StringUtils.join(Algorithm.values(), ", ") + "]");
}
private final String standardName;
/**
* Constructor of Algorithm
*
* @param standardName
*/
Algorithm(String standardName) {
this.standardName = standardName;
}
/**
* Return the Java standard algorithm name
*
* @return
*/
public String getStandardName() {
return standardName;
}
};
private static Log logger = LogFactory.getLog(EcdsaSigner.class); private static Log logger = LogFactory.getLog(EcdsaSigner.class);
public static final String KEYPAIR_ALGORITHM = "EC"; public static final String KEYPAIR_ALGORITHM = "EC";
@ -97,11 +43,14 @@ public class EcdsaSigner extends AbstractJwtSigner implements InitializingBean {
private PublicKey publicKey; private PublicKey publicKey;
private Signature signer; private Signature signer;
public static final String DEFAULT_ALGORITHM = JwsAlgorithm.ES256.toString();
//public static final String PREPEND = "ES";
/** /**
* Default constructor * Default constructor
*/ */
public EcdsaSigner() { public EcdsaSigner() {
super(Algorithm.DEFAULT); super(DEFAULT_ALGORITHM);
} }
/** /**
@ -167,8 +116,7 @@ public class EcdsaSigner extends AbstractJwtSigner implements InitializingBean {
* @param privateKey * @param privateKey
* The private key * The private key
*/ */
public EcdsaSigner(String algorithmName, PublicKey publicKey, public EcdsaSigner(String algorithmName, PublicKey publicKey, PrivateKey privateKey) {
PrivateKey privateKey) {
super(algorithmName); super(algorithmName);
Assert.notNull(publicKey, "A publicKey must be supplied"); Assert.notNull(publicKey, "A publicKey must be supplied");
@ -188,11 +136,9 @@ public class EcdsaSigner extends AbstractJwtSigner implements InitializingBean {
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
// Can throw a GeneralException // Can throw a GeneralException
signer = Signature.getInstance(Algorithm.getByName(super.getAlgorithm()) signer = Signature.getInstance(JwsAlgorithm.getByName(super.getAlgorithm()).getStandardName()); // PROVIDER);
.getStandardName()); // PROVIDER);
logger.debug(Algorithm.getByName(getAlgorithm()).getStandardName() logger.debug(JwsAlgorithm.getByName(getAlgorithm()).getStandardName() + " ECDSA Signer ready for business");
+ " ECDSA Signer ready for business");
} }
/* (non-Javadoc) /* (non-Javadoc)

View File

@ -4,16 +4,15 @@ import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import javax.crypto.Mac; import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.mitre.jwt.signer.AbstractJwtSigner; import org.mitre.jwt.signer.AbstractJwtSigner;
import org.mitre.jwt.signer.JwsAlgorithm;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -25,65 +24,9 @@ import org.springframework.util.Assert;
*/ */
public class HmacSigner extends AbstractJwtSigner implements InitializingBean { public class HmacSigner extends AbstractJwtSigner implements InitializingBean {
/** public static final String DEFAULT_PASSPHRASE = "changeit";
* an enum for mapping a JWS name to standard algorithm name
*
* @author nemonik
*
*/
public enum Algorithm {
// Algorithm constants public static final String DEFAULT_ALGORITHM = JwsAlgorithm.HS256.toString();
HS256("HMACSHA256"), HS384("HMACSHA384"), HS512("HMACSHA512");
public static final String DEFAULT = Algorithm.HS256.toString();
/**
* Returns the Algorithm for the name
*
* @param name
* @return
*/
public static Algorithm getByName(String name) {
for (Algorithm correspondingType : Algorithm.values()) {
if (correspondingType.toString().equals(name)) {
return correspondingType;
}
}
ArrayList<String> longValues = new ArrayList<String>();
for (Algorithm v : Algorithm.values()) {
longValues.add(v.standardName);
}
// corresponding type not found
throw new IllegalArgumentException(
"Algorithm name " + name + " does not have a corresponding Algorithm: expected one of [" + StringUtils.join(Algorithm.values(), ", ") + "]");
}
private final String standardName;
/**
* Constructor of Algorithm
*
* @param standardName
*/
Algorithm(String standardName) {
this.standardName = standardName;
}
/**
* Return the Java standard algorithm name
*
* @return
*/
public String getStandardName() {
return standardName;
}
}
public static final String DEFAULT_PASSPHRASE = "changeit";;
private static Log logger = LogFactory.getLog(HmacSigner.class); private static Log logger = LogFactory.getLog(HmacSigner.class);
@ -95,7 +38,7 @@ public class HmacSigner extends AbstractJwtSigner implements InitializingBean {
* Default constructor * Default constructor
*/ */
public HmacSigner() { public HmacSigner() {
super(Algorithm.DEFAULT); super(DEFAULT_ALGORITHM);
} }
/** /**
@ -106,8 +49,7 @@ public class HmacSigner extends AbstractJwtSigner implements InitializingBean {
*/ */
public HmacSigner(byte[] passphraseAsRawBytes) public HmacSigner(byte[] passphraseAsRawBytes)
throws NoSuchAlgorithmException { throws NoSuchAlgorithmException {
this(Algorithm.DEFAULT, new String(passphraseAsRawBytes, this(DEFAULT_ALGORITHM, new String(passphraseAsRawBytes, Charset.forName("UTF-8")));
Charset.forName("UTF-8")));
} }
/** /**
@ -117,7 +59,7 @@ public class HmacSigner extends AbstractJwtSigner implements InitializingBean {
* The passphrase as raw bytes * The passphrase as raw bytes
*/ */
public HmacSigner(String passphrase) throws NoSuchAlgorithmException { public HmacSigner(String passphrase) throws NoSuchAlgorithmException {
this(Algorithm.DEFAULT, passphrase); this(DEFAULT_ALGORITHM, passphrase);
} }
/** /**
@ -130,8 +72,7 @@ public class HmacSigner extends AbstractJwtSigner implements InitializingBean {
*/ */
public HmacSigner(String algorithmName, byte[] passphraseAsRawBytes) public HmacSigner(String algorithmName, byte[] passphraseAsRawBytes)
throws NoSuchAlgorithmException { throws NoSuchAlgorithmException {
this(algorithmName, new String(passphraseAsRawBytes, this(algorithmName, new String(passphraseAsRawBytes, Charset.forName("UTF-8")));
Charset.forName("UTF-8")));
} }
/** /**
@ -160,11 +101,9 @@ public class HmacSigner extends AbstractJwtSigner implements InitializingBean {
@Override @Override
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
mac = Mac.getInstance(Algorithm.getByName(super.getAlgorithm()) mac = Mac.getInstance(JwsAlgorithm.getByName(super.getAlgorithm()).getStandardName());
.getStandardName());
logger.debug(Algorithm.getByName(getAlgorithm()).getStandardName() logger.debug(JwsAlgorithm.getByName(getAlgorithm()).getStandardName() + " ECDSA Signer ready for business");
+ " ECDSA Signer ready for business");
} }
@ -182,8 +121,7 @@ public class HmacSigner extends AbstractJwtSigner implements InitializingBean {
} }
try { try {
mac.init(new SecretKeySpec(getPassphrase().getBytes(), mac mac.init(new SecretKeySpec(getPassphrase().getBytes(), mac.getAlgorithm()));
.getAlgorithm()));
mac.update(signatureBase.getBytes("UTF-8")); mac.update(signatureBase.getBytes("UTF-8"));
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {

View File

@ -4,6 +4,7 @@ import org.mitre.jwt.signer.AbstractJwtSigner;
public class PlaintextSigner extends AbstractJwtSigner { public class PlaintextSigner extends AbstractJwtSigner {
// Todo: should this be a JwsAlgorithm?
public static final String PLAINTEXT = "none"; public static final String PLAINTEXT = "none";
public PlaintextSigner() { public PlaintextSigner() {

View File

@ -10,10 +10,10 @@ import java.security.interfaces.RSAPrivateKey;
import java.util.List; import java.util.List;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.mitre.jwt.signer.AbstractJwtSigner; import org.mitre.jwt.signer.AbstractJwtSigner;
import org.mitre.jwt.signer.JwsAlgorithm;
import org.mitre.jwt.signer.service.impl.KeyStore; import org.mitre.jwt.signer.service.impl.KeyStore;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -29,64 +29,13 @@ import com.google.common.collect.Lists;
*/ */
public class RsaSigner extends AbstractJwtSigner implements InitializingBean { public class RsaSigner extends AbstractJwtSigner implements InitializingBean {
/**
* an enum for mapping a JWS name to standard algorithm name
*
* @author nemonik
*
*/
public enum Algorithm {
// Algorithm constants
RS256("SHA256withRSA"), RS384("SHA384withRSA"), RS512("SHA512withRSA");
public static final String DEFAULT = Algorithm.RS256.toString();
public static final String PREPEND = "RS";
/**
* Returns the Algorithm for the name
*
* @param name
* @return
*/
public static Algorithm getByName(String name) {
for (Algorithm correspondingType : Algorithm.values()) {
if (correspondingType.toString().equals(name)) {
return correspondingType;
}
}
// corresponding type not found
throw new IllegalArgumentException(
"Algorithm name " + name + " does not have a corresponding Algorithm: expected one of [" + StringUtils.join(Algorithm.values(), ", ") + "]");
}
private final String standardName;
/**
* Constructor of Algorithm
*
* @param standardName
*/
Algorithm(String standardName) {
this.standardName = standardName;
}
/**
* Return the Java standard algorithm name
*
* @return
*/
public String getStandardName() {
return standardName;
}
};
private static Log logger = LogFactory.getLog(RsaSigner.class); private static Log logger = LogFactory.getLog(RsaSigner.class);
public static final String KEYPAIR_ALGORITHM = "RSA"; public static final String KEYPAIR_ALGORITHM = "RSA";
public static final String DEFAULT_PASSWORD = "changeit"; public static final String DEFAULT_PASSWORD = "changeit";
public static final String DEFAULT_ALGORITHM = JwsAlgorithm.RS256.toString();
private KeyStore keystore; private KeyStore keystore;
private String alias; private String alias;
private String password = DEFAULT_PASSWORD; private String password = DEFAULT_PASSWORD;
@ -99,7 +48,7 @@ public class RsaSigner extends AbstractJwtSigner implements InitializingBean {
* Default constructor * Default constructor
*/ */
public RsaSigner() { public RsaSigner() {
super(Algorithm.DEFAULT); super(DEFAULT_ALGORITHM);
} }
/** /**
@ -135,8 +84,7 @@ public class RsaSigner extends AbstractJwtSigner implements InitializingBean {
* The password used to access and retrieve the key pair. * The password used to access and retrieve the key pair.
* @throws GeneralSecurityException * @throws GeneralSecurityException
*/ */
public RsaSigner(String algorithmName, KeyStore keystore, String alias, public RsaSigner(String algorithmName, KeyStore keystore, String alias, String password) throws GeneralSecurityException {
String password) throws GeneralSecurityException {
super(algorithmName); super(algorithmName);
Assert.notNull(keystore, "An keystore must be supplied"); Assert.notNull(keystore, "An keystore must be supplied");
@ -166,8 +114,7 @@ public class RsaSigner extends AbstractJwtSigner implements InitializingBean {
* @param privateKey * @param privateKey
* The private key * The private key
*/ */
public RsaSigner(String algorithmName, PublicKey publicKey, public RsaSigner(String algorithmName, PublicKey publicKey, PrivateKey privateKey) {
PrivateKey privateKey) {
super(algorithmName); super(algorithmName);
Assert.notNull(publicKey, "An publicKey must be supplied"); Assert.notNull(publicKey, "An publicKey must be supplied");
@ -187,12 +134,9 @@ public class RsaSigner extends AbstractJwtSigner implements InitializingBean {
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
// unsupported algorithm will throw a NoSuchAlgorithmException // unsupported algorithm will throw a NoSuchAlgorithmException
signer = Signature.getInstance(Algorithm signer = Signature.getInstance(JwsAlgorithm.getByName(super.getAlgorithm()).getStandardName()); // ,PROVIDER);
.getByName(super.getAlgorithm()).getStandardName()); // ,
// PROVIDER);
logger.debug(Algorithm.getByName(getAlgorithm()).getStandardName() logger.debug(JwsAlgorithm.getByName(getAlgorithm()).getStandardName() + " RSA Signer ready for business");
+ " RSA Signer ready for business");
} }
@ -214,8 +158,7 @@ public class RsaSigner extends AbstractJwtSigner implements InitializingBean {
byte[] sigBytes = signer.sign(); byte[] sigBytes = signer.sign();
sig = (new String(Base64.encodeBase64URLSafe(sigBytes))).replace( sig = (new String(Base64.encodeBase64URLSafe(sigBytes))).replace("=", "");
"=", "");
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
logger.error(e); logger.error(e);
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {

View File

@ -3,12 +3,9 @@ package org.mitre.jwt;
import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyFactory; import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
@ -20,6 +17,7 @@ import org.bouncycastle.x509.X509V3CertificateGenerator;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mitre.jwt.model.Jwt; import org.mitre.jwt.model.Jwt;
import org.mitre.jwt.signer.JwsAlgorithm;
import org.mitre.jwt.signer.JwtSigner; import org.mitre.jwt.signer.JwtSigner;
import org.mitre.jwt.signer.impl.HmacSigner; import org.mitre.jwt.signer.impl.HmacSigner;
import org.mitre.jwt.signer.impl.PlaintextSigner; import org.mitre.jwt.signer.impl.PlaintextSigner;
@ -72,8 +70,7 @@ public class JwtTest {
* Expected signature: iGBPJj47S5q_HAhSoQqAdcS6A_1CFj3zrLaImqNbt9E * Expected signature: iGBPJj47S5q_HAhSoQqAdcS6A_1CFj3zrLaImqNbt9E
*/ */
String signature = "p-63Jzz7mgi3H4hvW6MFB7lmPRZjhsL666MYkmpX33Y"; String signature = "p-63Jzz7mgi3H4hvW6MFB7lmPRZjhsL666MYkmpX33Y";
String expected = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ." String expected = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ." + signature;
+ signature;
String actual = jwt.toString(); String actual = jwt.toString();
@ -118,21 +115,17 @@ public class JwtTest {
// BC sez X509V3CertificateGenerator is deprecated and the docs say to // BC sez X509V3CertificateGenerator is deprecated and the docs say to
// use another, but it seemingly isn't included jar... // use another, but it seemingly isn't included jar...
X509V3CertificateGenerator v3CertGen = KeyStoreTest.createCertificate( X509V3CertificateGenerator v3CertGen = KeyStoreTest.createCertificate("testGenerateRsaSignature", 30, 30);
"testGenerateRsaSignature", 30, 30);
v3CertGen.setPublicKey(publicKey); v3CertGen.setPublicKey(publicKey);
v3CertGen.setSignatureAlgorithm("SHA256WithRSAEncryption"); v3CertGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
// BC docs say to use another, but it seemingly isn't included... // BC docs say to use another, but it seemingly isn't included...
X509Certificate certificate = v3CertGen X509Certificate certificate = v3CertGen.generateX509Certificate(privateKey);
.generateX509Certificate(privateKey);
// if exist, overwrite // if exist, overwrite
java.security.KeyStore ks = keystore.getKeystore(); java.security.KeyStore ks = keystore.getKeystore();
ks.setKeyEntry("testGenerateRsaSignature", privateKey, ks.setKeyEntry("testGenerateRsaSignature", privateKey, RsaSigner.DEFAULT_PASSWORD.toCharArray(), new java.security.cert.Certificate[] { certificate });
RsaSigner.DEFAULT_PASSWORD.toCharArray(),
new java.security.cert.Certificate[] { certificate });
keystore.setKeystore(ks); keystore.setKeystore(ks);
@ -143,9 +136,7 @@ public class JwtTest {
jwt.getClaims().setIssuer("joe"); jwt.getClaims().setIssuer("joe");
jwt.getClaims().setClaim("http://example.com/is_root", Boolean.TRUE); jwt.getClaims().setClaim("http://example.com/is_root", Boolean.TRUE);
JwtSigner signer = new RsaSigner(RsaSigner.Algorithm.RS256.toString(), JwtSigner signer = new RsaSigner(JwsAlgorithm.RS256.toString(), keystore, "testGenerateRsaSignature", RsaSigner.DEFAULT_PASSWORD);
keystore, "testGenerateRsaSignature",
RsaSigner.DEFAULT_PASSWORD);
((RsaSigner) signer).afterPropertiesSet(); ((RsaSigner) signer).afterPropertiesSet();
/* /*
@ -182,15 +173,10 @@ public class JwtTest {
Jwt jwt = Jwt.parse(source); Jwt jwt = Jwt.parse(source);
assertThat(jwt.getHeader().getAlgorithm(), assertThat(jwt.getHeader().getAlgorithm(), equalTo(PlaintextSigner.PLAINTEXT));
equalTo(PlaintextSigner.PLAINTEXT));
assertThat(jwt.getClaims().getIssuer(), equalTo("joe")); assertThat(jwt.getClaims().getIssuer(), equalTo("joe"));
assertThat(jwt.getClaims().getExpiration(), equalTo(new Date( assertThat(jwt.getClaims().getExpiration(), equalTo(new Date(1300819380L * 1000L)));
1300819380L * 1000L))); assertThat((Boolean) jwt.getClaims().getClaim("http://example.com/is_root"), equalTo(Boolean.TRUE));
assertThat(
(Boolean) jwt.getClaims()
.getClaim("http://example.com/is_root"),
equalTo(Boolean.TRUE));
} }
@ -258,4 +244,5 @@ public class JwtTest {
e.printStackTrace(); e.printStackTrace();
} }
} }
} }