update to encryption/decryption. added cmk, cek, and cik use
parent
bc1ff0d964
commit
7e7cd4f480
|
@ -20,7 +20,6 @@ public class JweHeader extends JwtHeader{
|
||||||
public static final String X509_CERTIFICATE_CHAIN = "x5c";
|
public static final String X509_CERTIFICATE_CHAIN = "x5c";
|
||||||
public static final String KEY_ID = "kid";
|
public static final String KEY_ID = "kid";
|
||||||
public static final String KEY_DERIVATION_FUNCTION = "kdf";
|
public static final String KEY_DERIVATION_FUNCTION = "kdf";
|
||||||
public static final String CONTENT_TYPE = "cty";
|
|
||||||
|
|
||||||
public JweHeader(){
|
public JweHeader(){
|
||||||
super();
|
super();
|
||||||
|
@ -112,14 +111,10 @@ public class JweHeader extends JwtHeader{
|
||||||
return KEY_ID;
|
return KEY_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getKeyDerivationFunction() {
|
public String getKeyDerivationFunction() {
|
||||||
return KEY_DERIVATION_FUNCTION;
|
return KEY_DERIVATION_FUNCTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getContentType() {
|
|
||||||
return CONTENT_TYPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIv(String iv) {
|
public void setIv(String iv) {
|
||||||
setClaim(INITIALIZATION_VECTOR, iv);
|
setClaim(INITIALIZATION_VECTOR, iv);
|
||||||
}
|
}
|
||||||
|
@ -163,8 +158,4 @@ public class JweHeader extends JwtHeader{
|
||||||
public void setKeyDerivationFunction(String kdf) {
|
public void setKeyDerivationFunction(String kdf) {
|
||||||
setClaim(KEY_DERIVATION_FUNCTION, kdf);
|
setClaim(KEY_DERIVATION_FUNCTION, kdf);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setContentType(String cty) {
|
|
||||||
setClaim(CONTENT_TYPE, cty);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.mitre.jwt.encryption;
|
package org.mitre.jwt.encryption;
|
||||||
|
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.Key;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
|
@ -39,13 +40,13 @@ public abstract class AbstractJweDecrypter implements JwtDecrypter {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String decryptCipherText(Jwe jwe) {
|
public String decryptCipherText(Jwe jwe, Key cek) {
|
||||||
Cipher cipher;
|
Cipher cipher;
|
||||||
String clearTextString = null;
|
String clearTextString = null;
|
||||||
try {
|
try {
|
||||||
|
|
||||||
cipher = Cipher.getInstance("RSA");
|
cipher = Cipher.getInstance("RSA");
|
||||||
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
cipher.init(Cipher.DECRYPT_MODE, cek);
|
||||||
byte[] clearText = cipher.doFinal(jwe.getCiphertext());
|
byte[] clearText = cipher.doFinal(jwe.getCiphertext());
|
||||||
clearTextString = new String(clearText);
|
clearTextString = new String(clearText);
|
||||||
|
|
||||||
|
@ -73,13 +74,13 @@ public abstract class AbstractJweDecrypter implements JwtDecrypter {
|
||||||
@Override
|
@Override
|
||||||
public byte[] decryptEncryptionKey(Jwe jwe) {
|
public byte[] decryptEncryptionKey(Jwe jwe) {
|
||||||
Cipher cipher;
|
Cipher cipher;
|
||||||
byte[] unencryptedKey = null;
|
byte[] contentMasterKey = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
cipher = Cipher.getInstance("RSA");
|
cipher = Cipher.getInstance("RSA");
|
||||||
cipher.init(Cipher.DECRYPT_MODE, privateKey);//TODO: Keys are null, get them from keystore. Placeholder
|
cipher.init(Cipher.DECRYPT_MODE, privateKey);//TODO: Get private key from key store. Placeholder
|
||||||
unencryptedKey = cipher.doFinal(jwe.getEncryptedKey());
|
contentMasterKey = cipher.doFinal(jwe.getEncryptedKey());
|
||||||
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
|
@ -98,6 +99,6 @@ public abstract class AbstractJweDecrypter implements JwtDecrypter {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
return unencryptedKey;
|
return contentMasterKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,23 @@
|
||||||
package org.mitre.jwt.encryption;
|
package org.mitre.jwt.encryption;
|
||||||
|
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.Key;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.KeyPairGenerator;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.interfaces.RSAPrivateKey;
|
import java.security.PublicKey;
|
||||||
|
import java.security.SecureRandom;
|
||||||
import java.security.interfaces.RSAPublicKey;
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
import java.security.spec.InvalidParameterSpecException;
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
import javax.crypto.BadPaddingException;
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
import javax.crypto.NoSuchPaddingException;
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
|
||||||
import org.mitre.jwe.model.Jwe;
|
import org.mitre.jwe.model.Jwe;
|
||||||
|
import org.springframework.security.crypto.codec.Base64;
|
||||||
|
|
||||||
|
|
||||||
public abstract class AbstractJweEncrypter implements JwtEncrypter {
|
public abstract class AbstractJweEncrypter implements JwtEncrypter {
|
||||||
|
@ -21,10 +28,6 @@ public abstract class AbstractJweEncrypter implements JwtEncrypter {
|
||||||
|
|
||||||
private RSAPublicKey publicKey;
|
private RSAPublicKey publicKey;
|
||||||
|
|
||||||
private RSAPrivateKey privateKey;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public byte[] getEncryptecKey() {
|
public byte[] getEncryptecKey() {
|
||||||
return encryptedKey;
|
return encryptedKey;
|
||||||
}
|
}
|
||||||
|
@ -44,13 +47,29 @@ public abstract class AbstractJweEncrypter implements JwtEncrypter {
|
||||||
|
|
||||||
public byte[] encryptKey(Jwe jwe){
|
public byte[] encryptKey(Jwe jwe){
|
||||||
|
|
||||||
|
//generate random content master key
|
||||||
|
PublicKey contentMasterKey = null;
|
||||||
|
|
||||||
//TODO:Get keys from keystore, currently 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;
|
Cipher cipher;
|
||||||
try {
|
try {
|
||||||
cipher = Cipher.getInstance("RSA");
|
cipher = Cipher.getInstance("RSA");
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||||
encryptedKey = cipher.doFinal(privateKey.getEncoded());
|
encryptedKey = cipher.doFinal(contentMasterKey.getEncoded());
|
||||||
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
|
@ -73,14 +92,17 @@ public abstract class AbstractJweEncrypter implements JwtEncrypter {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] encryptClaims(Jwe jwe) {
|
public byte[] encryptClaims(Jwe jwe, Key cek) {
|
||||||
|
|
||||||
|
|
||||||
//TODO:Get keys from keystore, currently null
|
|
||||||
Cipher cipher;
|
Cipher cipher;
|
||||||
try {
|
try {
|
||||||
cipher = Cipher.getInstance("RSA");
|
cipher = Cipher.getInstance("RSA");
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
|
||||||
|
//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());
|
cipherText = cipher.doFinal(jwe.getClaims().toString().getBytes());
|
||||||
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
@ -98,13 +120,16 @@ public abstract class AbstractJweEncrypter implements JwtEncrypter {
|
||||||
} catch (BadPaddingException e) {
|
} catch (BadPaddingException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
} catch (InvalidParameterSpecException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
return cipherText;
|
return cipherText;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Jwe encryptAndSign(Jwe jwe);
|
public abstract Jwe encryptAndSign(Jwe jwe) throws NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
package org.mitre.jwt.encryption;
|
package org.mitre.jwt.encryption;
|
||||||
|
|
||||||
|
import java.security.Key;
|
||||||
|
|
||||||
import org.mitre.jwe.model.Jwe;
|
import org.mitre.jwe.model.Jwe;
|
||||||
|
|
||||||
public interface JwtDecrypter {
|
public interface JwtDecrypter {
|
||||||
|
|
||||||
public Jwe decrypt(String encryptedJwe);
|
public Jwe decrypt(String encryptedJwe);
|
||||||
|
|
||||||
public String decryptCipherText(Jwe jwe);
|
public String decryptCipherText(Jwe jwe, Key cek);
|
||||||
|
|
||||||
public byte[] decryptEncryptionKey(Jwe jwe);
|
public byte[] decryptEncryptionKey(Jwe jwe);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package org.mitre.jwt.encryption;
|
package org.mitre.jwt.encryption;
|
||||||
|
|
||||||
|
import java.security.Key;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
import org.mitre.jwe.model.Jwe;
|
import org.mitre.jwe.model.Jwe;
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,8 +10,8 @@ public interface JwtEncrypter {
|
||||||
|
|
||||||
public byte[] encryptKey(Jwe jwe);
|
public byte[] encryptKey(Jwe jwe);
|
||||||
|
|
||||||
public byte[] encryptClaims(Jwe jwe);
|
public byte[] encryptClaims(Jwe jwe, Key cik);
|
||||||
|
|
||||||
public Jwe encryptAndSign(Jwe jwe);
|
public Jwe encryptAndSign(Jwe jwe) throws NoSuchAlgorithmException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
package org.mitre.jwt.encryption.impl;
|
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.apache.commons.codec.binary.Base64;
|
||||||
import org.mitre.jwe.model.Jwe;
|
import org.mitre.jwe.model.Jwe;
|
||||||
import org.mitre.jwe.model.JweHeader;
|
import org.mitre.jwe.model.JweHeader;
|
||||||
import org.mitre.jwt.encryption.AbstractJweDecrypter;
|
import org.mitre.jwt.encryption.AbstractJweDecrypter;
|
||||||
|
import org.mitre.jwt.signer.impl.HmacSigner;
|
||||||
|
|
||||||
|
|
||||||
public class Decrypter extends AbstractJweDecrypter {
|
public class Decrypter extends AbstractJweDecrypter {
|
||||||
|
@ -33,6 +40,7 @@ public class Decrypter extends AbstractJweDecrypter {
|
||||||
//Base 64 decode each part of the jwe
|
//Base 64 decode each part of the jwe
|
||||||
String decodedHeader = new String(Base64.decodeBase64(jwe.getHeader().toString()));
|
String decodedHeader = new String(Base64.decodeBase64(jwe.getHeader().toString()));
|
||||||
JweHeader unencryptedHeader = new JweHeader(decodedHeader);
|
JweHeader unencryptedHeader = new JweHeader(decodedHeader);
|
||||||
|
jwe.setHeader(unencryptedHeader);
|
||||||
|
|
||||||
String decodedEncryptionKey = new String(Base64.decodeBase64(jwe.getEncryptedKey().toString()));
|
String decodedEncryptionKey = new String(Base64.decodeBase64(jwe.getEncryptedKey().toString()));
|
||||||
//sets decoded key on jwe so that it can be decrypted
|
//sets decoded key on jwe so that it can be decrypted
|
||||||
|
@ -42,12 +50,43 @@ public class Decrypter extends AbstractJweDecrypter {
|
||||||
//sets decoded ciphertext on jwe so that it can be decrypted
|
//sets decoded ciphertext on jwe so that it can be decrypted
|
||||||
jwe.setCiphertext(decodedCiphertext.getBytes());
|
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()));
|
String decodedSig = new String(Base64.decodeBase64(jwe.getSignature()));
|
||||||
|
|
||||||
|
|
||||||
//create new jwe using the decoded header and signature, and decrypt the ciphertext and key
|
PrivateKey contentEncryptionKey = null;
|
||||||
jwe.setHeader(unencryptedHeader);
|
PublicKey contentIntegrityKey = null;
|
||||||
jwe.setCiphertext(decryptCipherText(jwe).getBytes());
|
|
||||||
|
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));
|
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);
|
jwe.setSignature(decodedSig);
|
||||||
|
|
||||||
} else if(alg.equals("HS256") || alg.equals("HS384") || alg.equals("HS512")){
|
} else if(alg.equals("HS256") || alg.equals("HS384") || alg.equals("HS512")){
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package org.mitre.jwt.encryption.impl;
|
package org.mitre.jwt.encryption.impl;
|
||||||
|
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.KeyPairGenerator;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
import org.mitre.jwe.model.Jwe;
|
import org.mitre.jwe.model.Jwe;
|
||||||
import org.mitre.jwe.model.JweHeader;
|
import org.mitre.jwe.model.JweHeader;
|
||||||
|
@ -59,25 +63,38 @@ public class Encrypter extends AbstractJweEncrypter {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Jwe encryptAndSign(Jwe jwe) {
|
public Jwe encryptAndSign(Jwe jwe) throws NoSuchAlgorithmException {
|
||||||
|
|
||||||
String alg = jwe.getHeader().getAlgorithm();
|
String alg = jwe.getHeader().getAlgorithm();
|
||||||
String iv = jwe.getHeader().getIntegrity();
|
String iv = jwe.getHeader().getIntegrity();
|
||||||
|
|
||||||
if(alg.equals("RS256") || alg.equals("RS384") || alg.equals("RS512")) {
|
if(alg.equals("RS256") || alg.equals("RS384") || alg.equals("RS512")) {
|
||||||
|
|
||||||
jwe.setCiphertext(encryptClaims(jwe));
|
//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));
|
jwe.setEncryptedKey(encryptKey(jwe));
|
||||||
|
|
||||||
if(iv.equals("HS256") || iv.equals("HS384") || iv.equals("HS512")){
|
if(iv.equals("HS256") || iv.equals("HS384") || iv.equals("HS512")){
|
||||||
|
|
||||||
HmacSigner hmacSigner = new HmacSigner(); //TODO: Add parameters to RsaSigner. ie: keys from keystore (null at the moment)
|
HmacSigner hmacSigner = new HmacSigner(contentIntegrityKey.getEncoded());
|
||||||
try {
|
jwe = (Jwe) hmacSigner.sign(jwe);
|
||||||
jwe = (Jwe) hmacSigner.sign(jwe);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
} else if(iv.equals("RS256") || iv.equals("RS384") || iv.equals("RS512")) {
|
} else if(iv.equals("RS256") || iv.equals("RS384") || iv.equals("RS512")) {
|
||||||
throw new IllegalArgumentException("Integrity Value must use Hmac signing");
|
throw new IllegalArgumentException("Integrity Value must use Hmac signing");
|
||||||
} else {
|
} else {
|
||||||
|
@ -85,9 +102,7 @@ public class Encrypter extends AbstractJweEncrypter {
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if(alg.equals("HS256") || alg.equals("HS384") || alg.equals("HS512")){
|
} else if(alg.equals("HS256") || alg.equals("HS384") || alg.equals("HS512")){
|
||||||
|
|
||||||
throw new IllegalArgumentException("Cannot use Hmac for encryption");
|
throw new IllegalArgumentException("Cannot use Hmac for encryption");
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Not a valid signing algorithm");
|
throw new IllegalArgumentException("Not a valid signing algorithm");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue