URL Validation

To prevent live resources from being stolen, you can configure URL validation to add authentication information to the end of the original ingest or streaming URL. When a streamer starts live streaming or a viewer requests playback, CDN verifies encrypted information in a URL. Only the requests that pass the verification are responded, and other illegitimate requests are rejected.

How Does URL Validation Works

Figure 1 URL validation working principles

The process is as follows:

  1. A tenant enables URL validation on the Live console and configures the key and timeout interval.
  2. The Live service delivers the key value and timeout interval to a CDN node.
  3. The streamer or viewer requests CDN to push streams or play video through an authentication ingest/streaming URL.
  4. CDN verifies the request based on authentication information carried in an ingest or streaming URL. Only requests that pass the verification are allowed.

Notes

  • This function is optional and is disabled by default.
  • After this function is enabled, the original URLs cannot be used. New authentication URLs must be generated based on rules.
  • If a signed URL expires or the signature fails to be authenticated, the live stream fails to be played and the message "403 Forbidden" is returned.
  • To disable URL validation, submit a service ticket.

Prerequisites

  • The ingest domain name and streaming domain name have been added and associated.
  • CNMAE records have been added to your domains' DNS records.

Enabling URL Validation

  1. Log in to the Live console.
  2. In the navigation pane, choose Domains.
  3. Click Settings in the row containing the target streaming domain name.
  4. In the navigation pane, choose Basic Settings > Access Control.
  5. Choose URL Validation. The URL Validation dialog box is displayed.
  6. Turn on the switch and configure related parameters.

    Figure 2 Configuring URL validation
    • Key: authentication key, which can be customized. The value consists of 16 characters, including letters and digits.
    • Timeout Interval: timeout interval of URL authentication information, that is, the maximum difference between the request time carried in authentication information and the time when the Live service receives the request. This parameter is used to check whether an ingest URL or streaming URL expires. The unit is second. The value ranges from 1 minute to 30 days.

  7. Click OK.
  8. Submit a service ticket for the configuration to take effect.

    If you modify related parameters, you also need to submit a service ticket for the change to take effect. Table 1 describes the information to be submitted in a service ticket.

    Table 1 Information to be submitted

    Information

    Description

    Ingest domain name

    Ingest domain name for which URL validation needs to be configured

    Example: test-push.example.com

    Streaming domain name

    Streaming domain name for which URL validation needs to be configured

    Example: test-play.example.com

    Key

    Set this parameter to the key value set in 4.

    Example: 9wAynnEtvouJwJMs

    Timeout Interval

    Set this parameter to the duration set in 4.

    Example: 120

    After your application is approved, you can use the authentication URL to push streams or play video. Obtain the ingest and streaming URLs that contain authentication information by referring to Generating a signed URL.

Generating a signed URL

A signed URL is in the following format:

Original URL?auth_info=Encrypted string.EncodedIV
The algorithm for generating the authentication fields is as follows:
  • LiveID=<AppName>+"/"+<StreamName>
  • Encrypted string = UrlEncode(Base64(AES128(<Key>,"$"+<Timestamp>+"$"+<LiveID>+"$"+<CheckLevel>)))
  • EncodedIV = Hex (IV used for encryption)

Table 2 describes encryption parameters in the algorithm.

Table 2 Encryption parameters

Parameter

Description

AppName

Application name, which is the same as the value of AppName in an ingest or streaming URL

StreamName

Stream name, which is the same as the value of StreamName in an ingest or streaming URL

Key

Authentication key, which is the key value configured in Enabling URL Validation

LiveID

Live stream ID, which uniquely identifies a live stream. The value consists of AppName and StreamName.

LiveID=<AppName>+"/"+<StreamName>

Timestamp

UTC time when an authentication parameter is generated, in yyyyMMddHHmmss format. This parameter is used to check whether the authentication parameter has expired, that is, whether the absolute value of the difference between Timestamp and the current time is greater than the configured timeout interval.

CheckLevel

Check level. The value is 3 or 5.

  • If CheckLevel is 3, the system only checks whether the value of LiveID is matched.
  • If CheckLevel is 5, the system checks whether the value of LiveID is matched and whether Timestamp times out.

IV

Cipher block chaining (CBC) depends on the initialization vector (IV). IV consists of 16 random digits and letters and must be 128 bits. In CBC mode, PKCS7 padding is used.

Signed URL example:

rtmp://live.huaweitest1.com/live/8712345?auth_info=LpB4kdZfnOwfbpIgYVo4ABAU6CRUmV00OEARLlC7NLs%3D.79436d453636364e335941713330534e

In the preceding information, LpB4kdZfnOwfbpIgYVo4ABAU6CRUmV00OEARLlC7NLs%3D is the encrypted string, and 79436d453636364e335941713330534e is the EncodedIV value.

Sample Code

Java demo is used as an example to describe how to generate an encrypted string and EncodedIV.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

public class Main {

        public static void main(String[] args) {

		// data="$"+<Timestamp>+"$"+<LiveID>+"$"+<CheckLevel>. For details, see "Generating a Signed URL."
                String data = "$20190428110000$live/stream01$3";

                // A random 16-digit string consisting of digits and letters
		byte[] ivBytes = "yCmE666N3YAq30SN".getBytes();

                // Key value configured on the Live console
		byte[] key = "MyLiveKeyValue01".getBytes();

                String msg = aesCbcEncrypt(data, ivBytes, key);
		try {
			System.out.println(URLEncoder.encode(msg, "UTF-8") + "." + bytesToHexString(ivBytes));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}

        private static String aesCbcEncrypt(String data, byte[] ivBytes, byte[] key) {
		try {
			SecretKeySpec sk = new SecretKeySpec(key, "AES");
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

                        if (ivBytes != null) {
				cipher.init(Cipher.ENCRYPT_MODE, sk, new IvParameterSpec(ivBytes));
			} else {
				cipher.init(Cipher.ENCRYPT_MODE, sk);
			}

                        return Base64.encode(cipher.doFinal(data.getBytes("UTF-8")));
		} catch (Exception e) {
			return null;
		}
	}

        public static String bytesToHexString(byte[] src) {
		StringBuilder stringBuilder = new StringBuilder("");
		if ((src == null) || (src.length <= 0)) {
			return null;
		}

                for (int i = 0; i < src.length; i++) {
			int v = src[i] & 0xFF;
			String hv = Integer.toHexString(v);
			if (hv.length() < 2) {
				stringBuilder.append(0);
			}
			stringBuilder.append(hv);
		}
		return stringBuilder.toString();
	}
}

Base64 is used to encode encrypted strings.

public class Base64
{

    / ** Base64 encoding table */
    private static char base64Code[] =
    {
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
        'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
        'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1',
        '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',};

    /**
     * The construction method is privatized to prevent instantiation.
     */
    private Base64()
    {
        super();
    }

    /**
     * Encode three bytes in a byte array into four visible characters.
     * @param bytes Byte data to be encoded
     * @return Base64 character string after encoding
     */
    public static String encode(byte[] bytes)
    {
        int a = 0;

        // Allocate memory based on the actual length after encoding for acceleration.
        StringBuffer buffer = new StringBuffer(((bytes.length - 1) / 3) << 2 + 4);

        // Encoding
        for (int i = 0; i < bytes.length; i++)
        {
            a |= (bytes[i] << (16 - i % 3 * 8)) & (0xff << (16 - i % 3 * 8));
            if (i % 3 == 2 || i == bytes.length - 1)
            {
                buffer.append(Base64.base64Code[(a & 0xfc0000) >>> 18]);
                buffer.append(Base64.base64Code[(a & 0x3f000) >>> 12]);
                buffer.append(Base64.base64Code[(a & 0xfc0) >>> 6]);
                buffer.append(Base64.base64Code[a & 0x3f]);
                a = 0;
            }
        }

        // For a byte array whose length is not an integral multiple of 3, add 0 before encoding and replace it with = after encoding.
        // The number of equal signs (=) is the same as the length of the missing data to identify the actual data length.
        if (bytes.length % 3 > 0)
        {
            buffer.setCharAt(buffer.length() - 1, '=');
        }
        if (bytes.length % 3 == 1)
        {
            buffer.setCharAt(buffer.length() - 2, '=');
        }
        return buffer.toString();
    }

}