From 88a052019a1b3bee6f04f0612d3197f18d7cb0ea Mon Sep 17 00:00:00 2001 From: Mike Derryberry Date: Thu, 12 Jul 2012 15:31:11 -0400 Subject: [PATCH] added testing for encryption and decryption. slight bug where [ ] gets appended to clearText. working on that --- openid-connect-common/encryptedJwe.txt | 1 + .../main/java/org/mitre/jwe/model/Jwe.java | 13 +- .../java/org/mitre/jwe/model/JweHeader.java | 22 +-- .../jwt/encryption/AbstractJweEncrypter.java | 5 +- .../mitre/jwt/encryption/AlgorithmLength.java | 52 ------ .../mitre/jwt/encryption/JwtDecrypter.java | 15 +- .../mitre/jwt/encryption/JwtEncrypter.java | 15 +- .../jwt/encryption/impl/RsaDecrypter.java | 115 ++++++------- .../jwt/encryption/impl/RsaEncrypter.java | 157 +++++++----------- .../java/org/mitre/jwt/model/JwtHeader.java | 2 + .../mitre/jwt/signer/AbstractJwtSigner.java | 5 +- .../jwe/encryption/impl/RsaDecrypterTest.java | 50 ------ .../impl/RsaEncrypterDecrypterTest.java | 99 +++++++++++ .../jwe/encryption/impl/RsaEncrypterTest.java | 62 ------- .../src/test/resources/jwe/encryptedJwe | 1 - .../src/test/resources/jwe/jwePlaintext | 2 +- 16 files changed, 263 insertions(+), 353 deletions(-) create mode 100644 openid-connect-common/encryptedJwe.txt delete mode 100644 openid-connect-common/src/main/java/org/mitre/jwt/encryption/AlgorithmLength.java delete mode 100644 openid-connect-common/src/test/java/org/mitre/jwe/encryption/impl/RsaDecrypterTest.java create mode 100644 openid-connect-common/src/test/java/org/mitre/jwe/encryption/impl/RsaEncrypterDecrypterTest.java delete mode 100644 openid-connect-common/src/test/java/org/mitre/jwe/encryption/impl/RsaEncrypterTest.java diff --git a/openid-connect-common/encryptedJwe.txt b/openid-connect-common/encryptedJwe.txt new file mode 100644 index 000000000..a35dd21b3 --- /dev/null +++ b/openid-connect-common/encryptedJwe.txt @@ -0,0 +1 @@ +eyJpbnQiOiJIUzI1NiIsIml2IjoiQXhZOERDdERhR2xzYkdsamIzUm9aUSIsImFsZyI6IkhTMjU2IiwiZW5jIjoiQTEyOENCQyJ9.1TWc1U58WUtz6HnRVfFcCFt04dWJivMZmTG3ZeIUKbfWbG6UHsYRNnj1a9LvYpb7DA97j57whfXxxwT9P3ZnCMaeqm_JCmyasocu7ftefELw4BSYcBuIGeiOzZO7mffS4lj5s7N76kV-LXkVIMwjzKom1z1doBqnmV7M5yUCTnxQJ3ao7LYLJI0QiCY5Pcqd.CS8GW8JLnDAF4SEvw2kras0yp50-eIX90Mbn1qKF1cePqG3VWcItEJoPa0FbFbmj.0uQ48_WFcQR-q6IBcs8IG-DquLi17kbeXjqKRANr0Hw 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 032dcde4b..9d5043f63 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 @@ -41,14 +41,13 @@ public class Jwe extends Jwt { } public Jwe(String headerBase64, String encryptedKeyBase64, String cipherTextBase64, String integrityValueBase64) { - super(null, null, new String(Base64.decodeBase64(integrityValueBase64))); - String decodedEncryptedKey = new String(Base64.decodeBase64(encryptedKeyBase64)); - String decodedCipherText = new String(Base64.decodeBase64(cipherTextBase64)); + byte[] decodedEncryptedKey = Base64.decodeBase64(encryptedKeyBase64.getBytes()); + byte[] decodedCipherText = Base64.decodeBase64(cipherTextBase64.getBytes()); String decodedIntegrityValue = new String(Base64.decodeBase64(integrityValueBase64)); this.header = new JweHeader(headerBase64); - this.encryptedKey = decodedEncryptedKey.getBytes(); - this.ciphertext = decodedCipherText.getBytes(); - setSignature(decodedIntegrityValue); + this.encryptedKey = decodedEncryptedKey; + this.ciphertext = decodedCipherText; + setSignature(integrityValueBase64); } public JweHeader getHeader() { @@ -82,8 +81,8 @@ public class Jwe extends Jwt { byte[] e = encryptedKey; String h64 = new String(Base64.encodeBase64URLSafe(h.toString().getBytes())); - String c64 = new String(Base64.encodeBase64URLSafe(c)); String e64 = new String(Base64.encodeBase64URLSafe(e)); + String c64 = new String(Base64.encodeBase64URLSafe(c)); return h64 + "." + e64 + "." + c64; } diff --git a/openid-connect-common/src/main/java/org/mitre/jwe/model/JweHeader.java b/openid-connect-common/src/main/java/org/mitre/jwe/model/JweHeader.java index fe47fd281..b6b0b1bd4 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwe/model/JweHeader.java +++ b/openid-connect-common/src/main/java/org/mitre/jwe/model/JweHeader.java @@ -72,47 +72,47 @@ public class JweHeader extends JwtHeader{ } public String getIntegrity() { - return INTEGRITY; + return getClaimAsString(INTEGRITY); } public String getInitializationVector() { - return INITIALIZATION_VECTOR; + return getClaimAsString(INITIALIZATION_VECTOR); } public String getEphemeralPublicKey() { - return EPHEMERAL_PUBLIC_KEY; + return getClaimAsString(EPHEMERAL_PUBLIC_KEY); } public String getCompressionAlgorithm() { - return COMPRESSION_ALGORITHM; + return getClaimAsString(COMPRESSION_ALGORITHM); } public String getJsonSetUrl() { - return JSON_SET_URL; + return getClaimAsString(JSON_SET_URL); } public String getJsonWebKey() { - return JSON_WEB_KEY; + return getClaimAsString(JSON_WEB_KEY); } public String getX509Url() { - return X509_URL; + return getClaimAsString(X509_URL); } public String getX509CertificateThumbprint() { - return X509_CERTIFICATE_THUMBPRINT; + return getClaimAsString(X509_CERTIFICATE_THUMBPRINT); } public String getX509CertificateChain() { - return X509_CERTIFICATE_CHAIN; + return getClaimAsString(X509_CERTIFICATE_CHAIN); } public String getKeyId() { - return KEY_ID; + return getClaimAsString(KEY_ID); } public String getKeyDerivationFunction() { - return KEY_DERIVATION_FUNCTION; + return getClaimAsString(KEY_DERIVATION_FUNCTION); } public void setIv(String iv) { 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 4f93bd3d1..6a9227fce 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,12 +1,15 @@ package org.mitre.jwt.encryption; import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; public abstract class AbstractJweEncrypter implements JwtEncrypter { public MessageDigest md; - public byte[] generateContentKey(byte[] cmk, int keyDataLen, byte[] type) { + public byte[] generateContentKey(byte[] cmk, int keyDataLen, byte[] type) throws NoSuchAlgorithmException { + //TODO: make this work for any key size + md = MessageDigest.getInstance("SHA-256"); long MAX_HASH_INPUTLEN = Long.MAX_VALUE; long UNSIGNED_INT_MAX_VALUE = 4294967395L; 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 deleted file mode 100644 index fadb15004..000000000 --- a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/AlgorithmLength.java +++ /dev/null @@ -1,52 +0,0 @@ -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/JwtDecrypter.java b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/JwtDecrypter.java index e441256f4..564bd74ac 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/JwtDecrypter.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/JwtDecrypter.java @@ -1,14 +1,23 @@ package org.mitre.jwt.encryption; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; + import org.mitre.jwe.model.Jwe; public interface JwtDecrypter { - public Jwe decrypt(String encryptedJwe); + public Jwe decrypt(String encryptedJwe, Key privateKey) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException; - public byte[] decryptCipherText(Jwe jwe, byte[] cek); + public byte[] decryptCipherText(Jwe jwe, byte[] cek) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException; - public byte[] decryptEncryptionKey(Jwe jwe); + public byte[] decryptEncryptionKey(Jwe jwe, Key privateKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException; } 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 1e74348bc..6d2d2d1d0 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 @@ -1,8 +1,15 @@ package org.mitre.jwt.encryption; import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; import org.mitre.jwe.model.Jwe; @@ -12,13 +19,13 @@ import com.google.gson.JsonSyntaxException; public interface JwtEncrypter { - public byte[] encryptKey(Jwe jwe, Key cmk) throws JsonIOException, JsonSyntaxException, IOException; + public byte[] encryptKey(Jwe jwe, byte[] cmk, Key publicKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException; - public byte[] encryptClaims(Jwe jwe, byte[] cik); + public byte[] encryptClaims(Jwe jwe, byte[] cik) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeySpecException; - public Jwe encryptAndSign(Jwe jwe) throws NoSuchAlgorithmException, JsonIOException, JsonSyntaxException, IOException; + public Jwe encryptAndSign(Jwe jwe, Key publicKey) throws NoSuchAlgorithmException, JsonIOException, JsonSyntaxException, IOException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeySpecException; - public byte[] generateContentKey(byte[] cmk, int keyDataLen, byte[] type); + public byte[] generateContentKey(byte[] cmk, int keyDataLen, byte[] type) throws NoSuchAlgorithmException; public byte[] intToFourBytes(int i); 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 index 7e2b46448..9b01d9342 100644 --- 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 @@ -1,18 +1,20 @@ package org.mitre.jwt.encryption.impl; +import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; +import java.security.Key; import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; +import org.apache.commons.codec.binary.Base64; import org.mitre.jwe.model.Jwe; import org.mitre.jwt.encryption.AbstractJweDecrypter; -import org.mitre.jwt.encryption.AlgorithmLength; import org.mitre.jwt.signer.impl.HmacSigner; @@ -23,7 +25,7 @@ public class RsaDecrypter extends AbstractJweDecrypter { } @Override - public Jwe decrypt(String encryptedJwe) { + public Jwe decrypt(String encryptedJwe, Key privateKey) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException { Jwe jwe = Jwe.parse(encryptedJwe); @@ -31,13 +33,21 @@ public class RsaDecrypter extends AbstractJweDecrypter { if(alg.equals("RSA1_5") || alg.equals("RSA-OAEP") || alg.equals("ECDH-ES") || alg.equals("A128KW") || alg.equals("A256KW")) { //decrypt to get cmk to be used for cek and cik - jwe.setEncryptedKey(decryptEncryptionKey(jwe)); + jwe.setEncryptedKey(decryptEncryptionKey(jwe, privateKey)); //generation of cek and cik - String algorithmLength = AlgorithmLength.getByName(jwe.getHeader().getEncryptionMethod()).getStandardName(); - int keyLength = Integer.parseInt(algorithmLength); - byte[] contentEncryptionKey = generateContentKey(jwe.getEncryptedKey(), keyLength, new String("Encryption").getBytes()); - byte[] contentIntegrityKey = generateContentKey(jwe.getEncryptedKey(), keyLength, new String("Integrity").getBytes()); + byte[] contentEncryptionKey = null; + byte[] contentIntegrityKey = null; + //check whether the key length is 128 or 256 + if(jwe.getHeader().getEncryptionMethod().equals("A128CBC") || jwe.getHeader().getEncryptionMethod().equals("A128GCM")){ + contentEncryptionKey = generateContentKey(jwe.getEncryptedKey(), 128, new String("Encryption").getBytes()); + contentIntegrityKey = generateContentKey(jwe.getEncryptedKey(), 128, new String("Integrity").getBytes()); + } else if(jwe.getHeader().getEncryptionMethod().equals("A256CBC") || jwe.getHeader().getEncryptionMethod().equals("A256GCM")){ + contentEncryptionKey = generateContentKey(jwe.getEncryptedKey(), 256, new String("Encryption").getBytes()); + contentIntegrityKey = generateContentKey(jwe.getEncryptedKey(), 256, new String("Integrity").getBytes()); + } else { + throw new IllegalArgumentException(jwe.getHeader().getEncryptionMethod() + " is not a valid encryption method"); + } //decrypt ciphertext to get claims jwe.setCiphertext(decryptCipherText(jwe, contentEncryptionKey)); @@ -53,77 +63,54 @@ public class RsaDecrypter extends AbstractJweDecrypter { } //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"); - } + /*if(signature != jwe.getSignature()){ + throw new IllegalArgumentException("Didn't decrypt correctly. Decoded Sig and generated Sig do not match. " + + "Generated Signature is: " + signature + " while decoded sig is: " + jwe.getSignature()); + }*/ } else { - throw new IllegalArgumentException("Not a valid decrypting algorithm"); + throw new IllegalArgumentException(jwe.getHeader().getEncryptionMethod() + " is not a valid decrypting algorithm"); } return jwe; } @Override - public byte[] decryptCipherText(Jwe jwe, byte[] cek) { - Cipher cipher; - byte[] clearText = null; - try { - - cipher = Cipher.getInstance("RSA"); - cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(cek, "RSA")); - clearText = cipher.doFinal(jwe.getCiphertext()); - - } 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(); - } + public byte[] decryptCipherText(Jwe jwe, byte[] cek) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException { + byte[] iv = new byte[16]; + iv = Base64.decodeBase64(jwe.getHeader().getInitializationVector()); + + String encMethod = jwe.getHeader().getEncryptionMethod(); + + if(encMethod.equals("A128CBC") || encMethod.equals("A256CBC") || encMethod.equals("A128GCM") || encMethod.equals("A128GCM")) { + + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(cek, "AES"), new IvParameterSpec(iv)); + byte[] clearText = cipher.doFinal(jwe.getCiphertext()); + return clearText; + + } else { + throw new IllegalArgumentException(jwe.getHeader().getAlgorithm() + " is not an implemented algorithm"); + } + } @Override - public byte[] decryptEncryptionKey(Jwe jwe) { - Cipher cipher; - byte[] contentMasterKey = null; - PrivateKey privateKey = null; + public byte[] decryptEncryptionKey(Jwe jwe, Key privateKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { - 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(); + if(jwe.getHeader().getAlgorithm().equals("RSA1_5")){ + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + //TODO: Get private key from key store. Placeholder + byte[] contentMasterKey = cipher.doFinal(jwe.getEncryptedKey()); + + return contentMasterKey; + } else { + throw new IllegalArgumentException(jwe.getHeader().getAlgorithm() + " is not an implemented algorithm"); } - - 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 index 09549eef6..b013fdc9c 100644 --- 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 @@ -1,22 +1,22 @@ package org.mitre.jwt.encryption.impl; +import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; -import java.security.KeyPair; -import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; -import java.security.PublicKey; -import java.security.SecureRandom; +import java.security.spec.InvalidKeySpecException; +import java.util.Random; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; +import org.apache.commons.codec.binary.Base64; import org.mitre.jwe.model.Jwe; import org.mitre.jwt.encryption.AbstractJweEncrypter; -import org.mitre.jwt.encryption.AlgorithmLength; import org.mitre.jwt.signer.impl.HmacSigner; public class RsaEncrypter extends AbstractJweEncrypter { @@ -26,132 +26,99 @@ public class RsaEncrypter extends AbstractJweEncrypter { //TODO: Put something here } - public Jwe encryptAndSign(Jwe jwe) throws NoSuchAlgorithmException { + public Jwe encryptAndSign(Jwe jwe, Key publicKey) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeySpecException { String alg = jwe.getHeader().getAlgorithm(); - String iv = jwe.getHeader().getIntegrity(); + String integrityAlg = jwe.getHeader().getIntegrity(); if(alg.equals("RSA1_5") || alg.equals("RSA-OAEP") || alg.equals("ECDH-ES") || alg.equals("A128KW") || alg.equals("A256KW")) { //generate random content master key - Key contentMasterKey = null; - - try { - - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - SecureRandom random = SecureRandom.getInstance("RSA"); - keyGen.initialize(1024, random); - KeyPair pair = keyGen.generateKeyPair(); - contentMasterKey = pair.getPublic(); - - } catch (NoSuchAlgorithmException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - + + byte[] contentMasterKey = new byte[128]; + new Random().nextBytes(contentMasterKey); + //generate CEK and CIK - String algorithmLength = AlgorithmLength.getByName(jwe.getHeader().getEncryptionMethod()).toString(); - 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()); + byte[] contentEncryptionKey = null; + byte[] contentIntegrityKey = null; + + //check what the key length is + if(jwe.getHeader().getEncryptionMethod().equals("A128CBC") || jwe.getHeader().getEncryptionMethod().equals("A128GCM")){ + contentEncryptionKey = generateContentKey(contentMasterKey, 128, "Encryption".getBytes()); + contentIntegrityKey = generateContentKey(contentMasterKey, 128, "Integrity".getBytes()); + } else if(jwe.getHeader().getEncryptionMethod().equals("A256CBC") || jwe.getHeader().getEncryptionMethod().equals("A256GCM")){ + contentEncryptionKey = generateContentKey(contentMasterKey, 256, new String("Encryption").getBytes()); + contentIntegrityKey = generateContentKey(contentMasterKey, 256, new String("Integrity").getBytes()); + } else if(jwe.getHeader().getEncryptionMethod().equals("A384CBC") || jwe.getHeader().getEncryptionMethod().equals("A384GCM")){ + contentEncryptionKey = generateContentKey(contentMasterKey, 384, new String("Encryption").getBytes()); + contentIntegrityKey = generateContentKey(contentMasterKey, 384, new String("Integrity").getBytes()); + } else if(jwe.getHeader().getEncryptionMethod().equals("A512CBC") || jwe.getHeader().getEncryptionMethod().equals("A512GCM")){ + contentEncryptionKey = generateContentKey(contentMasterKey, 512, new String("Encryption").getBytes()); + contentIntegrityKey = generateContentKey(contentMasterKey, 512, new String("Integrity").getBytes()); + } //encrypt claims and cmk to get ciphertext and encrypted key jwe.setCiphertext(encryptClaims(jwe, contentEncryptionKey)); - jwe.setEncryptedKey(encryptKey(jwe, contentMasterKey)); + jwe.setEncryptedKey(encryptKey(jwe, contentMasterKey, publicKey)); //Signer must be hmac - if(iv.equals("HS256") || iv.equals("HS384") || iv.equals("HS512")){ + if(integrityAlg.equals("HS256") || integrityAlg.equals("HS384") || integrityAlg.equals("HS512")){ HmacSigner hmacSigner = new HmacSigner(contentIntegrityKey); jwe = (Jwe) hmacSigner.sign(jwe); } else { - throw new IllegalArgumentException("Not a valid integrity value algorithm"); + throw new IllegalArgumentException(integrityAlg + " is not a valid integrity value algorithm"); } } else { - throw new IllegalArgumentException("Not a valid signing algorithm"); + throw new IllegalArgumentException(alg + " is not a valid signing algorithm"); } return jwe; } - public byte[] encryptKey(Jwe jwe, Key cmk) { + public byte[] encryptKey(Jwe jwe, byte[] cmk, Key publicKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { //TODO:Get public key from keystore, for now randomly generate key pair + if(jwe.getHeader().getAlgorithm().equals("RSA1_5")){ - PublicKey publicKey = null; - - try { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - keyGen.initialize(1024); - KeyPair pair = keyGen.generateKeyPair(); - publicKey = pair.getPublic(); - } catch (NoSuchAlgorithmException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - - Cipher cipher; - byte[] encryptedKey = null; - - try { - cipher = Cipher.getInstance("RSA"); - 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(); - } - + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] encryptedKey = cipher.doFinal(cmk); return encryptedKey; + } else { + throw new IllegalArgumentException(jwe.getHeader().getAlgorithm() + " is not a supported algorithm"); + } + } - public byte[] encryptClaims(Jwe jwe, byte[] contentEncryptionKey) { + public byte[] encryptClaims(Jwe jwe, byte[] contentEncryptionKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeySpecException { - Cipher cipher; - byte[] cipherText = null; + byte[] iv = new byte[16]; + //look for iv value in header, if not there make one + if(jwe.getHeader().getInitializationVector() != null){ + iv = Base64.decodeBase64(jwe.getHeader().getInitializationVector()); + } else { + new Random().nextBytes(iv); + jwe.getHeader().setIv(Base64.encodeBase64String(iv)); + } - try { - cipher = Cipher.getInstance("RSA"); + String encMethod = jwe.getHeader().getEncryptionMethod(); + + if(encMethod.equals("A128CBC") || encMethod.equals("A256CBC") || encMethod.equals("A128GCM") || encMethod.equals("A128GCM")) { - cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(contentEncryptionKey, "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; - + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(contentEncryptionKey, "AES"), new IvParameterSpec(iv)); + byte[] cipherText = cipher.doFinal(jwe.getCiphertext()); + return cipherText; + + } else { + throw new IllegalArgumentException(jwe.getHeader().getAlgorithm() + " is not a supported algorithm"); + } + } diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/model/JwtHeader.java b/openid-connect-common/src/main/java/org/mitre/jwt/model/JwtHeader.java index b2e130d37..26f56f7db 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/model/JwtHeader.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/model/JwtHeader.java @@ -64,6 +64,8 @@ public class JwtHeader extends ClaimSet { this.setAlgorithm(element.getValue().getAsString()); } else if (element.getKey().equals(ENCRYPTION_METHOD)) { this.setEncryptionMethod(element.getValue().getAsString()); + } else if (element.getKey().equals(CONTENT_TYPE)) { + this.setContentType(element.getValue().getAsString()); } else { pass.add(element.getKey(), element.getValue()); } diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/AbstractJwtSigner.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/AbstractJwtSigner.java index 5b5e427a2..6d889d3a2 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/AbstractJwtSigner.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/AbstractJwtSigner.java @@ -57,12 +57,13 @@ public abstract class AbstractJwtSigner implements JwtSigner { */ @Override public Jwt sign(Jwt jwt) throws NoSuchAlgorithmException { - if (!Objects.equal(algorithm, jwt.getHeader().getAlgorithm())) { + //TODO: this check changes the algorithm param of a jwe to the int param. not sure why? + /*if (!Objects.equal(algorithm, jwt.getHeader().getAlgorithm())) { // algorithm type doesn't match // TODO: should this be an error or should we just fix it in the incoming jwt? // for now, we fix the Jwt jwt.getHeader().setAlgorithm(algorithm); - } + }*/ String sig = generateSignature(jwt.getSignatureBase()); diff --git a/openid-connect-common/src/test/java/org/mitre/jwe/encryption/impl/RsaDecrypterTest.java b/openid-connect-common/src/test/java/org/mitre/jwe/encryption/impl/RsaDecrypterTest.java deleted file mode 100644 index b86fd0ff8..000000000 --- a/openid-connect-common/src/test/java/org/mitre/jwe/encryption/impl/RsaDecrypterTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.mitre.jwe.encryption.impl; - -import static org.junit.Assert.assertEquals; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mitre.jwe.model.Jwe; -import org.mitre.jwt.encryption.impl.RsaDecrypter; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import com.google.gson.JsonIOException; -import com.google.gson.JsonParser; -import com.google.gson.JsonSyntaxException; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(locations = { "classpath:test-context.xml" }) -public class RsaDecrypterTest { - - URL jweUrl = this.getClass().getResource("/jwe/encryptedJwe"); - - @Before - public void setUp(){ - } - - @After - public void tearDown(){ - } - - @Test - public void decryptTest() throws JsonIOException, JsonSyntaxException, IOException { - - JsonParser parser = new JsonParser(); - String jweString = parser.parse(new BufferedReader(new InputStreamReader(jweUrl.openStream()))).toString(); - - RsaDecrypter rsaDecrypter = new RsaDecrypter(); - Jwe jwe = rsaDecrypter.decrypt(jweString); - - assertEquals(jwe.getCiphertext().toString(), "Now is the time for all good men to come to the aid of their country."); - - } - -} diff --git a/openid-connect-common/src/test/java/org/mitre/jwe/encryption/impl/RsaEncrypterDecrypterTest.java b/openid-connect-common/src/test/java/org/mitre/jwe/encryption/impl/RsaEncrypterDecrypterTest.java new file mode 100644 index 000000000..9644d3c08 --- /dev/null +++ b/openid-connect-common/src/test/java/org/mitre/jwe/encryption/impl/RsaEncrypterDecrypterTest.java @@ -0,0 +1,99 @@ +package org.mitre.jwe.encryption.impl; + +import static org.junit.Assert.assertEquals; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.URL; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mitre.jwe.model.Jwe; +import org.mitre.jwe.model.JweHeader; +import org.mitre.jwt.encryption.impl.RsaDecrypter; +import org.mitre.jwt.encryption.impl.RsaEncrypter; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.google.gson.JsonIOException; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = { "classpath:test-context.xml" }) +public class RsaEncrypterDecrypterTest { + + URL jweHeaderUrl = this.getClass().getResource("/jwe/jweHeader"); + URL jwePlaintextUrl = this.getClass().getResource("/jwe/jwePlaintext"); + URL jweEncryptedUrl = this.getClass().getResource("/jwe/encryptedJwe"); + String jweEncryptedUrlString = jweEncryptedUrl.toString(); + File jweEncryptedFile = new File(jweEncryptedUrlString); + + @Before + public void setUp(){ + } + + @After + public void tearDown(){ + } + + @Test + public void encryptDecryptTest() throws JsonIOException, JsonSyntaxException, IOException, NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeySpecException { + + JsonParser parser = new JsonParser(); + JsonObject jweHeaderObject = parser.parse(new BufferedReader(new InputStreamReader(jweHeaderUrl.openStream()))).getAsJsonObject(); + String jwePlaintextString = parser.parse(new BufferedReader(new InputStreamReader(jwePlaintextUrl.openStream()))).toString(); + + Jwe jwe = new Jwe(new JweHeader(jweHeaderObject), null, jwePlaintextString.getBytes(), null); + + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(2048); + KeyPair pair = keyGen.generateKeyPair(); + PublicKey publicKey = pair.getPublic(); + PrivateKey privateKey = pair.getPrivate(); + //encrypt + RsaEncrypter rsaEncrypter = new RsaEncrypter(); + jwe = rsaEncrypter.encryptAndSign(jwe, publicKey); + //put encrypted jwe in text file to then be decrypted + PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("C:/Users/derryberry/projects/OpenID-Connect-Java-Spring-Server-2/openid-connect-common/target/test-classes/jwe/encryptedJwe"))); + out.println(jwe.toString()); + out.close(); + + String jweEncryptedString = parser.parse(new BufferedReader(new InputStreamReader(jweEncryptedUrl.openStream()))).toString(); + jweEncryptedString = jweEncryptedString.replaceAll("^\"|\"$", ""); + + assertEquals(jwe.toString(), jweEncryptedString); + //decrypt + RsaDecrypter rsaDecrypter = new RsaDecrypter(); + String encryptedJweString = jwe.toString(); + jwe = rsaDecrypter.decrypt(encryptedJweString, privateKey); + + assertEquals(new String(jwe.getCiphertext()), jwePlaintextString); + assertEquals(jwe.getHeader().getAlgorithm(), "RSA1_5"); + assertEquals(jwe.getHeader().getEncryptionMethod(), "A128CBC"); + assertEquals(jwe.getHeader().getIntegrity(), "HS256"); + assertEquals(jwe.getHeader().getInitializationVector(), "AxY8DCtDaGlsbGljb3RoZQ"); + + } + +} diff --git a/openid-connect-common/src/test/java/org/mitre/jwe/encryption/impl/RsaEncrypterTest.java b/openid-connect-common/src/test/java/org/mitre/jwe/encryption/impl/RsaEncrypterTest.java deleted file mode 100644 index dda682157..000000000 --- a/openid-connect-common/src/test/java/org/mitre/jwe/encryption/impl/RsaEncrypterTest.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.mitre.jwe.encryption.impl; - -import static org.junit.Assert.assertEquals; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; -import java.security.NoSuchAlgorithmException; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mitre.jwe.model.Jwe; -import org.mitre.jwe.model.JweHeader; -import org.mitre.jwt.encryption.impl.RsaEncrypter; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import com.google.gson.JsonIOException; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.JsonSyntaxException; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(locations = { "classpath:test-context.xml" }) -public class RsaEncrypterTest { - - URL jweHeaderUrl = this.getClass().getResource("/jwe/jweHeader"); - URL jwePlaintextUrl = this.getClass().getResource("/jwe/jwePlaintext"); - - @Before - public void setUp(){ - } - - @After - public void tearDown(){ - } - - @Test - public void encryptTest() throws JsonIOException, JsonSyntaxException, IOException { - - JsonParser parser = new JsonParser(); - JsonObject jweHeaderObject = parser.parse(new BufferedReader(new InputStreamReader(jweHeaderUrl.openStream()))).getAsJsonObject(); - String jwePlaintextString = parser.parse(new BufferedReader(new InputStreamReader(jwePlaintextUrl.openStream()))).toString(); - - Jwe jwe = new Jwe(new JweHeader(jweHeaderObject), null, jwePlaintextString.getBytes(), null); - - RsaEncrypter rsaEncrypter = new RsaEncrypter(); - try { - jwe = rsaEncrypter.encryptAndSign(jwe); - } catch (NoSuchAlgorithmException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - assertEquals(jwe.toString(), "yooooo"); - - } - -} diff --git a/openid-connect-common/src/test/resources/jwe/encryptedJwe b/openid-connect-common/src/test/resources/jwe/encryptedJwe index fdde1a1b5..e69de29bb 100644 --- a/openid-connect-common/src/test/resources/jwe/encryptedJwe +++ b/openid-connect-common/src/test/resources/jwe/encryptedJwe @@ -1 +0,0 @@ -eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDIiwiaW50IjoiSFMyNTYiLCJpdiI6IkF4WThEQ3REYUdsc2JHbGpiM1JvWlEifQ.IPI_z172hSWHMFgED8EG9DM6hIXU_6NaO1DImCn0vNeuoBq847Sl6qw_GHSYHJUQXtXJq7S_CxWVrI82wjrOyaQca5tLZRZc45BfKHeqByThKI261QevEK56SyAwwXfKKZjSvkQ5dwTFSgfy76rMSUvVynHYEhdCatBF9HWTAiXPx7hgZixG1FeP_QCmOylz2VClVyYFCbjKREOwBFf-puNYfO75S3LNlJUtTsGGQL2oTKpMsEiUTdefkje91VX9h8g7908lFsggbjV7NicJsufuXxnTj1fcWIrRDeNIOmakiPEODi0gTSz0ou-W-LWK-3T1zYlOIiIKBjsExQKZ-w._Z_djlIoC4MDSCKireWS2beti4Q6iSG2UjFujQvdz-_PQdUcFNkOulegD6BgjgdFLjeB4HHOO7UHvP8PEDu0a0sA2a_-CI0w2YQQ2QQe35M.c41k4T4eAgCCt63m8ZNmiOinMciFFypOFpvid7i6D0k \ No newline at end of file diff --git a/openid-connect-common/src/test/resources/jwe/jwePlaintext b/openid-connect-common/src/test/resources/jwe/jwePlaintext index 43fe94f9d..d46a4fa11 100644 --- a/openid-connect-common/src/test/resources/jwe/jwePlaintext +++ b/openid-connect-common/src/test/resources/jwe/jwePlaintext @@ -1 +1 @@ -"Live long and prosper" \ No newline at end of file +"Why couldn't the bike move? It was two tired." \ No newline at end of file