Help Center/ Cognitive Engagement Center/ API Reference/ iKBS Interface Reference/ Authentication/ Authentication Mode of Knowledge Base Interfaces
Updated on 2023-09-27 GMT+08:00

Authentication Mode of Knowledge Base Interfaces

Obtaining an AK/SK

AK/SK Authentication

In AK/SK-based authentication, an AK/SK is used to sign requests and the signature is then added to the requests for authentication.

  • AK: ID of the access key. It is a unique identifier that is associated with a secret access key. The AK and SK are used together to sign requests cryptographically.

  • SK: secret access key that works with an AK to obtain an encrypted signature for a request, identify the sender, and prevent the request from being modified.

In AK/SK-based authentication, you can use an AK/SK to sign requests based on the signature algorithm.

Obtaining the Private AK/SK

  • Integrated environment: Contact operations personnel to obtain it.

If the interface authentication is successful, the HTTP response code 200 is returned. If the authentication fails, the HTTP response code 401 is returned.

Authentication Algorithm

Assume that the CC-CMS interface is used as an example to describe the authentication algorithm. The authentication algorithms of the CC-iKBS, CC-FS, and CC-iSales are similar.

All interface servers authenticate the signature of the request data sent from the client. The authentication process is as follows.

The following is the rule for the invoker to generate Authorization.

Algorithm and Rule of Each Content Segment

  • SignInfo

    This class contains some fixed HEAD definitions and some basic functions used to construct CanonicalRequest and Signature.

package com.huawei.client.rest.v2.demo.sign;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeSet;

@Setter
@Getter
public class SignInfo
{
	public static final String HEAD_AUTHORIZATION = "authorization";
	public static final String HEAD_HOST = "host";
	public static final String HEAD_CONTENT_LENGTH = "Content-Length";
	public static final String HEAD_CONTENT_TYPE = "Content-Type";
	public static final String TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";

	//Retain the default setting.
	private String authVersion = "auth-v2";

	private String httpMethod;
	private String uri;

	//GET is not supported on the service side.
	private Map<String, String> queryParameters;

	private Map<String, String> signedHeaders;
	private String payload;
	private String accessKey;
	private String secretKey;
	private Date timestamp;

	public String authString() throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException
	{
		String authStringPrefix = this.authStringPrefix();
		String signingKey = SignerUtils.sha256Hex(this.getSecretKey(), authStringPrefix);
		String canonicalRequest = this.canonicalRequest();
		String signature = SignerUtils.sha256Hex(signingKey, canonicalRequest);
		String authString = authStringPrefix + '/' + signature;

		return authString;
	}

	public String authStringPrefix()
	{
		StringBuilder buffer = new StringBuilder();
		buffer.append(this.authVersion);
		buffer.append('/').append(this.accessKey);
		buffer.append('/').append(this.formatTimestamp());
		buffer.append('/');
		this.appendSignedHeaders(buffer);

		return buffer.toString();
	}

	public String canonicalRequest()
	{
		StringBuilder buffer = new StringBuilder();
		buffer.append(this.httpMethod).append('\n');
		buffer.append(this.uri).append('\n');

		if (this.isNotEmpty(this.queryParameters))
		{
			this.appendCanonicalQueryString(buffer);
			buffer.append('\n');
		}

		this.appendSignedHeaders(buffer);
		buffer.append('\n');

		this.appendCanonicalHeaders(buffer);
		buffer.append('\n');

		if (this.isNotEmpty(this.payload))
		{
			buffer.append(PathUtils.normalize(this.payload));
		}

		return buffer.toString();
	}

	private String appendSignedHeaders(StringBuilder buffer)
	{
		int start = buffer.length();

		Set<String> headerNames = new TreeSet<>(this.signedHeaders.keySet());
		for (String name : headerNames)
		{
			buffer.append(name.toLowerCase(Locale.ENGLISH)).append(';');
		}
		buffer.deleteCharAt(buffer.length() - 1);

		int end = buffer.length();
		String signedHeadersStr = buffer.substring(start, end);
		return signedHeadersStr;
	}

	private String appendCanonicalHeaders(StringBuilder buffer)
	{
		int start = buffer.length();

		Set<String> headers = new TreeSet<>();
		for (Map.Entry<String, String> entry : this.signedHeaders.entrySet())
		{
			String header = PathUtils.normalize(entry.getKey()) + ':' 
                        + PathUtils.normalize(entry.getValue());
			headers.add(header);
		}
		for (String header : headers)
		{
			buffer.append(header).append('\n');
		}
		buffer.deleteCharAt(buffer.length() - 1);

		int end = buffer.length();
		String canonicalHeadersStr = buffer.substring(start, end);
		return canonicalHeadersStr;
	}

	private void appendCanonicalQueryString(StringBuilder buffer)
	{
		//Encode and sort the data.
		Set<String> sortedSet = new TreeSet<>();
		for (Map.Entry<String, String> e : this.queryParameters.entrySet())
		{
			String uriEncodeKey = PathUtils.normalize(e.getKey());
			String uriEncodeValue = this.isNotEmpty(e.getValue()) ? PathUtils.normalize(e.getValue()) : "";
			sortedSet.add(uriEncodeKey + "=" + uriEncodeValue);
		}

		for (String e : sortedSet)
		{
			buffer.append(e).append('&');
		}
		buffer.deleteCharAt(buffer.length() - 1);
	}

	private String formatTimestamp()
	{
		SimpleDateFormat format = new SimpleDateFormat(SignInfo.TIMESTAMP_FORMAT);
		format.setTimeZone(TimeZone.getTimeZone("UTC"));
		return format.format(this.timestamp);
	}

	private boolean isNotEmpty(String str)
	{
		if ((null == str) || str.isEmpty())
		{
			return false;
		}
		return true;
	}

	private <K, V> boolean isNotEmpty(Map<K, V> map)
	{
		if ((null == map) || map.isEmpty())
		{
			return false;
		}
		return true;
	}
}

The PathUtils tool class used by SignInfo is as follows:

package com.huawei.client.rest.v2.demo.utils;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.util.BitSet;

public class PathUtils {

	private static final String CHARSET = "UTF-8";

	private static BitSet URI_UNRESERVED_CHARACTERS = new BitSet();

	private static String[] PERCENT_ENCODED_STRINGS = new String[256];

	static {
		for (int i = 97; i <= 122; i++) {
			PathUtils.URI_UNRESERVED_CHARACTERS.set(i);
		}
		for (int i = 65; i <= 90; i++) {
			PathUtils.URI_UNRESERVED_CHARACTERS.set(i);
		}
		for (int i = 48; i <= 57; i++) {
			PathUtils.URI_UNRESERVED_CHARACTERS.set(i);
		}
		PathUtils.URI_UNRESERVED_CHARACTERS.set(45);
		PathUtils.URI_UNRESERVED_CHARACTERS.set(46);
		PathUtils.URI_UNRESERVED_CHARACTERS.set(95);
		PathUtils.URI_UNRESERVED_CHARACTERS.set(126);

		for (int i = 0; i < PathUtils.PERCENT_ENCODED_STRINGS.length; i++) {
			PathUtils.PERCENT_ENCODED_STRINGS[i] = String.format("%%%02X", new Object[] {Integer.valueOf(i)});
		}
	}

	public static String normalizePath(String path) {
		return PathUtils.normalize(path).replace("%2F", "/");
	}

	public static String normalize(String value) {
		try {
			StringBuilder builder = new StringBuilder();
			for (byte b : value.getBytes(PathUtils.CHARSET)) {
				if (PathUtils.URI_UNRESERVED_CHARACTERS.get(b & 0xFF)) {
					builder.append((char) b);
				} else {
					builder.append(PathUtils.PERCENT_ENCODED_STRINGS[(b & 0xFF)]);
				}
			}
			return builder.toString();
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e);
		}
	}

	/**
           * URL normalization
	 *
	 * @param url
	 * @return
	 */
	public static String normalizeURL(String url) {
		try {
			return URLDecoder.decode(new URI(url).normalize().toString(), PathUtils.CHARSET);
		} catch (URISyntaxException | UnsupportedEncodingException e) {
			return url;
		}
	}

	public static void main(String[] args)
	{
		System.out.println(PathUtils.normalize("123%456"));
	}

}
  • SignedHeaders

    The invoker can selectively encode the header part in an HTTP request. The only requirement is that the Host field must be encoded. However, the head whose headname is Authorization cannot be involved in the encoding calculation because Authorization is the final authentication head field.

    Note: The value of Host is the value of ip:port in the HTTPS URL of the corresponding service.

    In most cases, you are advised to encode the following headers:

    Host="10.22.26.181:28080"  
    Content-Length="22"
    Content-Type="application/json;charset=UTF-8"
    • Content calculation rule:

      Traverse the header names in the HttpHead involved in encoding.

      1. Invoke the lowerCase() function to convert a header name to lowercase letters.
      2. Add a separator (;) to the end of the characters converted from the header name. A record is generated. Note that the last field is not appended with semicolons (;).
      3. Sort all the preceding records in alphabetical order, and then concatenate the records into a character string in sequence.
    • Code implementation:
      1. Construct the map of signedHeaders, fill in the required field information, and invoke the lowerCaseSignedHeaders function to process characters.
        //Head field for signature authentication. The host field is mandatory.
        Map<String, String> signedHeaders = new HashMap<>();
        signedHeaders.put(SignInfo.HEAD_HOST, CmsParameters.host);
        signedHeaders.put(SignInfo.HEAD_CONTENT_LENGTH, String.valueOf(bodyJson.getBytes("UTF-8").length));
        signedHeaders.put(SignInfo.HEAD_CONTENT_TYPE, "application/json;charset=UTF-8");
        
        //Set the authentication data interface.
        SignInfo signInfo = new SignInfo();
        signInfo.setAccessKey(CmsParameters.accessKey);
        signInfo.setSecretKey(CmsParameters.secretKey);
        signInfo.setPayload(bodyJson);
        signInfo.setTimestamp(new Date());
        signInfo.setHttpMethod(HttpProxyHelper.HTTP_METHOD_POST);
        signInfo.setUri(httpPath);
        signInfo.setSignedHeaders(lowerCaseSignedHeaders(signedHeaders));
        

        For details about the complete code of the preceding function, see 1.

      2. Function: lowerCaseSignedHeaders
        /**
         * The signedHeaders involved in the calculation are converted.
         * @param
         * @return Map<String, String>
         */
        private Map<String, String> lowerCaseSignedHeaders(Map<String, String> signedHeaders)
        {
        	if ((null == signedHeaders) || signedHeaders.isEmpty())
        	{
        		throw new IllegalArgumentException("signedHeaders cann't be null.");
        	}
        	Map<String, String> headers = new HashMap<>();
        	for (Entry<String, String> e : signedHeaders.entrySet())
        	{
        		String name = e.getKey();
        		String value = e.getValue();
        		headers.put(name.toLowerCase(Locale.ENGLISH), value.trim());
        	}
        	if (!signedHeaders.containsKey(SignInfo.HEAD_HOST))
        	{
        		throw new IllegalArgumentException("signedHeaders must has host.");
        	}
        	return headers;
        }	
    • Example:
      Example: The HttpHead involved in encoding are as follows:
      Host="10.22.26.181:28080"  
      Content-Length="22"
      Content-Type="application/json;charset=UTF-8"
      After the processing based on the preceding rules, the result is as follows:
      SignedHeaders=content-length;content-type;host
  • CanonicalHeaders

    The encoding rule is the same as that of SignedHeaders, but head value encoding is added.

    • Content calculation rule:

      Traverse the header names in the HttpHead involved in encoding.

      1. Invoke the lowerCase() function to convert a header name to lowercase letters.
      2. Invoke the NormalizePath function to format the converted lowercase character string.
      3. Generate a record character string in the formatted character string +":"+NormalizePath((Header value).trim()).
      4. Sort the preceding records in alphabetical order.
      5. Traverse the sorted records and add the character string \n to connect them to a long character string.

      Note that \n is not added to the last record.

      HTTP headers involved in encoding are as follows:
      Host="10.22.26.181:28080"  
      Content-Length="22"
      Content-Type="application/json;charset=UTF-8"
      After the processing based on the preceding rules, CanonicalHeaders is as follows:
      content-length:22\n
      content-type:application%2Fjson%3Bcharset%3DUTF-8\n
      host:10.22.26.181%3A28080
  • CanonicalRequest
    • Content calculation rule:
      CanonicalRequest = $HttpMethod + "\n" + $HttpURI+ "\n" + $HttpParameters + "\n" + SignedHeaders($HttpHeaders) + "\n" + CanonicalHeaders ($HttpHeaders) + "\n" + NormalizePath($HttpBody)

      Parameter description:

      1. $HttpMethod: GET, PUT, and POST requests defined in the HTTP protocol. The value must be in uppercase. The involved HTTP methods include GET, POST, PUT, DELETE, and HEAD. However, the CC-CMS service supports only POST.
      2. $HttpURI: HTTP URI of the interface request. For example, if the complete URL is https://10.22.26.181:28080/rest/cmsapp/v1/ping, the HTTP URI is /rest/cmsapp/v1/ping. The HTTP URI must start with a slash (/). If the HTTP URI does not start with a slash (/), add it. If the URL is left blank, the value is a slash (/).
      3. $HttpParameters: request parameter following the request URI. For example, if the request URI is https://10.22.26.181:28080/rest/cmsapp/v1/ping?id=123&name=test, the value of $HttpParameters is id=123&name=test. Currently, this parameter does not exist in the CC-CMS interface.
      4. $HttpBody: character string submitted to the server through the HTTP body. The character string is a standard JSON string. For details about the fields, see the definition of each interface.
    • Code implementation:

      For details, see the canonicalRequest() function in •SignInfo.

  • Generating the Character String Authorization
    • Content calculation rule:
      1. Generate authStringPrefix and SigningKey.
        1. The rules for generating authStringPrefix and SigningKey are as follows:
          authStringPrefix="auth-v2/{accessKey}/{timestamp}/{SignedHeaders}";
          SigningKey = sha256Hex(secretKey, authStringPrefix);

          Note:

          auth-v2: authentication version number. In the current version, the value is fixed to auth-v2.

          accessKey: authentication ID of the invoker, that is, the AK.

          secretKey: authentication key of the invoker, that is, the SK.

          timestamp: UTC time generated by the invoker. The time is in yyyy-MM-dd'T'HH:mm:ss'Z' format.

        2. Code implementation: authStringPrefix

          The function for generating authStringPrefix is implemented in the following functions of the SignInfo class:

          authStringPrefix()
          appendSignedHeaders(StringBuilder buffer)

        3. Code implementation: SigningKey

          For details, see authString() in the SignInfo class.

          String signingKey = SignerUtils.sha256Hex(this.getSecretKey(), authStringPrefix);
          For details about the sha256Hex() encryption algorithm, see the following tool class:
          package com.huawei.client.rest.v2.demo.utils;
          
          import java.io.UnsupportedEncodingException;
          import java.security.InvalidKeyException;
          import java.security.NoSuchAlgorithmException;
          
          import javax.crypto.Mac;
          import javax.crypto.spec.SecretKeySpec;
          
          /**
          * Tool class for the authentication digest algorithm of RESTful interfaces
           */
          public class SignerUtils {
          	
          	private static final String CHARSET = "UTF-8";	
          	private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
          	
          	/**
          	 * Digest algorithm
          	 * @param key
          	 * @param toSigned
          	 * @return String
          	 * @throws NoSuchAlgorithmException
          	 * @throws InvalidKeyException
          	 * @throws UnsupportedEncodingException
          	 */
          	public static String sha256Hex(String key, String toSigned) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
          		Mac mac = Mac.getInstance("HmacSHA256");
          		mac.init(new SecretKeySpec(key.getBytes(SignerUtils.CHARSET), "HmacSHA256"));
          		
          		String digit = new String(SignerUtils.encodeHex(mac.doFinal(toSigned.getBytes(SignerUtils.CHARSET))));
          		return digit;
          	}
          	
          	private static char[] encodeHex(final byte[] data) {
          		final int l = data.length;
          		final char[] out = new char[l << 1];
          		for (int i = 0, j = 0; i < l; i++) {
          			out[j++] = SignerUtils.DIGITS_LOWER[(0xF0 & data[i]) >>> 4];
          			out[j++] = SignerUtils.DIGITS_LOWER[0x0F & data[i]];
          		}
          		return out;
          	}	
          }

      1. Generate a signature.
        1. Signature generation rule:
          Signature = sha256Hex(SigningKey, CanonicalRequest)
        2. Code implementation: The encryption function sha256Hex is included.

          For details, see the implementation method of sha256Hex() in SigningKey of c. Code implementation: SigningKey.

      2. Authentication character string
        1. Rule for generating an authentication string:
          Authorization:$authStringPrefix/$Signature
        2. Code implementation: signedHeaders is the map constructed in the signedHeaders module.
          //Generate a signature.
          String signature = signInfo.authString();
          Map<String, String> httpHeads = new HashMap<>(signedHeaders);
          //Append to HTTPHEAD and send it to the server.
          httpHeads.put(SignInfo.HEAD_AUTHORIZATION, signature);

  • Example
    1. Commission the first interface:

      Authentication is necessary for commissioning an interface.

      In this project, the authentication entry is the buildSignHttpHead() function. This function constructs the map of signedHeaders and transfers the map to SignInfo.

      The authString() function in the SignInfo class reveals the main process of constructing the authentication character string and generates the signature.

      The key component of the authentication string is the signature. Other information concatenation functions are also included in the SignInfo class.

      Define BaseRequest.

      package com.huawei.client.rest.v2.demo.base;
      
      @Setter
      @Getter
      public class BaseRequest
      {
      	private RequestHeader request;
      
      	private Object msgBody;
      
      	public BaseRequest(Object body)
      	{
      		this.request = new RequestHeader();
      		this.msgBody = body;
      	}
      
      	public BaseRequest(RequestHeader head, Object body)
      	{
      		this.request = head;
      		this.msgBody = body;
      	}
      
      	public RequestHeader getRequest()
      	{
      		return this.request;
      	}
      
      	public void setRequest(RequestHeader request)
      	{
      		this.request = request;
      	}
      
      	public Object getMsgBody()
      	{
      		return this.msgBody;
      	}
      
      	public void setMsgBody(Object msgBody)
      	{
      		this.msgBody = msgBody;
      	}
      
      }

      RequestHeader is as follows:

      package com.huawei.client.rest.v2.demo.base;
      
      public class RequestHeader
      {
      	/**
                 * Version
      	 */
      	private String version = "2.0";
      	
      	public String getVersion()
      	{
      		return this.version;
      	}
      	
      	public void setVersion(String version)
      	{
      		this.version = version;
      	}
      
      }

      Define BaseResponse.

      package com.huawei.client.rest.v2.demo.base;
      
      @Setter
      @Getter
      public class BaseResponse
      {
      
      	final static int SUCCESS = 0;
      
      	private ResponseHead resultHead;
      
      	private Object resultData;
      
      	public BaseResponse()
      	{
      
      	}
      
      	public BaseResponse(String resultCode, String resultMsg, Object resultData)
      	{
      		this.resultHead = new ResponseHead(resultCode, resultMsg);
      		this.resultData = resultData;
      	}
      
      	public BaseResponse(ResponseHead resultHead, Object resultData)
      	{
      		this.resultHead = resultHead;
      		this.resultData = resultData;
      	}
      }

      ResponseHead is as follows:

      package com.huawei.client.rest.v2.demo.base;
      
      public class ResponseHead
      {
      	private String resultCode;
      
      	/**
                 * Response result
      	 */
      	private String resultMsg;
      
      	public ResponseHead()
      	{
      	}
      
      	public ResponseHead(String resultCode, String resultMsg)
      	{
      		this.resultCode = resultCode;
      		this.resultMsg = resultMsg;
      	}
      
      	public String getResultCode()
      	{
      		return this.resultCode;
      	}
      
      	public void setResultCode(String resultCode)
      	{
      		this.resultCode = resultCode;
      	}
      
      	public String getResultMsg()
      	{
      		return this.resultMsg;
      	}
      
      	public void setResultMsg(String resultMsg)
      	{
      		this.resultMsg = resultMsg;
      	}
      
      }

      For details about the implementation of the buildSignHttpHead() function, see ToolUtils.

      package com.huawei.client.rest.v2.demo.utils;
      
      import java.io.BufferedReader;
      import java.io.File;
      import java.io.FileOutputStream;
      import java.io.FileReader;
      import java.io.InputStream;
      import java.io.UnsupportedEncodingException;
      import java.security.InvalidKeyException;
      import java.security.NoSuchAlgorithmException;
      import java.text.SimpleDateFormat;
      import java.util.ArrayList;
      import java.util.Date;
      import java.util.HashMap;
      import java.util.List;
      import java.util.Locale;
      import java.util.Map;
      import com.alibaba.fastjson.JSONObject;
      
      import com.huawei.client.rest.v2.demo.HttpProxyHelper;
      import com.huawei.client.rest.v2.demo.base.BaseRequest;
      import com.huawei.client.rest.v2.demo.config.CmsParameters;
      import com.huawei.client.rest.v2.demo.sign.SignInfo;
      
      /**
      * Tool class for the authentication digest algorithm of RESTful interfaces
       */
      public class ToolUtils {
      	/**
                 * Convert character strings to files.
      	 * @param in
      	 * @param filePath
      	 * @param fileName
      	 * @return
      	 */
      	public static boolean saveToFileByStr(InputStream in, String filePath, String fileName){
      		boolean flag = true;
      		if(in != null){
      			try {
      				//Restore the character string fileStr in the generated file to a file.
      
      				File file=new File(filePath,fileName);
      				FileOutputStream fos=new FileOutputStream(file);
      				try{
      					byte[] buffer = new byte[1024];
      					int reader = 0;
      					while ((reader = in.read(buffer)) != -1) {
      						fos.write(buffer, 0, reader);
      					}
      					fos.flush();
      				}
      				finally {
      					fos.close();
      					in.close();
      				}
      
      			} catch (Exception e) {
      				flag = false;
      				e.printStackTrace();
      			}
      		}
      		return flag;
      	}
      
      	/**
      	 * Enter the path of the .csv file.
      	 * Return the address of the original recording file that is read.
      	 * @param filePath
      	 * @return
      	 */
      	public static List<String> readFile(String filePath){
      		List<String> originalFileList = new ArrayList<String>();
      		try {
      			BufferedReader reader = new BufferedReader(new FileReader(filePath));
      			//The first line is the table header information.
      			reader.readLine();
      
      			String line = null;
      			while((line=reader.readLine())!=null){
      				String item[] = line.split(",");//The CSV file is a comma-separated file.
      				originalFileList.add(item[item.length-4]);
      			}
      		} catch (Exception e) {
      			e.printStackTrace();
      		}
      
      		return originalFileList;
      	}
      
      	/**
      	 * During actual client development, BaseRequest is directly converted into a JSON string based on the JSON framework.
      	 * @param request
      	 * @return String
      	 * @throws Exception
      	 */
      	public static String toJsonString(BaseRequest request) throws Exception
      	{
      		JSONObject jsonObject = new JSONObject();
      		StringBuilder buffer = new StringBuilder();
      		buffer.append("{");
      		buffer.append("\"request\":");
      		buffer.append(jsonObject.toJSONString(request.getRequest()));
      		buffer.append(",");
      		buffer.append("\"msgBody\":");
      		buffer.append(jsonObject.toJSONString(request.getMsgBody()));
      		buffer.append("}");
      
      		return buffer.toString();
      	}
      
      	/**
      	 * Construct authentication fields.
      	 * @param httpPath
      	 * @param bodyJson
      	 * @return
      	 * @throws InvalidKeyException
      	 * @throws NoSuchAlgorithmException
      	 * @throws UnsupportedEncodingException
      	 */
      	public Map<String, String> buildSignHttpHead(String httpPath, String bodyJson)
      			throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException
      	{
      		//Head field for signature authentication. The host field is mandatory.
      		Map<String, String> signedHeaders = new HashMap<>();
      		signedHeaders.put(SignInfo.HEAD_HOST, CmsParameters.host);
      		signedHeaders.put(SignInfo.HEAD_CONTENT_LENGTH, String.valueOf(bodyJson.getBytes("UTF-8").length));
      		signedHeaders.put(SignInfo.HEAD_CONTENT_TYPE, "application/json;charset=UTF-8");
      
      		//Set the authentication data interface.
      		SignInfo signInfo = new SignInfo();
      		signInfo.setAccessKey(CmsParameters.accessKey);
      		signInfo.setSecretKey(CmsParameters.secretKey);
      		signInfo.setPayload(bodyJson);
      		signInfo.setTimestamp(new Date());
      		signInfo.setHttpMethod(HttpProxyHelper.HTTP_METHOD_POST);
      		signInfo.setUri(httpPath);
      		signInfo.setSignedHeaders(this.lowerCaseSignedHeaders(signedHeaders));
      
      		//Generate a signature.
      		String signature = signInfo.authString();
      		Map<String, String> httpHeads = new HashMap<>(signedHeaders);
      		//Append to HTTPHEAD and send it to the server.
      		httpHeads.put(SignInfo.HEAD_AUTHORIZATION, signature);
      
      		return httpHeads;
      	}	
      
      	/**
      	 * The signedHeaders involved in the calculation are converted.
      	 * @param
      	 * @return Map<String, String>
      	 */
      	private Map<String, String> lowerCaseSignedHeaders(Map<String, String> signedHeaders)
      	{
      		if ((null == signedHeaders) || signedHeaders.isEmpty())
      		{
      			throw new IllegalArgumentException("signedHeaders cann't be null.");
      		}
      		Map<String, String> headers = new HashMap<>();
      		for (Entry<String, String> e : signedHeaders.entrySet())
      		{
      			String name = e.getKey();
      			String value = e.getValue();
      			headers.put(name.toLowerCase(Locale.ENGLISH), value.trim());
      		}
      		if (!signedHeaders.containsKey(SignInfo.HEAD_HOST))
      		{
      			throw new IllegalArgumentException("signedHeaders must has host.");
      		}
      		return headers;
      	}
      }
    2. Code implementation:
      • Main function for creating a project:
        public static void main(String[] args)
        	{
        		HttpClientMain demo = new HttpClientMain();
        		
        		try
        		{
        			demo.cmsPingTest();
        		}
        		catch (Exception e)
        		{
        			e.printStackTrace();
        		}
        	}
      • Main function for implementing the POST request of the interface (including authentication):
        public void cmsPingTest() throws Exception
        {
                HttpProxyHelper httpProxy = new HttpProxyHelper();
                //URL of the request interface.
                final String httpPath = "/rest/cmsapp/v1/ping";
                final String postUrl = CmsParameters.url + httpPath;
               //Construct bodyJson in the request interface.
                JSONObject jsonBody = new JSONObject();
                jsonBody.put("say", "Hello world!");
                String jsonBodyStr = JSONObject.toJSONString(jsonBody);
        
                //Construct authentication information.
                Map<String, String> httpHeads = ToolUtils.buildSignHttpHead(httpPath, jsonBodyStr);
                //Execute the HTTP request and obtain the returned JSON character string.
                String response = httpProxy.doPost(postUrl, httpHeads, jsonBodyStr);
                //If the interface is successfully invoked, information containing responseId is returned.
                System.out.println(response);
        }
      • Main function for implementing the GET request of the interface (including authentication) (compared with the POST request, other code of the get request remains unchanged):
        public void cmsPingTest() throws Exception
        {
                HttpProxyHelper httpProxy = new HttpProxyHelper();
                //URL of the request interface.
                final String httpPath = "/rest/cmsapp/v1/ping";
                final String postUrl = CmsParameters.url + httpPath;
                //GET request parameters
                Map<String, String> queryParameters = new HashMap<>();
                //Authentication information constructed in a GET request
                Map<String, String> httpHeads = ToolUtils.buildSignHttpHead(httpPath, null, queryParameters, HttpProxyHelper.HTTP_METHOD_GET);
                System.out.println("httpHeads:" + httpHeads);
                //Execute the HTTP request and obtain the returned JSON character string.
                String response = httpProxy.doGet(postUrl, httpHeads, queryParameters);
                //If the interface is successfully invoked, information containing responseId is returned.
                System.out.println(response);
        }
      • For the implementation of the ToolUtils.toJsonString() function, the actual invoker needs to convert BaseRequest into a JSON string based on the JSON framework. The following is an example:
        /**
         *During actual client development, BaseRequest is directly converted into a JSON string based on the JSON framework.
         * @param request
         * @return String
         * @throws Exception
         */
        public static String toJsonString(BaseRequest request) throws Exception
        {
        	JSONObject jsonObject = new JSONObject();
        	StringBuilder buffer = new StringBuilder();
        	buffer.append("{");
        	buffer.append("\"request\":");
        	buffer.append(jsonObject.toJSONString(request.getRequest()));
        	buffer.append(",");
        	buffer.append("\"msgBody\":");
        	buffer.append(jsonObject.toJSONString(request.getMsgBody()));
        	buffer.append("}");
        
        	return buffer.toString();
        }
      • To implement the POST method of the HTTP request, the actual invoker can select a framework and use the POST method of its own framework.
        package com.huawei.client.rest.v2.demo;
        
        import java.io.BufferedInputStream;
        import java.io.ByteArrayOutputStream;
        import java.io.Closeable;
        import java.io.IOException;
        import java.io.InputStream;
        import java.io.OutputStream;
        import java.net.HttpURLConnection;
        import java.net.URL;
        import java.security.KeyManagementException;
        import java.security.NoSuchAlgorithmException;
        import java.security.cert.CertificateException;
        import java.security.cert.X509Certificate;
        import java.util.Map;
        import java.util.Map.Entry;
        
        import javax.net.ssl.HostnameVerifier;
        import javax.net.ssl.HttpsURLConnection;
        import javax.net.ssl.SSLContext;
        import javax.net.ssl.SSLSession;
        import javax.net.ssl.TrustManager;
        import javax.net.ssl.X509TrustManager;
        
        
        public class HttpProxyHelper
        {
        	public static final String HTTP_METHOD_POST = "POST";
        
        	public String doPost(String urlAddress, Map<String, String> httpHeads, String bodyJson) throws IOException, NoSuchAlgorithmException, KeyManagementException
        	{
        		byte[] byteData = bodyJson.getBytes("UTF-8");
        		OutputStream out = null;
        		InputStream in = null;
        
        		//Set up a connection.
        		this.initHttpsURLConnection();
        		URL url = new URL(urlAddress);
        		HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
        
        		//Set parameters.
        		httpConn.setRequestMethod(HttpProxyHelper.HTTP_METHOD_POST);
        		httpConn.setRequestProperty("Charset", "UTF-8");
        		//httpConn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
        		httpConn.setRequestProperty("accept", "application/json");
        		//httpConn.setRequestProperty("Content-Length", String.valueOf(byteData.length));
        		httpConn.setDoOutput(true);
        		httpConn.setDoInput(true);
        		httpConn.setUseCaches(false);
        		httpConn.setConnectTimeout(20 * 1000);
        		httpConn.setReadTimeout(30 * 1000);
        		//Set the parameters carried by the business.
        		if ((null != httpHeads) && !httpHeads.isEmpty())
        		{
        			for (Entry<String, String> e : httpHeads.entrySet())
        			{
        				httpConn.setRequestProperty(e.getKey(), e.getValue());
        			}
        		}
        
        		try
        		{
        			//Send data.
        			out = httpConn.getOutputStream();
        			out.write(byteData);
        			out.flush();
        
        			//Receive data.
        			int responseCode = httpConn.getResponseCode();
        			if (responseCode != HttpURLConnection.HTTP_OK)
        			{
        				throw new RuntimeException("Failed responseCode " + responseCode);
        			}
        
        			in = httpConn.getInputStream();
        			String reponseJson = this.getStreamAsString(in, "UTF-8");
        			return reponseJson;
        		}
        		finally
        		{
        			this.closeStream(out);
        			this.closeStream(in);
        		}
        	}
        
        	/**
        	 * Obtain a character string from a stream.
        	 * @param in
        	 * @param charset
        	 * @return String
        	 * @throws IOException
        	 */
        	private String getStreamAsString(InputStream in, String charset) throws IOException
        	{
        		BufferedInputStream buffer = new BufferedInputStream(in);
        		ByteArrayOutputStream out = new ByteArrayOutputStream();
        		try
        		{
        			byte[] cache = new byte[512];
        			int count = 0;
        			while ((count = buffer.read(cache)) > 0)
        			{
        				out.write(cache, 0, count);
        			}
        		}
        		finally
        		{
        			if (buffer != null)
        			{
        				buffer.close();
        			}
        		}
        
        		return new String(out.toByteArray(), charset);
        	}
        
        	/**
        	 * Close a stream.
        	 * @param stream
        	 */
        	private void closeStream(Closeable stream)
        	{
        		if (null != stream)
        		{
        			try
        			{
        				stream.close();
        			}
        			catch (Exception e)
        			{
        				e.printStackTrace();
        			}
        		}
        	}
        
        	private void initHttpsURLConnection() throws NoSuchAlgorithmException, KeyManagementException
        	{
        		SSLContext sslcontext = SSLContext.getInstance("SSL");
        		HostnameVerifier hnv = new HttpsHostnameVerifier();
        		sslcontext.init(null, new TrustManager[] { new HttpsTrustAnyTrustManager() }, new java.security.SecureRandom());
        
        		HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory());
        		HttpsURLConnection.setDefaultHostnameVerifier(hnv);
        	}
        
        	private class HttpsTrustAnyTrustManager implements X509TrustManager
        	{
        		@Override
        		public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException
        		{
        		}
        
        		@Override
        		public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException
        		{
        		}
        
        		@Override
        		public X509Certificate[] getAcceptedIssuers()
        		{
        			return new X509Certificate[] {};
        		}
        	}
        
        	private class HttpsHostnameVerifier implements HostnameVerifier
        	{
        		@Override
        		public boolean verify(String hostname, SSLSession session)
        		{
        			return true;
        		}
        	}
        }
    3. Variables:

      Variable

      Value

      HttpHead

      Host="10.22.26.181:28080"

      Content-Length="22"

      Content-Type="application/json;charset=UTF-8"

      accessKey

      globalaktest

      secretKey

      1qaz**********************************20

      HttpURI

      /rest/cmsapp/v1/ping

      HttpMethod

      POST

      timestamp

      2018-10-17T11:48:24Z

      HttpBody

      {"say": "Hello world!"}

      SignedHeaders

      content-length;content-type;host

      CanonicalHeaders

      content-length:22\n

      content-type:application%2Fjson%3Bcharset%3DUTF-8\n

      host:10.22.26.181%3A28080

      CanonicalRequest

      POST\n

      /rest/cmsapp/v1/ping\n

      content-length;content-type;host\n

      content-length:22\n

      content-type:application%2Fjson%3Bcharset%3DUTF-8\n

      host:10.22.26.181%3A28080\n

      %7B%22request%22%3A%7B%22version%22%3A%222.0%22%7D%2C%22msgBody%22%3A%7B%22accountId%22%3A%22%22%2C%22beginTime%22%3A%222018-06-29%2010%3A42%3A49%22%2C%22endTime%22%3A%222018-07-02%2010%3A42%3A49%22%2C%22agentId%22%3A%22%22%2C%22callId%22%3A%22%22%2C%22dataType%22%3A%22call_record%22%2C%22callBackURL%22%3A%22http%3A%2F%2F10.57.118.171%3A8080%22%7D%7D

      SigningKey

      b25b************************************************40

      Signature

      d5a8************************************************2f

      Authorization

      auth-v2/globalak/2018-10-17T11:48:24Z/content-length;content-type;host/d5a8************************************************2f