Skip to content

Commit

Permalink
Merge pull request Azure#40 from logachev/kiril/messagesecurity
Browse files Browse the repository at this point in the history
Fix bugs with  message protection for keys create\delete
  • Loading branch information
schaabs authored May 3, 2018
2 parents ac1aa47 + 2fdd623 commit d654a3f
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Arrays;
import java.util.List;

import com.microsoft.rest.credentials.ServiceClientCredentials;
import com.microsoft.azure.keyvault.messagesecurity.HttpMessageSecurity;
Expand All @@ -31,6 +33,7 @@ public abstract class KeyVaultCredentials implements ServiceClientCredentials {

private static final String WWW_AUTHENTICATE = "WWW-Authenticate";
private static final String BEARER_TOKEP_REFIX = "Bearer ";
private List<String> supportedMethods = Arrays.asList("sign", "verify", "encrypt", "decrypt", "wrapkey", "unwrapkey");

private final ChallengeCache cache = new ChallengeCache();

Expand Down Expand Up @@ -88,7 +91,9 @@ public Response intercept(Chain chain) throws IOException {
* @return Pair of protected request and HttpMessageSecurity used for encryption.
*/
private Pair<Request, HttpMessageSecurity> buildAuthenticatedRequest(Request originalRequest, Map<String, String> challengeMap) throws IOException{
AuthenticationResult authResult = getAuthenticationCredentials(challengeMap);

Boolean supportsPop = supportsMessageProtection(originalRequest.url().toString(), challengeMap);
AuthenticationResult authResult = getAuthenticationCredentials(supportsPop, challengeMap);

if (authResult == null) {
return null;
Expand All @@ -97,9 +102,9 @@ private Pair<Request, HttpMessageSecurity> buildAuthenticatedRequest(Request ori
HttpMessageSecurity httpMessageSecurity =
new HttpMessageSecurity(
authResult.getAuthToken(),
authResult.getPopKey(),
challengeMap.get("x-ms-message-encryption-key"),
challengeMap.get("x-ms-message-signing-key"));
supportsPop ? authResult.getPopKey() : "",
supportsPop ? challengeMap.get("x-ms-message-encryption-key") : "",
supportsPop ? challengeMap.get("x-ms-message-signing-key") : "");

Request request = httpMessageSecurity.protectRequest(originalRequest);
return Pair.of(request, httpMessageSecurity);
Expand Down Expand Up @@ -136,7 +141,7 @@ private Pair<Request, HttpMessageSecurity> buildAuthenticatedRequest(Request ori
* @return request with removed body.
*/
private Request buildEmptyRequest(Request request){
RequestBody body = RequestBody.create(MediaType.parse("application/jose+json"), "{}");
RequestBody body = RequestBody.create(MediaType.parse("application/json"), "{}");
if (request.method().equalsIgnoreCase("get")){
return request;
}
Expand All @@ -145,15 +150,41 @@ private Request buildEmptyRequest(Request request){
}
}

/**
* Checks if resource supports message protection.
*
* @param url
* resource url.
* @param challengeMap
* the challenge map.
* @return true if message protection is supported.
*/
private Boolean supportsMessageProtection(String url, Map<String, String> challengeMap) {

if (!"true".equals(challengeMap.get("supportspop"))){
return false;
}

// Message protection is enabled only for subset of keys operations.
if (!url.toLowerCase().contains("/keys/")){
return false;
}

String[] tokens = url.split("\\?")[0].split("/");
return supportedMethods.contains(tokens[tokens.length - 1]);
}

/**
* Extracts the authentication challenges from the challenge map and calls
* the authentication callback to get the bearer token and return it.
*
* @param supportsPop
* is resource supports pop authentication.
* @param challengeMap
* the challenge map.
* @return AuthenticationResult with bearer token and PoP key.
*/
private AuthenticationResult getAuthenticationCredentials(Map<String, String> challengeMap) {
private AuthenticationResult getAuthenticationCredentials(Boolean supportsPop, Map<String, String> challengeMap) {

String authorization = challengeMap.get("authorization");
if (authorization == null) {
Expand All @@ -162,7 +193,7 @@ private AuthenticationResult getAuthenticationCredentials(Map<String, String> ch

String resource = challengeMap.get("resource");
String scope = challengeMap.get("scope");
String schema = "true".equals(challengeMap.get("supportspop")) ? "pop" : "bearer";
String schema = supportsPop ? "pop" : "bearer";
return doAuthenticate(authorization, resource, scope, schema);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import okhttp3.internal.http.HttpHeaders;

import okio.Buffer;

Expand Down Expand Up @@ -55,6 +56,7 @@ public class HttpMessageSecurity {
* string with server signing key (public only) or null if not supported
*/
public HttpMessageSecurity(String _clientSecurityToken, String _clientSignatureKeyString, String _serverEncryptionKeyString, String _serverSignatureKeyString) throws IOException{

this.clientSecurityToken = _clientSecurityToken;

if (_clientSignatureKeyString != null && !_clientSignatureKeyString.equals("")){
Expand Down Expand Up @@ -115,6 +117,10 @@ public Request protectRequest(Request request) throws IOException {
request.body().writeTo(buffer);
String currentbody = buffer.readUtf8();

if (currentbody == null || currentbody.length() == 0){
return result;
}

JsonWebKey clientPublicEncryptionKey = MessageSecurityHelper.GetJwkWithPublicKeyOnly(clientEncryptionKey);

String payload = currentbody.substring(0, currentbody.length() - 1) + ",\"rek\":{\"jwk\":" + clientPublicEncryptionKey.toString() + "}}";
Expand Down Expand Up @@ -165,8 +171,13 @@ public Request protectRequest(Request request) throws IOException {
*/
public Response unprotectResponse(Response response) throws IOException{
try{
if (!supportsProtection())
if (!supportsProtection() || !HttpHeaders.hasBody(response)){
return response;
}

if (!response.header("content-type").toLowerCase().contains("application/jose+json")){
return response;
}

JWSObject jwsObject = JWSObject.deserialize(response.body().string());
JWSHeader jwsHeader = jwsObject.jwsHeader();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,13 @@ private Response getResponse(){
String responseBodyString = new String(Base64.decodeBase64("eyJwcm90ZWN0ZWQiOiJleUpoYkdjaU9pSlNVekkxTmlJc0ltdHBaQ0k2SWpSaU9EWTBaR0ptTFRZMFpHUXRORFZqWVMwNFlqWTVMV05tWldNd05EVTJOR0kxTUNJc0ltRjBJam9pVkc5clpXNGlMQ0owY3lJNk1Dd2ljQ0k2Ym5Wc2JDd2lkSGx3SWpvaVVHOVFJbjAiLCJwYXlsb2FkIjoiZXlKd2NtOTBaV04wWldRaU9pSmxlVXBvWWtkamFVOXBTbE5WTUVWMFZEQkdSbFZEU1hOSmJYUndXa05KTmtscVdUQlBWRmt4V2tkT2FVeFVTbXhPYWxGMFRrZEZlVTVETVdsWlYwMTNURmRSTVZscVl6QlpNa2w0V21wa2FrNURTWE5KYlZaMVdYbEpOa2xyUlhoTmFtaEVVV3ROZEZOR1RYbE9WRmxwWmxFaUxDSmxibU55ZVhCMFpXUmZhMlY1SWpvaVFsVnNkVEJIWDJObldVSm9iVFYzUm5WaGEzZHViekEwUkZVd1kxTkRYemxVWjNwRGVsVlNRMWd5Y1VkdWRqRldaRTFRU0RVelpFcE1TbkZJT0ROc2IyWmplVTVSV0hGWWEwRlZkbVUxYVhVdFNGVlpXRmRwVmpOYVNsaGhWRVZRTUZKb2RHZHdaMkl3VUhGMFZtSlVibWRHU25VNVJ6Rm5VMWswTmxaTVdYaGtWek41ZGtsTFpFRmhRalV5Ums4M1MyOVlZVWw2T1ROblgxOHdjMnhuV1hSTlkxSjNkRm80VlZaTVpFVjNOemxXZUY5elMybGZNbVZyVm5VeFFYaE1iV0o2ZWxKeVNYQnplblJhUWpKWWNIWlRSak16VFRoVE1XSXRWVnBSU2s5QlVqWmpaQzE0UkVsRFYzVTRWbEJLZUVaalFWSlVaVlpHT1ZCaGNqbFBSMEpKV1ZOd1NXMXdTRlpYTUVWWE4yTkVSVVl6YWpCU2NFaEVRMko1YUZoeGFtVmpUMEZsVlV4MVRtaExOa0ZLYVhwTmF6Rldha3RFVldzM09HSlVhbkpSYkZOV1ZHWmxOamxTYVdGYVJsUXRabmhtYVhCQklpd2lhWFlpT2lKV1JWWlVWa1JGZVUxNlVsVlNWazVWVFZSSmVrNUJJaXdpWTJsd2FHVnlkR1Y0ZENJNkltVnVNVXQxZURORWMzaDBNek5pT1Vwa1ZIcExlbkpEZW1GNVVtVmxhRVo2T0Y5dlZtRkxYMVpQUlhWa2NXb3pkVGt3WWxWamFqVlllV2xyVVdGSFMyUXlZa0V3T0hsaWVuaHRUalZMVFVkbVRGSjBRbWMwVGpOMFNEQmhXRGxFWHpKSk4wdFZVRk5SY25GQ1dGTkpaSFZtV0RsaFdrUnZaRTl4ZW0xMWFWRmpkWFJUVWtWVFJtRkJUa2xTWHpCS2FsUkpORmhOVW1RMk5uQXdiVFJLVjA5TlluSmZkRlJ3VDNwVmJtbEhXRzloYUdZMU1sSk5kV3MxVlRCWFNrOU9aSGRxWlhwdll6bHdWM0JLTUdSTVZVVmlhVGhQVjJzMk9IVkdkemR3TFRWc1JIcFRSbGx4WVhOQ2FsaHFXSFJLV1c1alUzRnZUbTU0Y1VFMWVtTjBSSGxpUm5NNFMzSXRWSGRCYUU1S2JYQnJUVzVpYkZoTWVrczRVMGxZWkc1alZEbHJRWEJFWVZKWFQzWkNVa014ZVdaNlFUTmhhemd6VmpaSlFUZzRRMGt5WVRWcmMzSm9lakZIWTNKWllVVkRkWEY1ZFROMFNsZ3RMVVIyWlhSRWIwSlpkbDgzWlY5eWN6RnNWMVIzV0VOdk9FdG5MVVZzTTA1VFVFTnJjM2xFT1RWWFRYRkhSVWx6Y1V4aU1XVllWazEwWVd4aE5scGZYMHQyVTFSWVRVa3pZa0pVYkRoTFJITklNME5GTW5FNWNFdGxSbXBvTFROdVVuQnNibGcwYVdWcldsUjFRbWxrVDJaT1ozQktkRk13TTFSM05uVlJRbTB0WXpaWVRrUlNTR00xTUZjMFJ6WTFOemMxWDJJMmFFZGZjbmcxYkZSV2NYUTNaamRRTkZGeU1tRnFPSE5RTUdwaVJYWk1SRkJYWlhKYVdtNXpUak55T0VscmIwcFpPVTVLT0ZSelJuRXpSemMyVDE5U1gyVnJNRlZQY0Vob2VTMDFWR3B0UW1wTWFYSm1SMGRaZW5RNGJEWkRkVGd4YzBJdExVZG1SSEYwVkRNNVQxTTJaM2RvYWsxQlUxSjRORzQwYkRCVmRFbFNiMlpCWVdNd1IzWkNhVUZrV2xRMVIzcEVYekZZYUdSdGVGSnhNbmR1Vms0eE1XaDRSeTFTTUY5ZlFYQlNVVUY1WVdwaWNGQnFhMkYxWDE5ck5qVnFibTFWYWxGcGNXbHNjM1Z4UkZkR2JVWXlja1V0VlUwMFFXRkxSVlI2TUdWdFJtd3diakZ5WVRWRlNYVkpXRXczVFVjNVgybDZVMlZ2UlRaT1lWWnRkMnRHWnpKbFpqTnRNbk0xWTFKMlRGZ3hSeUlzSW5SaFp5STZJbUZIWTAwelVrWnJOR2xQVG5KZmVGUjBURkpQV21jaWZRIiwic2lnbmF0dXJlIjoiSXYzMzZ4Z1ZMVjNjOXpVb1RIcVRPd3Z6cHg2VTBySXpYaVRqS1Fxak8zSmZsbUJWczJ1OEw0STNDVFhSbGZuU0U5Y0plSXNuazR1Nmh1ZzhhakN0eHdVczBaUjVJREtPaDlfYjJoTG5vZEhQaE9Fb1dEMTBaWEJSZFlKb2dqVDd6cXhNSkd2NC1Mdmg5aGp5TE5BLUZ5TXd0dG9QVmQxdjRfOVlNQzNGczhUTHAyZTlrRHdreUxYUXpDYXN1ZkpRSGVybW82MDFJcXFfUVVZbTZnaWJCVXFMOWI4NEk3OHNOSVRLcWxkb0tGLXVKeHcxNzJRM2NMbEZycEJIWWhDalB2OFYtZnJHOGtMOVBhXzdaRjJBME4ycTNkR1ltMDAzWGo3VllmUDBkVU1rTFRnLUppakJNUzNnMDlqVTdLck5xRjlkUF9xejhjXzFjaG1JWFhxa3VRIn0="));

ResponseBody responseBody = ResponseBody.create(MediaType.parse("application/jose+json"), responseBodyString);
return (new Response.Builder()).body(responseBody).request(getRequest()).protocol(okhttp3.Protocol.HTTP_2).code(200).build();
return (new Response.Builder())
.header("content-type", "application/jose+json")
.body(responseBody)
.request(getRequest())
.protocol(okhttp3.Protocol.HTTP_2)
.code(200)
.build();
}

private String getRequestBody(Request request) throws Exception{
Expand Down

0 comments on commit d654a3f

Please sign in to comment.