From d86ee2329b2284c34cb7d2da5cc6045924497526 Mon Sep 17 00:00:00 2001 From: Mike Derryberry Date: Tue, 10 Jul 2012 13:30:58 -0400 Subject: [PATCH] updated encryption to generate cek and cik through key derivation. also fixed signers to use afterPropertiesSet() --- .../main/java/org/mitre/jwe/model/Jwe.java | 49 +++--- .../jwt/encryption/AbstractJweDecrypter.java | 79 +-------- .../jwt/encryption/AbstractJweEncrypter.java | 150 ++++++------------ .../mitre/jwt/encryption/AlgorithmLength.java | 52 ++++++ .../mitre/jwt/encryption/JwtAlgorithm.java | 57 +++++++ .../mitre/jwt/encryption/JwtEncrypter.java | 8 +- .../mitre/jwt/encryption/impl/Decrypter.java | 102 ------------ .../mitre/jwt/encryption/impl/Encrypter.java | 113 ------------- .../jwt/encryption/impl/RsaDecrypter.java | 141 ++++++++++++++++ .../jwt/encryption/impl/RsaEncrypter.java | 146 +++++++++++++++++ .../org/mitre/jwt/signer/impl/HmacSigner.java | 24 +-- .../org/mitre/jwt/signer/impl/RsaSigner.java | 33 +--- 12 files changed, 483 insertions(+), 471 deletions(-) create mode 100644 openid-connect-common/src/main/java/org/mitre/jwt/encryption/AlgorithmLength.java create mode 100644 openid-connect-common/src/main/java/org/mitre/jwt/encryption/JwtAlgorithm.java delete mode 100644 openid-connect-common/src/main/java/org/mitre/jwt/encryption/impl/Decrypter.java delete mode 100644 openid-connect-common/src/main/java/org/mitre/jwt/encryption/impl/Encrypter.java create mode 100644 openid-connect-common/src/main/java/org/mitre/jwt/encryption/impl/RsaDecrypter.java create mode 100644 openid-connect-common/src/main/java/org/mitre/jwt/encryption/impl/RsaEncrypter.java diff --git a/openid-connect-common/src/main/java/org/mitre/jwe/model/Jwe.java b/openid-connect-common/src/main/java/org/mitre/jwe/model/Jwe.java index 0421c32c1..dd8dcf8a6 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwe/model/Jwe.java +++ b/openid-connect-common/src/main/java/org/mitre/jwe/model/Jwe.java @@ -6,10 +6,18 @@ import org.apache.commons.codec.binary.Base64; import org.mitre.jwt.model.Jwt; import com.google.common.base.Splitter; -import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.gson.JsonObject; +/** + * + * + * Return the canonical encoded string of this JWE, the header in Base64, a period ".", the encrypted key in Base64, a period ".", + * the ciphertext in Base64, a period ".", and the signature, or integrity value, in Base64. + + * @author DERRYBERRY + * + */ public class Jwe extends Jwt { private JweHeader header; @@ -18,20 +26,30 @@ public class Jwe extends Jwt { private byte[] ciphertext; - private String signature; - public Jwe() { + super(); this.header = new JweHeader(); this.encryptedKey = null; this.ciphertext = null; - this.signature = null; } public Jwe(JweHeader header, byte[] encryptedKey, byte[] ciphertext, String integrityValue) { + super(null, null, integrityValue); this.header = header; this.encryptedKey = encryptedKey; this.ciphertext = ciphertext; - this.signature = integrityValue; + } + + public Jwe(String headerBase64, String encryptedKeyBase64, String cipherTextBase64, String integrityValueBase64) { + super(null, null, new String(Base64.decodeBase64(integrityValueBase64))); + String decodedHeader = new String(Base64.decodeBase64(headerBase64)); + String decodedEncryptedKey = new String(Base64.decodeBase64(encryptedKeyBase64)); + String decodedCipherText = new String(Base64.decodeBase64(cipherTextBase64)); + String decodedIntegrityValue = new String(Base64.decodeBase64(integrityValueBase64)); + this.header = new JweHeader(decodedHeader); + this.encryptedKey = decodedEncryptedKey.getBytes(); + this.ciphertext = decodedCipherText.getBytes(); + setSignature(decodedIntegrityValue); } public JweHeader getHeader() { @@ -58,24 +76,6 @@ public class Jwe extends Jwt { this.ciphertext = ciphertext; } - public String getSignature() { - return signature; - } - - public void setSignature(String signature) { - this.signature = signature; - } - - - /** - * Return the canonical encoded string of this JWE, the header in Base64, a period ".", the encrypted key in Base64, a period ".", - * the ciphertext in Base64, a period ".", and the signature, or integrity value, in Base64. - */ - @Override - public String toString() { - return getSignatureBase() + "." + Strings.nullToEmpty(this.signature); - } - @Override public String getSignatureBase() { JsonObject h = header.getAsJsonObject(); @@ -109,8 +109,7 @@ public class Jwe extends Jwt { String c64 = parts.get(2); String i64 = parts.get(3); - Jwe jwe = new Jwe(new JweHeader(h64), e64.getBytes(), c64.getBytes(), i64); - //Jwe jwe = new Jwe(new JweHeader(h64), e64.getBytes(), new ClaimSet(c64), i64); + Jwe jwe = new Jwe(h64, e64, c64, i64); return jwe; diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/AbstractJweDecrypter.java b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/AbstractJweDecrypter.java index a6216766c..db4f85b3f 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/AbstractJweDecrypter.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/AbstractJweDecrypter.java @@ -1,24 +1,11 @@ package org.mitre.jwt.encryption; -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; - -import org.mitre.jwe.model.Jwe; - - public abstract class AbstractJweDecrypter implements JwtDecrypter { - - - private PrivateKey privateKey; + protected PrivateKey privateKey; private PublicKey publicKey; @@ -37,68 +24,4 @@ public abstract class AbstractJweDecrypter implements JwtDecrypter { public void setPublicKey(PublicKey publicKey) { this.publicKey = publicKey; } - - - @Override - public String decryptCipherText(Jwe jwe, Key cek) { - Cipher cipher; - String clearTextString = null; - try { - - cipher = Cipher.getInstance("RSA"); - cipher.init(Cipher.DECRYPT_MODE, cek); - byte[] clearText = cipher.doFinal(jwe.getCiphertext()); - clearTextString = new String(clearText); - - } catch (NoSuchAlgorithmException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (NoSuchPaddingException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InvalidKeyException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalBlockSizeException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (BadPaddingException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - return clearTextString; - - } - - @Override - public byte[] decryptEncryptionKey(Jwe jwe) { - Cipher cipher; - byte[] contentMasterKey = null; - - try { - - cipher = Cipher.getInstance("RSA"); - cipher.init(Cipher.DECRYPT_MODE, privateKey);//TODO: Get private key from key store. Placeholder - contentMasterKey = cipher.doFinal(jwe.getEncryptedKey()); - - } catch (NoSuchAlgorithmException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (NoSuchPaddingException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InvalidKeyException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalBlockSizeException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (BadPaddingException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - return contentMasterKey; - } } diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/AbstractJweEncrypter.java b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/AbstractJweEncrypter.java index f2506e956..f5dc0ee94 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/AbstractJweEncrypter.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/AbstractJweEncrypter.java @@ -1,32 +1,17 @@ package org.mitre.jwt.encryption; -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; +import java.security.MessageDigest; import java.security.PublicKey; -import java.security.SecureRandom; -import java.security.interfaces.RSAPublicKey; -import java.security.spec.InvalidParameterSpecException; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.IvParameterSpec; - -import org.mitre.jwe.model.Jwe; -import org.springframework.security.crypto.codec.Base64; - public abstract class AbstractJweEncrypter implements JwtEncrypter { - private byte[] encryptedKey; + protected byte[] encryptedKey; - private byte[] cipherText; + protected byte[] cipherText; - private RSAPublicKey publicKey; + protected PublicKey publicKey; + + public MessageDigest md; public byte[] getEncryptecKey() { return encryptedKey; @@ -43,93 +28,48 @@ public abstract class AbstractJweEncrypter implements JwtEncrypter { public void setCipherText(byte[] cipherText) { this.cipherText = cipherText; } + + public byte[] generateContentKey(byte[] cmk, int keyDataLen, byte[] type) { - - public byte[] encryptKey(Jwe jwe){ - - //generate random content master key - PublicKey contentMasterKey = null; - - try { - - KeyPairGenerator keyGen = KeyPairGenerator.getInstance(jwe.getHeader().getAlgorithm()); - SecureRandom random = SecureRandom.getInstance(jwe.getHeader().getAlgorithm()); - keyGen.initialize(1024, random); - KeyPair pair = keyGen.generateKeyPair(); - contentMasterKey = pair.getPublic(); - - } catch (NoSuchAlgorithmException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - - - //TODO:Get public key from keystore, currently null - Cipher cipher; - try { - cipher = Cipher.getInstance("RSA"); - cipher.init(Cipher.ENCRYPT_MODE, publicKey); - encryptedKey = cipher.doFinal(contentMasterKey.getEncoded()); - - } catch (NoSuchAlgorithmException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (NoSuchPaddingException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InvalidKeyException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalBlockSizeException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (BadPaddingException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - return encryptedKey; - + long MAX_HASH_INPUTLEN = Long.MAX_VALUE; + long UNSIGNED_INT_MAX_VALUE = 4294967395L; + + keyDataLen = keyDataLen / 8; + byte[] key = new byte[keyDataLen]; + int hashLen = md.getDigestLength(); + int reps = keyDataLen / hashLen; + if (reps > UNSIGNED_INT_MAX_VALUE) { + throw new IllegalArgumentException("Key derivation failed"); + } + int counter = 1; + byte[] counterInBytes = intToFourBytes(counter); + if ((counterInBytes.length + cmk.length + type.length) * 8 > MAX_HASH_INPUTLEN) { + throw new IllegalArgumentException("Key derivation failed"); + } + for (int i = 0; i <= reps; i++) { + md.reset(); + md.update(intToFourBytes(i + 1)); + md.update(cmk); + md.update(type); + byte[] hash = md.digest(); + if (i < reps) { + System.arraycopy(hash, 0, key, hashLen * i, hashLen); + } else { + System.arraycopy(hash, 0, key, hashLen * i, keyDataLen % hashLen); + } + } + return key; + } - public byte[] encryptClaims(Jwe jwe, Key cek) { - - Cipher cipher; - try { - cipher = Cipher.getInstance("RSA"); - - //TODO: generated the iv, but not sure how to use it to encrypt? - IvParameterSpec spec = cipher.getParameters().getParameterSpec(IvParameterSpec.class); - byte[] iv = spec.getIV(); - - cipher.init(Cipher.ENCRYPT_MODE, cek); - cipherText = cipher.doFinal(jwe.getClaims().toString().getBytes()); - - } catch (NoSuchAlgorithmException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (NoSuchPaddingException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InvalidKeyException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalBlockSizeException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (BadPaddingException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InvalidParameterSpecException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - return cipherText; - - } - - public abstract Jwe encryptAndSign(Jwe jwe) throws NoSuchAlgorithmException; + public byte[] intToFourBytes(int i) { + byte[] res = new byte[4]; + res[0] = (byte) (i >>> 24); + res[1] = (byte) ((i >>> 16) & 0xFF); + res[2] = (byte) ((i >>> 8) & 0xFF); + res[3] = (byte) (i & 0xFF); + return res; + } } diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/AlgorithmLength.java b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/AlgorithmLength.java new file mode 100644 index 000000000..fadb15004 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/AlgorithmLength.java @@ -0,0 +1,52 @@ +package org.mitre.jwt.encryption; + +import org.apache.commons.lang.StringUtils; + +public enum AlgorithmLength { + //TODO:Fill in values for each standard name + // RSA + A128CBC("128"), + A256CBC("256"), + A128GCM("128"), + A256GCM("256"); + + + /** + * Returns the Algorithm for the name + * + * @param name + * @return + */ + public static AlgorithmLength getByName(String name) { + for (AlgorithmLength correspondingType : AlgorithmLength.values()) { + if (correspondingType.toString().equals(name)) { + return correspondingType; + } + } + + // corresponding type not found + throw new IllegalArgumentException( + "AlgorithmLength name " + name + " does not have a corresponding AlgorithmLength: expected one of [" + StringUtils.join(AlgorithmLength.values(), ", ") + "]"); + } + + private final String standardName; + + /** + * Constructor of AlgorithmLength + * + * @param standardName + */ + AlgorithmLength(String standardName) { + this.standardName = standardName; + } + + /** + * Return the Java standard Algorithm Length + * + * @return + */ + public String getStandardName() { + return standardName; + } + +} diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/JwtAlgorithm.java b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/JwtAlgorithm.java new file mode 100644 index 000000000..cbc491b8e --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/JwtAlgorithm.java @@ -0,0 +1,57 @@ +package org.mitre.jwt.encryption; + +import org.apache.commons.lang.StringUtils; + +public enum JwtAlgorithm { + + //TODO:Fill in values for each standard name + // RSA + RSA1_5(""), + RSA_OAEP(""), + //EC + ECDH_ES(""), + //AES + A128KW(""), + A256KW(""), + A128GCM(""), + A256GCM(""); + + + /** + * Returns the Algorithm for the name + * + * @param name + * @return + */ + public static JwtAlgorithm getByName(String name) { + for (JwtAlgorithm correspondingType : JwtAlgorithm.values()) { + if (correspondingType.toString().equals(name)) { + return correspondingType; + } + } + + // corresponding type not found + throw new IllegalArgumentException( + "JwtAlgorithm name " + name + " does not have a corresponding JwtAlgorithm: expected one of [" + StringUtils.join(JwtAlgorithm.values(), ", ") + "]"); + } + + private final String standardName; + + /** + * Constructor of JwtAlgorithm + * + * @param standardName + */ + JwtAlgorithm(String standardName) { + this.standardName = standardName; + } + + /** + * Return the Java standard JwtAlgorithm name + * + * @return + */ + public String getStandardName() { + return standardName; + } +} diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/JwtEncrypter.java b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/JwtEncrypter.java index bde571ee3..10de1610e 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/JwtEncrypter.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/JwtEncrypter.java @@ -8,10 +8,14 @@ import org.mitre.jwe.model.Jwe; public interface JwtEncrypter { - public byte[] encryptKey(Jwe jwe); + public byte[] encryptKey(Jwe jwe, Key cmk); - public byte[] encryptClaims(Jwe jwe, Key cik); + public byte[] encryptClaims(Jwe jwe, byte[] cik); public Jwe encryptAndSign(Jwe jwe) throws NoSuchAlgorithmException; + + public byte[] generateContentKey(byte[] cmk, int keyDataLen, byte[] type); + + public byte[] intToFourBytes(int i); } diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/impl/Decrypter.java b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/impl/Decrypter.java deleted file mode 100644 index d69ddc4a4..000000000 --- a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/impl/Decrypter.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.mitre.jwt.encryption.impl; - -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; - -import org.apache.commons.codec.binary.Base64; -import org.mitre.jwe.model.Jwe; -import org.mitre.jwe.model.JweHeader; -import org.mitre.jwt.encryption.AbstractJweDecrypter; -import org.mitre.jwt.signer.impl.HmacSigner; - - -public class Decrypter extends AbstractJweDecrypter { - - private Jwe jwe; - - public Decrypter(Jwe jwe) { - setJwe(jwe); - } - - public Jwe getJwe() { - return jwe; - } - - public void setJwe(Jwe jwe) { - this.jwe = jwe; - } - - @Override - public Jwe decrypt(String encryptedJwe) { - - Jwe jwe = Jwe.parse(encryptedJwe); - - String alg = jwe.getHeader().getAlgorithm(); - if(alg.equals("RS256") || alg.equals("RS384") || alg.equals("RS512")) { - - //Base 64 decode each part of the jwe - String decodedHeader = new String(Base64.decodeBase64(jwe.getHeader().toString())); - JweHeader unencryptedHeader = new JweHeader(decodedHeader); - jwe.setHeader(unencryptedHeader); - - String decodedEncryptionKey = new String(Base64.decodeBase64(jwe.getEncryptedKey().toString())); - //sets decoded key on jwe so that it can be decrypted - jwe.setEncryptedKey(decodedEncryptionKey.getBytes()); - - String decodedCiphertext = new String(Base64.decodeBase64(jwe.getCiphertext().toString())); - //sets decoded ciphertext on jwe so that it can be decrypted - jwe.setCiphertext(decodedCiphertext.getBytes()); - - //decode signature, but don't set it on jwe yet. first has to be verified (see below) - String decodedSig = new String(Base64.decodeBase64(jwe.getSignature())); - - - PrivateKey contentEncryptionKey = null; - PublicKey contentIntegrityKey = null; - - try { - - KeyPairGenerator keyGen = KeyPairGenerator.getInstance(jwe.getHeader().getKeyDerivationFunction()); - KeyPair keyPair = keyGen.genKeyPair(); - contentEncryptionKey = keyPair.getPrivate(); - contentIntegrityKey = keyPair.getPublic(); - - } catch (NoSuchAlgorithmException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - - jwe.setCiphertext(decryptCipherText(jwe, contentEncryptionKey).getBytes()); - jwe.setEncryptedKey(decryptEncryptionKey(jwe)); - - //generate signature for decrypted signature base in order to verify that decryption worked - String signature = null; - try { - HmacSigner hmacSigner = new HmacSigner(contentIntegrityKey.getEncoded()); - signature = hmacSigner.generateSignature(jwe.getSignatureBase()); - } catch (NoSuchAlgorithmException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - //verifys that the signature base was decrypted correctly - if(signature != decodedSig){ - throw new IllegalArgumentException("Didn't decrypt correctly. Decoded Sig and generated Sig do not match"); - } - - jwe.setSignature(decodedSig); - - } else if(alg.equals("HS256") || alg.equals("HS384") || alg.equals("HS512")){ - - throw new IllegalArgumentException("Cannot use Hmac for decryption"); - - } else { - throw new IllegalArgumentException("Not a valid decrypting algorithm"); - } - return jwe; - } - -} diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/impl/Encrypter.java b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/impl/Encrypter.java deleted file mode 100644 index f7c510f15..000000000 --- a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/impl/Encrypter.java +++ /dev/null @@ -1,113 +0,0 @@ -package org.mitre.jwt.encryption.impl; - -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; - -import org.mitre.jwe.model.Jwe; -import org.mitre.jwe.model.JweHeader; -import org.mitre.jwt.encryption.AbstractJweEncrypter; -import org.mitre.jwt.model.JwtClaims; -import org.mitre.jwt.signer.impl.HmacSigner; - -public class Encrypter extends AbstractJweEncrypter { - - private Jwe jwe; - - private JweHeader header; - - private JwtClaims claims; - - private String signature; - - public Encrypter(Jwe jwe) { - setJwe(jwe); - setHeader(jwe.getHeader()); - setClaims(jwe.getClaims()); - setSignature(jwe.getSignature()); - } - - public Jwe getJwe() { - return jwe; - } - - public void setJwe(Jwe jwe) { - this.jwe = jwe; - } - - - public JweHeader getHeader() { - return header; - } - - public void setHeader(JweHeader header) { - this.header = header; - } - - public JwtClaims getClaims() { - return claims; - } - - public void setClaims(JwtClaims claims) { - this.claims = claims; - } - - public String getSignature() { - return signature; - } - - public void setSignature(String signature) { - this.signature = signature; - } - - @Override - public Jwe encryptAndSign(Jwe jwe) throws NoSuchAlgorithmException { - - String alg = jwe.getHeader().getAlgorithm(); - String iv = jwe.getHeader().getIntegrity(); - - if(alg.equals("RS256") || alg.equals("RS384") || alg.equals("RS512")) { - - //generate CEK and CIK - - PrivateKey contentEncryptionKey = null; - PublicKey contentIntegrityKey = null; - - try { - - KeyPairGenerator keyGen = KeyPairGenerator.getInstance(jwe.getHeader().getKeyDerivationFunction()); - KeyPair keyPair = keyGen.genKeyPair(); - contentEncryptionKey = keyPair.getPrivate(); - contentIntegrityKey = keyPair.getPublic(); - - } catch (NoSuchAlgorithmException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - - jwe.setCiphertext(encryptClaims(jwe, contentEncryptionKey)); - jwe.setEncryptedKey(encryptKey(jwe)); - - if(iv.equals("HS256") || iv.equals("HS384") || iv.equals("HS512")){ - - HmacSigner hmacSigner = new HmacSigner(contentIntegrityKey.getEncoded()); - jwe = (Jwe) hmacSigner.sign(jwe); - - } else if(iv.equals("RS256") || iv.equals("RS384") || iv.equals("RS512")) { - throw new IllegalArgumentException("Integrity Value must use Hmac signing"); - } else { - throw new IllegalArgumentException("Not a valid integrity value algorithm"); - } - - } else if(alg.equals("HS256") || alg.equals("HS384") || alg.equals("HS512")){ - throw new IllegalArgumentException("Cannot use Hmac for encryption"); - } else { - throw new IllegalArgumentException("Not a valid signing algorithm"); - } - - return jwe; - } - -} diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/impl/RsaDecrypter.java b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/impl/RsaDecrypter.java new file mode 100644 index 000000000..e4986c8a2 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/impl/RsaDecrypter.java @@ -0,0 +1,141 @@ +package org.mitre.jwt.encryption.impl; + +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; + +import org.mitre.jwe.model.Jwe; +import org.mitre.jwt.encryption.AbstractJweDecrypter; +import org.mitre.jwt.signer.impl.HmacSigner; + + +public class RsaDecrypter extends AbstractJweDecrypter { + + public RsaDecrypter() { + //TODO: Put something here + } + + @Override + public Jwe decrypt(String encryptedJwe) { + + Jwe jwe = Jwe.parse(encryptedJwe); + + String alg = jwe.getHeader().getAlgorithm(); + if(alg.equals("RS256") || alg.equals("RS384") || alg.equals("RS512")) { + + PrivateKey contentEncryptionKey = null; + PublicKey contentIntegrityKey = null; + + try { + + KeyPairGenerator keyGen = KeyPairGenerator.getInstance(jwe.getHeader().getKeyDerivationFunction()); + KeyPair keyPair = keyGen.genKeyPair(); + contentEncryptionKey = keyPair.getPrivate(); + contentIntegrityKey = keyPair.getPublic(); + + } catch (NoSuchAlgorithmException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + + jwe.setCiphertext(decryptCipherText(jwe, contentEncryptionKey).getBytes()); + jwe.setEncryptedKey(decryptEncryptionKey(jwe)); + + //generate signature for decrypted signature base in order to verify that decryption worked + String signature = null; + try { + HmacSigner hmacSigner = new HmacSigner(contentIntegrityKey.getEncoded()); + signature = hmacSigner.generateSignature(jwe.getSignatureBase()); + } catch (NoSuchAlgorithmException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + //verifys that the signature base was decrypted correctly + if(signature != jwe.getSignature()){ + throw new IllegalArgumentException("Didn't decrypt correctly. Decoded Sig and generated Sig do not match"); + } + + } else if(alg.equals("HS256") || alg.equals("HS384") || alg.equals("HS512")){ + + throw new IllegalArgumentException("Cannot use Hmac for decryption"); + + } else { + throw new IllegalArgumentException("Not a valid decrypting algorithm"); + } + return jwe; + } + + @Override + public String decryptCipherText(Jwe jwe, Key cek) { + Cipher cipher; + String clearTextString = null; + try { + + cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, cek); + byte[] clearText = cipher.doFinal(jwe.getCiphertext()); + clearTextString = new String(clearText); + + } catch (NoSuchAlgorithmException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (NoSuchPaddingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InvalidKeyException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalBlockSizeException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (BadPaddingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return clearTextString; + + } + + @Override + public byte[] decryptEncryptionKey(Jwe jwe) { + Cipher cipher; + byte[] contentMasterKey = null; + + try { + + cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, privateKey);//TODO: Get private key from key store. Placeholder + contentMasterKey = cipher.doFinal(jwe.getEncryptedKey()); + + } catch (NoSuchAlgorithmException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (NoSuchPaddingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InvalidKeyException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalBlockSizeException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (BadPaddingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return contentMasterKey; + } + +} diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/impl/RsaEncrypter.java b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/impl/RsaEncrypter.java new file mode 100644 index 000000000..130744a7f --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/impl/RsaEncrypter.java @@ -0,0 +1,146 @@ +package org.mitre.jwt.encryption.impl; + +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.SecretKeySpec; + +import org.mitre.jwe.model.Jwe; +import org.mitre.jwt.encryption.AbstractJweEncrypter; +import org.mitre.jwt.encryption.AlgorithmLength; +import org.mitre.jwt.encryption.JwtAlgorithm; +import org.mitre.jwt.signer.impl.HmacSigner; + +public class RsaEncrypter extends AbstractJweEncrypter { + + + public RsaEncrypter() { + //TODO: Put something here + } + + public Jwe encryptAndSign(Jwe jwe) throws NoSuchAlgorithmException { + + String alg = jwe.getHeader().getAlgorithm(); + String iv = jwe.getHeader().getIntegrity(); + + if(alg.equals("RS256") || alg.equals("RS384") || alg.equals("RS512")) { + + //generate random content master key + Key contentMasterKey = null; + + try { + + KeyPairGenerator keyGen = KeyPairGenerator.getInstance(jwe.getHeader().getAlgorithm()); + SecureRandom random = SecureRandom.getInstance(jwe.getHeader().getAlgorithm()); + keyGen.initialize(1024, random); + KeyPair pair = keyGen.generateKeyPair(); + contentMasterKey = pair.getPublic(); + + } catch (NoSuchAlgorithmException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + + //generate CEK and CIK + + String algorithmLength = AlgorithmLength.getByName(jwe.getHeader().getEncryptionMethod()).getStandardName(); + int keyLength = Integer.parseInt(algorithmLength); + byte[] contentEncryptionKey = generateContentKey(contentMasterKey.getEncoded(), keyLength, new String("Encryption").getBytes()); + byte[] contentIntegrityKey = generateContentKey(contentMasterKey.getEncoded(), keyLength, new String("Integrity").getBytes()); + + //encrypt claims and cmk to get ciphertext and encrypted key + jwe.setCiphertext(encryptClaims(jwe, contentEncryptionKey)); + jwe.setEncryptedKey(encryptKey(jwe, contentMasterKey)); + + //Signer must be hmac + if(iv.equals("HS256") || iv.equals("HS384") || iv.equals("HS512")){ + + HmacSigner hmacSigner = new HmacSigner(contentIntegrityKey); + jwe = (Jwe) hmacSigner.sign(jwe); + + } else if(iv.equals("RS256") || iv.equals("RS384") || iv.equals("RS512")) { + throw new IllegalArgumentException("Integrity Value must use Hmac signing"); + } else { + throw new IllegalArgumentException("Not a valid integrity value algorithm"); + } + + } else if(alg.equals("HS256") || alg.equals("HS384") || alg.equals("HS512")){ + throw new IllegalArgumentException("Cannot use Hmac for encryption"); + } else { + throw new IllegalArgumentException("Not a valid signing algorithm"); + } + + return jwe; + } + + public byte[] encryptKey(Jwe jwe, Key cmk) { + + //TODO:Get public key from keystore, currently null + Cipher cipher; + try { + cipher = Cipher.getInstance(JwtAlgorithm.getByName(jwe.getHeader().getAlgorithm()).getStandardName()); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + encryptedKey = cipher.doFinal(cmk.getEncoded()); + + } catch (NoSuchAlgorithmException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (NoSuchPaddingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InvalidKeyException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalBlockSizeException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (BadPaddingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return encryptedKey; + + } + + public byte[] encryptClaims(Jwe jwe, byte[] contentEncryptionKey) { + + Cipher cipher; + try { + cipher = Cipher.getInstance(JwtAlgorithm.getByName(jwe.getHeader().getAlgorithm()).getStandardName()); + + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(contentEncryptionKey, 0, contentEncryptionKey.length, "RSA")); + cipherText = cipher.doFinal(jwe.getClaims().toString().getBytes()); + + } catch (NoSuchAlgorithmException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (NoSuchPaddingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InvalidKeyException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalBlockSizeException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (BadPaddingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return cipherText; + + } + + + +} diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/impl/HmacSigner.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/impl/HmacSigner.java index dd48d9bd0..ac80268ca 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/impl/HmacSigner.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/impl/HmacSigner.java @@ -124,17 +124,7 @@ public class HmacSigner extends AbstractJwtSigner implements InitializingBean { */ @Override public void afterPropertiesSet(){ - - try { - mac = Mac.getInstance(JwsAlgorithm.getByName(super.getAlgorithm()) - .getStandardName()); - } catch (NoSuchAlgorithmException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - logger.debug(JwsAlgorithm.getByName(getAlgorithm()).getStandardName() - + " ECDSA Signer ready for business"); + initializeMac(); } /* @@ -147,14 +137,7 @@ public class HmacSigner extends AbstractJwtSigner implements InitializingBean { @Override public String generateSignature(String signatureBase) throws NoSuchAlgorithmException { - List parts = Lists.newArrayList(Splitter.on(".").split(signatureBase)); - - if (parts.size() == 2) { - initializeMac(); - } - else if (parts.size() == 3) { - initializeMacJwe(signatureBase); - } + afterPropertiesSet(); if (passphrase == null) { throw new IllegalArgumentException("Passphrase cannot be null"); @@ -194,6 +177,7 @@ public class HmacSigner extends AbstractJwtSigner implements InitializingBean { } public void initializeMac() { + // TODO: check if it's already been done try { mac = Mac.getInstance(JwsAlgorithm.getByName(super.getAlgorithm()).getStandardName()); } catch (NoSuchAlgorithmException e) { @@ -201,7 +185,7 @@ public class HmacSigner extends AbstractJwtSigner implements InitializingBean { e.printStackTrace(); } } - + // TODO: nuke and clean up public void initializeMacJwe(String signatureBase) { List parts = Lists.newArrayList(Splitter.on(".").split(signatureBase)); String header = parts.get(0); diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/impl/RsaSigner.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/impl/RsaSigner.java index 858453c88..32e6da67a 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/impl/RsaSigner.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/impl/RsaSigner.java @@ -36,8 +36,6 @@ import org.springframework.util.Assert; import com.google.common.base.Splitter; import com.google.common.collect.Lists; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; /** * JWT Signer using either the RSA SHA-256, SHA-384, SHA-512 hash algorithm @@ -138,14 +136,7 @@ public class RsaSigner extends AbstractJwtSigner implements InitializingBean { */ @Override public void afterPropertiesSet() throws NoSuchAlgorithmException, GeneralSecurityException { - - // unsupported algorithm will throw a NoSuchAlgorithmException - signer = Signature.getInstance(JwsAlgorithm.getByName(super.getAlgorithm()).getStandardName()); // ,PROVIDER); - - loadKeysFromKeystore(); - - logger.debug(JwsAlgorithm.getByName(getAlgorithm()).getStandardName() + " RSA Signer ready for business"); - + initializeSigner(); } /** @@ -176,13 +167,13 @@ public class RsaSigner extends AbstractJwtSigner implements InitializingBean { public String generateSignature(String signatureBase) throws NoSuchAlgorithmException { String sig = null; - List parts = Lists.newArrayList(Splitter.on(".").split(signatureBase)); - - if(parts.size() == 2) { - initializeSigner(); - } else if (parts.size() == 3) { - initializeSignerJwe(signatureBase); + try { + afterPropertiesSet(); + } catch (GeneralSecurityException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); } + try { signer.initSign(privateKey); signer.update(signatureBase.getBytes("UTF-8")); @@ -238,16 +229,6 @@ public class RsaSigner extends AbstractJwtSigner implements InitializingBean { public void initializeSigner() throws NoSuchAlgorithmException{ signer = Signature.getInstance(JwsAlgorithm.getByName(super.getAlgorithm()).getStandardName()); } - - public void initializeSignerJwe(String signatureBase) throws NoSuchAlgorithmException { - - List parts = Lists.newArrayList(Splitter.on(".").split(signatureBase)); - String header = parts.get(0); - JsonParser parser = new JsonParser(); - JsonObject object = (JsonObject) parser.parse(header); - - signer = Signature.getInstance(JwsAlgorithm.getByName(object.get("int").getAsString()).getStandardName()); - } /* * (non-Javadoc)