extra) {
+ this.serviceType = serviceType;
+ this.signature = signature;
+ this.appID = appID;
+ this.unixTs = unixTs;
+ this.salt = salt;
+ this.expiredTs = expiredTs;
+ this.extra = extra;
+ }
+
+ public ByteBuf marshal(ByteBuf out) {
+ return out.put(serviceType).put(signature).put(appID).put(unixTs).put(salt).put(expiredTs).put(extra);
+ }
+
+ public void unmarshall(ByteBuf in) {
+ this.serviceType = in.readShort();
+ this.signature = in.readString();
+ this.appID = in.readBytes();
+ this.unixTs = in.readInt();
+ this.salt = in.readInt();
+ this.expiredTs = in.readInt();
+ this.extra = in.readMap();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/io/agora/media/DynamicKeyUtil.java b/src/main/java/io/agora/media/DynamicKeyUtil.java
new file mode 100644
index 0000000..af4bfc5
--- /dev/null
+++ b/src/main/java/io/agora/media/DynamicKeyUtil.java
@@ -0,0 +1,33 @@
+package io.agora.media;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Created by hefeng on 15/8/10.
+ * Util to generate Agora media dynamic key.
+ */
+public class DynamicKeyUtil {
+
+ static byte[] encodeHMAC(String key, byte[] message) throws NoSuchAlgorithmException, InvalidKeyException {
+ return encodeHMAC(key.getBytes(), message);
+ }
+
+ static byte[] encodeHMAC(byte[] key, byte[] message) throws NoSuchAlgorithmException, InvalidKeyException {
+ SecretKeySpec keySpec = new SecretKeySpec(key, "HmacSHA1");
+
+ Mac mac = Mac.getInstance("HmacSHA1");
+ mac.init(keySpec);
+ return mac.doFinal(message);
+ }
+
+ static String bytesToHex(byte[] in) {
+ final StringBuilder builder = new StringBuilder();
+ for (byte b : in) {
+ builder.append(String.format("%02x", b));
+ }
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/io/agora/media/FpaTokenBuilder.java b/src/main/java/io/agora/media/FpaTokenBuilder.java
new file mode 100644
index 0000000..443c922
--- /dev/null
+++ b/src/main/java/io/agora/media/FpaTokenBuilder.java
@@ -0,0 +1,27 @@
+package io.agora.media;
+
+public class FpaTokenBuilder {
+ /**
+ * Build the FPA token.
+ *
+ * @param appId: The App ID issued to you by Agora. Apply for a new App ID from
+ * Agora Dashboard if it is missing from your kit. See Get an App ID.
+ * @param appCertificate: Certificate of the application that you registered in
+ * the Agora Dashboard. See Get an App Certificate.
+ * @return The FPA token.
+ */
+ public String buildToken(String appId, String appCertificate) {
+ AccessToken2 accessToken = new AccessToken2(appId, appCertificate, 24 * 3600);
+ AccessToken2.Service serviceFpa = new AccessToken2.ServiceFpa();
+
+ serviceFpa.addPrivilegeFpa(AccessToken2.PrivilegeFpa.PRIVILEGE_LOGIN, 0);
+ accessToken.addService(serviceFpa);
+
+ try {
+ return accessToken.build();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return "";
+ }
+ }
+}
diff --git a/src/main/java/io/agora/media/Packable.java b/src/main/java/io/agora/media/Packable.java
new file mode 100644
index 0000000..916c36c
--- /dev/null
+++ b/src/main/java/io/agora/media/Packable.java
@@ -0,0 +1,8 @@
+package io.agora.media;
+
+/**
+ * Created by Li on 10/1/2016.
+ */
+public interface Packable {
+ ByteBuf marshal(ByteBuf out);
+}
diff --git a/src/main/java/io/agora/media/PackableEx.java b/src/main/java/io/agora/media/PackableEx.java
new file mode 100644
index 0000000..76e274b
--- /dev/null
+++ b/src/main/java/io/agora/media/PackableEx.java
@@ -0,0 +1,5 @@
+package io.agora.media;
+
+public interface PackableEx extends Packable {
+ void unmarshal(ByteBuf in);
+}
diff --git a/src/main/java/io/agora/media/RtcTokenBuilder.java b/src/main/java/io/agora/media/RtcTokenBuilder.java
new file mode 100644
index 0000000..2c85246
--- /dev/null
+++ b/src/main/java/io/agora/media/RtcTokenBuilder.java
@@ -0,0 +1,108 @@
+package io.agora.media;
+
+public class RtcTokenBuilder {
+ public enum Role {
+ /**
+ * DEPRECATED. Role_Attendee has the same privileges as Role_Publisher.
+ */
+ Role_Attendee(0),
+ /**
+ * RECOMMENDED. Use this role for a voice/video call or a live broadcast, if your scenario does not require authentication for [Hosting-in](https://docs.agora.io/en/Agora%20Platform/terms?platform=All%20Platforms#hosting-in).
+ */
+ Role_Publisher(1),
+ /**
+ * Only use this role if your scenario require authentication for [Hosting-in](https://docs.agora.io/en/Agora%20Platform/terms?platform=All%20Platforms#hosting-in).
+ *
+ * @note In order for this role to take effect, please contact our support team to enable authentication for Hosting-in for you. Otherwise, Role_Subscriber still has the same privileges as Role_Publisher.
+ */
+ Role_Subscriber(2),
+ /**
+ * DEPRECATED. Role_Attendee has the same privileges as Role_Publisher.
+ */
+ Role_Admin(101);
+
+ public int initValue;
+
+ Role(int initValue) {
+ this.initValue = initValue;
+ }
+ }
+
+ /**
+ * Builds an RTC token using an int uid.
+ *
+ * @param appId The App ID issued to you by Agora.
+ * @param appCertificate Certificate of the application that you registered in
+ * the Agora Dashboard.
+ * @param channelName The unique channel name for the AgoraRTC session in the string format. The string length must be less than 64 bytes. Supported character scopes are:
+ *
+ * - The 26 lowercase English letters: a to z.
+ * - The 26 uppercase English letters: A to Z.
+ * - The 10 digits: 0 to 9.
+ * - The space.
+ * - "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+ *
+ * @param uid User ID. A 32-bit unsigned integer with a value ranging from
+ * 1 to (2^32-1).
+ * @param role The user role.
+ *
+ * - Role_Publisher = 1: RECOMMENDED. Use this role for a voice/video call or a live broadcast.
+ * - Role_Subscriber = 2: ONLY use this role if your live-broadcast scenario requires authentication for [Hosting-in](https://docs.agora.io/en/Agora%20Platform/terms?platform=All%20Platforms#hosting-in). In order for this role to take effect, please contact our support team to enable authentication for Hosting-in for you. Otherwise, Role_Subscriber still has the same privileges as Role_Publisher.
+ *
+ * @param privilegeTs Represented by the number of seconds elapsed since 1/1/1970.
+ * If, for example, you want to access the Agora Service within 10 minutes
+ * after the token is generated, set expireTimestamp as the current time stamp
+ * + 600 (seconds).
+ */
+ public String buildTokenWithUid(String appId, String appCertificate,
+ String channelName, int uid, Role role, int privilegeTs) {
+ String account = uid == 0 ? "" : String.valueOf(uid);
+ return buildTokenWithUserAccount(appId, appCertificate, channelName,
+ account, role, privilegeTs);
+ }
+
+ /**
+ * Builds an RTC token using a string userAccount.
+ *
+ * @param appId The App ID issued to you by Agora.
+ * @param appCertificate Certificate of the application that you registered in
+ * the Agora Dashboard.
+ * @param channelName The unique channel name for the AgoraRTC session in the string format. The string length must be less than 64 bytes. Supported character scopes are:
+ *
+ * - The 26 lowercase English letters: a to z.
+ * - The 26 uppercase English letters: A to Z.
+ * - The 10 digits: 0 to 9.
+ * - The space.
+ * - "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+ *
+ * @param account The user account.
+ * @param role The user role.
+ *
+ * - Role_Publisher = 1: RECOMMENDED. Use this role for a voice/video call or a live broadcast.
+ * - Role_Subscriber = 2: ONLY use this role if your live-broadcast scenario requires authentication for [Hosting-in](https://docs.agora.io/en/Agora%20Platform/terms?platform=All%20Platforms#hosting-in). In order for this role to take effect, please contact our support team to enable authentication for Hosting-in for you. Otherwise, Role_Subscriber still has the same privileges as Role_Publisher.
+ *
+ * @param privilegeTs represented by the number of seconds elapsed since 1/1/1970.
+ * If, for example, you want to access the Agora Service within 10 minutes
+ * after the token is generated, set expireTimestamp as the current time stamp
+ * + 600 (seconds).
+ */
+ public String buildTokenWithUserAccount(String appId, String appCertificate,
+ String channelName, String account, Role role, int privilegeTs) {
+
+ // Assign appropriate access privileges to each role.
+ AccessToken builder = new AccessToken(appId, appCertificate, channelName, account);
+ builder.addPrivilege(AccessToken.Privileges.kJoinChannel, privilegeTs);
+ if (role == Role.Role_Publisher || role == Role.Role_Subscriber || role == Role.Role_Admin) {
+ builder.addPrivilege(AccessToken.Privileges.kPublishAudioStream, privilegeTs);
+ builder.addPrivilege(AccessToken.Privileges.kPublishVideoStream, privilegeTs);
+ builder.addPrivilege(AccessToken.Privileges.kPublishDataStream, privilegeTs);
+ }
+
+ try {
+ return builder.build();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return "";
+ }
+ }
+}
diff --git a/src/main/java/io/agora/media/RtcTokenBuilder2.java b/src/main/java/io/agora/media/RtcTokenBuilder2.java
new file mode 100644
index 0000000..eb6da6c
--- /dev/null
+++ b/src/main/java/io/agora/media/RtcTokenBuilder2.java
@@ -0,0 +1,203 @@
+package io.agora.media;
+
+public class RtcTokenBuilder2 {
+ public enum Role {
+ ROLE_PUBLISHER(1),
+ ROLE_SUBSCRIBER(2),
+ ;
+
+ public int initValue;
+
+ Role(int initValue) {
+ this.initValue = initValue;
+ }
+ }
+
+ /**
+ * Build the RTC token with uid.
+ *
+ * @param appId: The App ID issued to you by Agora. Apply for a new App ID from
+ * Agora Dashboard if it is missing from your kit. See Get an App ID.
+ * @param appCertificate: Certificate of the application that you registered in
+ * the Agora Dashboard. See Get an App Certificate.
+ * @param channelName: Unique channel name for the AgoraRTC session in the string format
+ * @param uid: User ID. A 32-bit unsigned integer with a value ranging from 1 to (232-1).
+ * optionalUid must be unique.
+ * @param role: ROLE_PUBLISHER: A broadcaster/host in a live-broadcast profile.
+ * ROLE_SUBSCRIBER: An audience(default) in a live-broadcast profile.
+ * @param token_expire: represented by the number of seconds elapsed since now. If, for example,
+ * you want to access the Agora Service within 10 minutes after the token is generated,
+ * set token_expire as 600(seconds).
+ * @param privilege_expire: represented by the number of seconds elapsed since now. If, for example,
+ * you want to enable your privilege for 10 minutes, set privilege_expire as 600(seconds).
+ * @return The RTC token.
+ */
+ public String buildTokenWithUid(String appId, String appCertificate, String channelName, int uid, Role role, int token_expire, int privilege_expire) {
+ return buildTokenWithUserAccount(appId, appCertificate, channelName, AccessToken2.getUidStr(uid), role, token_expire, privilege_expire);
+ }
+
+ /**
+ * Build the RTC token with account.
+ *
+ * @param appId: The App ID issued to you by Agora. Apply for a new App ID from
+ * Agora Dashboard if it is missing from your kit. See Get an App ID.
+ * @param appCertificate: Certificate of the application that you registered in
+ * the Agora Dashboard. See Get an App Certificate.
+ * @param channelName: Unique channel name for the AgoraRTC session in the string format
+ * @param account: The user's account, max length is 255 Bytes.
+ * @param role: ROLE_PUBLISHER: A broadcaster/host in a live-broadcast profile.
+ * ROLE_SUBSCRIBER: An audience(default) in a live-broadcast profile.
+ * @param token_expire: represented by the number of seconds elapsed since now. If, for example,
+ * you want to access the Agora Service within 10 minutes after the token is generated,
+ * set token_expire as 600(seconds).
+ * @param privilege_expire: represented by the number of seconds elapsed since now. If, for example,
+ * you want to enable your privilege for 10 minutes, set privilege_expire as 600(seconds).
+ * @return The RTC token.
+ */
+ public String buildTokenWithUserAccount(String appId, String appCertificate, String channelName, String account, Role role, int token_expire, int privilege_expire) {
+ AccessToken2 accessToken = new AccessToken2(appId, appCertificate, token_expire);
+ AccessToken2.Service serviceRtc = new AccessToken2.ServiceRtc(channelName, account);
+
+ serviceRtc.addPrivilegeRtc(AccessToken2.PrivilegeRtc.PRIVILEGE_JOIN_CHANNEL, privilege_expire);
+ if (role == Role.ROLE_PUBLISHER) {
+ serviceRtc.addPrivilegeRtc(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_AUDIO_STREAM, privilege_expire);
+ serviceRtc.addPrivilegeRtc(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_VIDEO_STREAM, privilege_expire);
+ serviceRtc.addPrivilegeRtc(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_DATA_STREAM, privilege_expire);
+ }
+ accessToken.addService(serviceRtc);
+
+ try {
+ return accessToken.build();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return "";
+ }
+ }
+
+ /**
+ * Generates an RTC token with the specified privilege.
+ *
+ * This method supports generating a token with the following privileges:
+ * - Joining an RTC channel.
+ * - Publishing audio in an RTC channel.
+ * - Publishing video in an RTC channel.
+ * - Publishing data streams in an RTC channel.
+ *
+ * The privileges for publishing audio, video, and data streams in an RTC channel apply only if you have
+ * enabled co-host authentication.
+ *
+ * A user can have multiple privileges. Each privilege is valid for a maximum of 24 hours.
+ * The SDK triggers the onTokenPrivilegeWillExpire and onRequestToken callbacks when the token is about to expire
+ * or has expired. The callbacks do not report the specific privilege affected, and you need to maintain
+ * the respective timestamp for each privilege in your app logic. After receiving the callback, you need
+ * to generate a new token, and then call renewToken to pass the new token to the SDK, or call joinChannel to re-join
+ * the channel.
+ *
+ * @param appId The App ID of your Agora project.
+ * @param appCertificate The App Certificate of your Agora project.
+ * @param channelName The unique channel name for the Agora RTC session in string format. The string length must be less than 64 bytes. The channel name may contain the following characters:
+ * - All lowercase English letters: a to z.
+ * - All uppercase English letters: A to Z.
+ * - All numeric characters: 0 to 9.
+ * - The space character.
+ * - "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+ * @param uid The user ID. A 32-bit unsigned integer with a value range from 1 to (232 - 1). It must be unique. Set uid as 0, if you do not want to authenticate the user ID, that is, any uid from the app client can join the channel.
+ * @param tokenExpire represented by the number of seconds elapsed since now. If, for example, you want to access the
+ * Agora Service within 10 minutes after the token is generated, set tokenExpire as 600(seconds).
+ * @param joinChannelPrivilegeExpire The Unix timestamp when the privilege for joining the channel expires, represented
+ * by the sum of the current timestamp plus the valid time period of the token. For example, if you set joinChannelPrivilegeExpire as the
+ * current timestamp plus 600 seconds, the token expires in 10 minutes.
+ * @param pubAudioPrivilegeExpire The Unix timestamp when the privilege for publishing audio expires, represented
+ * by the sum of the current timestamp plus the valid time period of the token. For example, if you set pubAudioPrivilegeExpire as the
+ * current timestamp plus 600 seconds, the token expires in 10 minutes. If you do not want to enable this privilege,
+ * set pubAudioPrivilegeExpire as the current Unix timestamp.
+ * @param pubVideoPrivilegeExpire The Unix timestamp when the privilege for publishing video expires, represented
+ * by the sum of the current timestamp plus the valid time period of the token. For example, if you set pubVideoPrivilegeExpire as the
+ * current timestamp plus 600 seconds, the token expires in 10 minutes. If you do not want to enable this privilege,
+ * set pubVideoPrivilegeExpire as the current Unix timestamp.
+ * @param pubDataStreamPrivilegeExpire The Unix timestamp when the privilege for publishing data streams expires, represented
+ * by the sum of the current timestamp plus the valid time period of the token. For example, if you set pubDataStreamPrivilegeExpire as the
+ * current timestamp plus 600 seconds, the token expires in 10 minutes. If you do not want to enable this privilege,
+ * set pubDataStreamPrivilegeExpire as the current Unix timestamp.
+ * @note Agora recommends setting a reasonable timestamp for each privilege according to your scenario.
+ * Suppose the expiration timestamp for joining the channel is set earlier than that for publishing audio.
+ * When the token for joining the channel expires, the user is immediately kicked off the RTC channel
+ * and cannot publish any audio stream, even though the timestamp for publishing audio has not expired.
+ */
+ public String buildTokenWithUid(String appId, String appCertificate, String channelName, int uid,
+ int tokenExpire, int joinChannelPrivilegeExpire, int pubAudioPrivilegeExpire,
+ int pubVideoPrivilegeExpire, int pubDataStreamPrivilegeExpire) {
+ return buildTokenWithUserAccount(appId, appCertificate, channelName, AccessToken2.getUidStr(uid),
+ tokenExpire, joinChannelPrivilegeExpire, pubAudioPrivilegeExpire, pubVideoPrivilegeExpire, pubDataStreamPrivilegeExpire);
+ }
+
+ /**
+ * Generates an RTC token with the specified privilege.
+ *
+ * This method supports generating a token with the following privileges:
+ * - Joining an RTC channel.
+ * - Publishing audio in an RTC channel.
+ * - Publishing video in an RTC channel.
+ * - Publishing data streams in an RTC channel.
+ *
+ * The privileges for publishing audio, video, and data streams in an RTC channel apply only if you have
+ * enabled co-host authentication.
+ *
+ * A user can have multiple privileges. Each privilege is valid for a maximum of 24 hours.
+ * The SDK triggers the onTokenPrivilegeWillExpire and onRequestToken callbacks when the token is about to expire
+ * or has expired. The callbacks do not report the specific privilege affected, and you need to maintain
+ * the respective timestamp for each privilege in your app logic. After receiving the callback, you need
+ * to generate a new token, and then call renewToken to pass the new token to the SDK, or call joinChannel to re-join
+ * the channel.
+ *
+ * @param appId The App ID of your Agora project.
+ * @param appCertificate The App Certificate of your Agora project.
+ * @param channelName The unique channel name for the Agora RTC session in string format. The string length must be less than 64 bytes. The channel name may contain the following characters:
+ * - All lowercase English letters: a to z.
+ * - All uppercase English letters: A to Z.
+ * - All numeric characters: 0 to 9.
+ * - The space character.
+ * - "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+ * @param account The user account.
+ * @param tokenExpire represented by the number of seconds elapsed since now. If, for example, you want to access the
+ * Agora Service within 10 minutes after the token is generated, set tokenExpire as 600(seconds).
+ * @param joinChannelPrivilegeExpire The Unix timestamp when the privilege for joining the channel expires, represented
+ * by the sum of the current timestamp plus the valid time period of the token. For example, if you set joinChannelPrivilegeExpire as the
+ * current timestamp plus 600 seconds, the token expires in 10 minutes.
+ * @param pubAudioPrivilegeExpire The Unix timestamp when the privilege for publishing audio expires, represented
+ * by the sum of the current timestamp plus the valid time period of the token. For example, if you set pubAudioPrivilegeExpire as the
+ * current timestamp plus 600 seconds, the token expires in 10 minutes. If you do not want to enable this privilege,
+ * set pubAudioPrivilegeExpire as the current Unix timestamp.
+ * @param pubVideoPrivilegeExpire The Unix timestamp when the privilege for publishing video expires, represented
+ * by the sum of the current timestamp plus the valid time period of the token. For example, if you set pubVideoPrivilegeExpire as the
+ * current timestamp plus 600 seconds, the token expires in 10 minutes. If you do not want to enable this privilege,
+ * set pubVideoPrivilegeExpire as the current Unix timestamp.
+ * @param pubDataStreamPrivilegeExpire The Unix timestamp when the privilege for publishing data streams expires, represented
+ * by the sum of the current timestamp plus the valid time period of the token. For example, if you set pubDataStreamPrivilegeExpire as the
+ * current timestamp plus 600 seconds, the token expires in 10 minutes. If you do not want to enable this privilege,
+ * set pubDataStreamPrivilegeExpire as the current Unix timestamp.
+ * @note Agora recommends setting a reasonable timestamp for each privilege according to your scenario.
+ * Suppose the expiration timestamp for joining the channel is set earlier than that for publishing audio.
+ * When the token for joining the channel expires, the user is immediately kicked off the RTC channel
+ * and cannot publish any audio stream, even though the timestamp for publishing audio has not expired.
+ */
+ public String buildTokenWithUserAccount(String appId, String appCertificate, String channelName, String account,
+ int tokenExpire, int joinChannelPrivilegeExpire, int pubAudioPrivilegeExpire,
+ int pubVideoPrivilegeExpire, int pubDataStreamPrivilegeExpire) {
+ AccessToken2 accessToken = new AccessToken2(appId, appCertificate, tokenExpire);
+ AccessToken2.Service serviceRtc = new AccessToken2.ServiceRtc(channelName, account);
+
+ serviceRtc.addPrivilegeRtc(AccessToken2.PrivilegeRtc.PRIVILEGE_JOIN_CHANNEL, joinChannelPrivilegeExpire);
+ serviceRtc.addPrivilegeRtc(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_AUDIO_STREAM, pubAudioPrivilegeExpire);
+ serviceRtc.addPrivilegeRtc(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_VIDEO_STREAM, pubVideoPrivilegeExpire);
+ serviceRtc.addPrivilegeRtc(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_DATA_STREAM, pubDataStreamPrivilegeExpire);
+ accessToken.addService(serviceRtc);
+
+ try {
+ return accessToken.build();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return "";
+ }
+ }
+}
diff --git a/src/main/java/io/agora/media/Utils.java b/src/main/java/io/agora/media/Utils.java
new file mode 100644
index 0000000..537b0e5
--- /dev/null
+++ b/src/main/java/io/agora/media/Utils.java
@@ -0,0 +1,138 @@
+package io.agora.media;
+
+import org.apache.commons.codec.binary.Base64;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.ByteArrayOutputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Date;
+import java.util.zip.CRC32;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
+
+public class Utils {
+ public static final long HMAC_SHA256_LENGTH = 32;
+ public static final int VERSION_LENGTH = 3;
+ public static final int APP_ID_LENGTH = 32;
+
+ public static byte[] hmacSign(String keyString, byte[] msg) throws InvalidKeyException, NoSuchAlgorithmException {
+ SecretKeySpec keySpec = new SecretKeySpec(keyString.getBytes(), "HmacSHA256");
+ Mac mac = Mac.getInstance("HmacSHA256");
+ mac.init(keySpec);
+ return mac.doFinal(msg);
+ }
+
+ public static byte[] pack(PackableEx packableEx) {
+ ByteBuf buffer = new ByteBuf();
+ packableEx.marshal(buffer);
+ return buffer.asBytes();
+ }
+
+ public static void unpack(byte[] data, PackableEx packableEx) {
+ ByteBuf buffer = new ByteBuf(data);
+ packableEx.unmarshal(buffer);
+ }
+
+ public static String base64Encode(byte[] data) {
+ byte[] encodedBytes = Base64.encodeBase64(data);
+ return new String(encodedBytes);
+ }
+
+ public static byte[] base64Decode(String data) {
+ return Base64.decodeBase64(data.getBytes());
+ }
+
+ public static int crc32(String data) {
+ // get bytes from string
+ byte[] bytes = data.getBytes();
+ return crc32(bytes);
+ }
+
+ public static int crc32(byte[] bytes) {
+ CRC32 checksum = new CRC32();
+ checksum.update(bytes);
+ return (int)checksum.getValue();
+ }
+
+ public static int getTimestamp() {
+ return (int)((new Date().getTime())/1000);
+ }
+
+ public static int randomInt() {
+ return new SecureRandom().nextInt();
+ }
+
+ public static boolean isUUID(String uuid) {
+ if (uuid.length() != 32) {
+ return false;
+ }
+
+ return uuid.matches("\\p{XDigit}+");
+ }
+
+ public static byte[] compress(byte[] data) {
+ byte[] output;
+ Deflater deflater = new Deflater();
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length);
+
+ try {
+ deflater.reset();
+ deflater.setInput(data);
+ deflater.finish();
+
+ byte[] buf = new byte[data.length];
+ while (!deflater.finished()) {
+ int i = deflater.deflate(buf);
+ bos.write(buf, 0, i);
+ }
+ output = bos.toByteArray();
+ } catch (Exception e) {
+ output = data;
+ e.printStackTrace();
+ } finally {
+ deflater.end();
+ }
+
+ return output;
+ }
+
+ public static byte[] decompress(byte[] data) {
+ Inflater inflater = new Inflater();
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length);
+
+ try {
+ inflater.setInput(data);
+ byte[] buf = new byte[data.length];
+ while (!inflater.finished()) {
+ int i = inflater.inflate(buf);
+ bos.write(buf, 0, i);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ inflater.end();
+ }
+
+ return bos.toByteArray();
+ }
+
+ public static String md5(String plainText) {
+ byte[] secretBytes = null;
+ try {
+ secretBytes = MessageDigest.getInstance("md5").digest(
+ plainText.getBytes());
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("No md5 digest!");
+ }
+ String md5code = new BigInteger(1, secretBytes).toString(16);
+ for (int i = 0; i < 32 - md5code.length(); i++) {
+ md5code = "0" + md5code;
+ }
+ return md5code;
+ }
+}
diff --git a/src/main/java/io/agora/rtm/RtmTokenBuilder.java b/src/main/java/io/agora/rtm/RtmTokenBuilder.java
new file mode 100644
index 0000000..5c2d286
--- /dev/null
+++ b/src/main/java/io/agora/rtm/RtmTokenBuilder.java
@@ -0,0 +1,32 @@
+package io.agora.rtm;
+
+import io.agora.media.AccessToken;
+
+public class RtmTokenBuilder {
+ public enum Role {
+ Rtm_User(1);
+
+ int value;
+ Role(int value) {
+ this.value = value;
+ }
+ }
+
+ public AccessToken mTokenCreator;
+
+ public String buildToken(String appId, String appCertificate,
+ String uid, Role role, int privilegeTs) throws Exception {
+ mTokenCreator = new AccessToken(appId, appCertificate, uid, "");
+ mTokenCreator.addPrivilege(AccessToken.Privileges.kRtmLogin, privilegeTs);
+ return mTokenCreator.build();
+ }
+
+ public void setPrivilege(AccessToken.Privileges privilege, int expireTs) {
+ mTokenCreator.addPrivilege(privilege, expireTs);
+ }
+
+ public boolean initTokenBuilder(String originToken) {
+ mTokenCreator.fromString(originToken);
+ return true;
+ }
+}
diff --git a/src/main/java/io/agora/rtm/RtmTokenBuilder2.java b/src/main/java/io/agora/rtm/RtmTokenBuilder2.java
new file mode 100644
index 0000000..dd7cba9
--- /dev/null
+++ b/src/main/java/io/agora/rtm/RtmTokenBuilder2.java
@@ -0,0 +1,33 @@
+package io.agora.rtm;
+
+import io.agora.media.AccessToken2;
+
+public class RtmTokenBuilder2 {
+
+ /**
+ * Build the RTM token.
+ *
+ * @param appId: The App ID issued to you by Agora. Apply for a new App ID from
+ * Agora Dashboard if it is missing from your kit. See Get an App ID.
+ * @param appCertificate: Certificate of the application that you registered in
+ * the Agora Dashboard. See Get an App Certificate.
+ * @param userId: The user's account, max length is 64 Bytes.
+ * @param expire: represented by the number of seconds elapsed since now. If, for example, you want to access the
+ * Agora Service within 10 minutes after the token is generated, set expireTimestamp as 600(seconds).
+ * @return The RTM token.
+ */
+ public String buildToken(String appId, String appCertificate, String userId, int expire) {
+ AccessToken2 accessToken = new AccessToken2(appId, appCertificate, expire);
+ AccessToken2.Service serviceRtm = new AccessToken2.ServiceRtm(userId);
+
+ serviceRtm.addPrivilegeRtm(AccessToken2.PrivilegeRtm.PRIVILEGE_LOGIN, expire);
+ accessToken.addService(serviceRtm);
+
+ try {
+ return accessToken.build();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return "";
+ }
+ }
+}
diff --git a/src/main/java/io/agora/sample/AccessTokenInspector.java b/src/main/java/io/agora/sample/AccessTokenInspector.java
new file mode 100644
index 0000000..35625df
--- /dev/null
+++ b/src/main/java/io/agora/sample/AccessTokenInspector.java
@@ -0,0 +1,92 @@
+package io.agora.sample;
+
+import io.agora.media.AccessToken2;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeMap;
+
+public class AccessTokenInspector {
+ public static void main(String[] args) {
+ AccessTokenInspector inspector = new AccessTokenInspector();
+ inspector.inspect(args[0]);
+ }
+
+ void inspect(String input) {
+ AccessToken2 token = new AccessToken2();
+ System.out.printf("parsing token: %s\n\n", input);
+ token.parse(input);
+ System.out.printf("appId:%s\n", token.appId);
+ System.out.printf("appCert:%s\n", token.appCert);
+ System.out.printf("salt:%d\n", token.salt);
+ System.out.printf("issueTs:%d\n", token.issueTs);
+ System.out.printf("expire:%d\n", token.expire);
+ System.out.printf("services:\n");
+
+ for (AccessToken2.Service service : token.services.values()) {
+ System.out.printf("\t{%s}\n", toServiceStr(service));
+ }
+ }
+
+ String toServiceStr(AccessToken2.Service service) {
+ if (service.getServiceType() == AccessToken2.SERVICE_TYPE_RTC) {
+ AccessToken2.ServiceRtc serviceRtc = (AccessToken2.ServiceRtc) service;
+ return String.format("type:rtc, channel:%s, uid: %s, privileges: [%s]}", serviceRtc.getChannelName(),
+ serviceRtc.getUid(), toRtcPrivileges(serviceRtc.getPrivileges()));
+ } else if (service.getServiceType() == AccessToken2.SERVICE_TYPE_RTM) {
+ AccessToken2.ServiceRtm serviceRtm = (AccessToken2.ServiceRtm) service;
+ return String.format("type:rtm, user_id:%s, privileges:[%s]", serviceRtm.getUserId(),
+ toRtmPrivileges(serviceRtm.getPrivileges()));
+ } else if (service.getServiceType() == AccessToken2.SERVICE_TYPE_CHAT) {
+ AccessToken2.ServiceChat serviceChat = (AccessToken2.ServiceChat) service;
+ return String.format("type:chat, user_id:%s, privileges:[%s]", serviceChat.getUserId(),
+ toChatPrivileges(serviceChat.getPrivileges()));
+ }
+ return "unknown";
+ }
+
+
+
+ private String toRtcPrivileges(TreeMap privileges) {
+ List privilegeStrList = new ArrayList<>(privileges.size());
+ if (privileges.containsKey(AccessToken2.PrivilegeRtc.PRIVILEGE_JOIN_CHANNEL.intValue)) {
+ privilegeStrList.add(String.format("JOIN_CHANNEL(%d)",
+ privileges.get(AccessToken2.PrivilegeRtc.PRIVILEGE_JOIN_CHANNEL.intValue)));
+ }
+ if (privileges.containsKey(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_AUDIO_STREAM.intValue)) {
+ privilegeStrList.add(String.format("PUBLISH_AUDIO_STREAM(%d)",
+ privileges.get(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_AUDIO_STREAM.intValue)));
+ }
+ if (privileges.containsKey(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_VIDEO_STREAM.intValue)) {
+ privilegeStrList.add(String.format("PUBLISH_VIDEO_STREAM(%d)",
+ privileges.get(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_VIDEO_STREAM.intValue)));
+ }
+ if (privileges.containsKey(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_DATA_STREAM.intValue)) {
+ privilegeStrList.add(String.format("PUBLISH_DATA_STREAM(%d)",
+ privileges.get(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_DATA_STREAM.intValue)));
+ }
+ return String.join(",", privilegeStrList);
+ }
+
+ private String toRtmPrivileges(TreeMap privileges) {
+ List privilegeStrList = new ArrayList<>(privileges.size());
+ if (privileges.containsKey(AccessToken2.PrivilegeRtm.PRIVILEGE_LOGIN.intValue)) {
+ privilegeStrList.add(String.format("JOIN_LOGIN(%d)",
+ privileges.get(AccessToken2.PrivilegeRtm.PRIVILEGE_LOGIN.intValue)));
+ }
+ return String.join(",", privilegeStrList);
+ }
+
+ private String toChatPrivileges(TreeMap privileges) {
+ List privilegeStrList = new ArrayList<>(privileges.size());
+ if (privileges.containsKey(AccessToken2.PrivilegeChat.PRIVILEGE_CHAT_USER.intValue)) {
+ privilegeStrList.add(String.format("USER(%d)",
+ privileges.get(AccessToken2.PrivilegeChat.PRIVILEGE_CHAT_USER.intValue)));
+ }
+ if (privileges.containsKey(AccessToken2.PrivilegeChat.PRIVILEGE_CHAT_APP.intValue)) {
+ privilegeStrList.add(String.format("APP(%d)",
+ privileges.get(AccessToken2.PrivilegeChat.PRIVILEGE_CHAT_APP.intValue)));
+ }
+ return String.join(",", privilegeStrList);
+ }
+}
diff --git a/src/main/java/io/agora/sample/ChatTokenBuilder2Sample.java b/src/main/java/io/agora/sample/ChatTokenBuilder2Sample.java
new file mode 100644
index 0000000..a488911
--- /dev/null
+++ b/src/main/java/io/agora/sample/ChatTokenBuilder2Sample.java
@@ -0,0 +1,18 @@
+package io.agora.sample;
+
+import io.agora.chat.ChatTokenBuilder2;
+
+public class ChatTokenBuilder2Sample {
+ private static String appId = "970CA35de60c44645bbae8a215061b33";
+ private static String appCertificate = "5CFd2fd1755d40ecb72977518be15d3b";
+ private static String userId = "2882341273";
+ private static int expire = 600;
+
+ public static void main(String[] args) throws Exception {
+ ChatTokenBuilder2 tokenBuilder = new ChatTokenBuilder2();
+ String userToken = tokenBuilder.buildUserToken(appId, appCertificate, userId, expire);
+ System.out.printf("Chat user token: %s\n", userToken);
+ String appToken = tokenBuilder.buildAppToken(appId, appCertificate, expire);
+ System.out.printf("Chat app token: %s\n", appToken);
+ }
+}
diff --git a/src/main/java/io/agora/sample/DynamicKey5Sample.java b/src/main/java/io/agora/sample/DynamicKey5Sample.java
new file mode 100644
index 0000000..90f767e
--- /dev/null
+++ b/src/main/java/io/agora/sample/DynamicKey5Sample.java
@@ -0,0 +1,26 @@
+package io.agora.sample;
+
+import io.agora.media.DynamicKey5;
+
+import java.util.Date;
+import java.util.Random;
+
+/**
+ * Created by Li on 10/1/2016.
+ */
+public class DynamicKey5Sample {
+ static String appID = "970ca35de60c44645bbae8a215061b33";
+ static String appCertificate = "5cfd2fd1755d40ecb72977518be15d3b";
+ static String channel = "7d72365eb983485397e3e3f9d460bdda";
+ static int ts = (int)(new Date().getTime()/1000);
+ static int r = new Random().nextInt();
+ static long uid = 2882341273L;
+ static int expiredTs = 0;
+
+ public static void main(String[] args) throws Exception {
+ System.out.println(DynamicKey5.generateMediaChannelKey(appID, appCertificate, channel, ts, r, uid, expiredTs));
+ System.out.println(DynamicKey5.generateRecordingKey(appID, appCertificate, channel, ts, r, uid, expiredTs));
+ System.out.println(DynamicKey5.generateInChannelPermissionKey(appID, appCertificate, channel, ts, r, uid, expiredTs, DynamicKey5.noUpload));
+ System.out.println(DynamicKey5.generateInChannelPermissionKey(appID, appCertificate, channel, ts, r, uid, expiredTs, DynamicKey5.audioVideoUpload));
+ }
+}
diff --git a/src/main/java/io/agora/sample/EducationTokenBuilder2Sample.java b/src/main/java/io/agora/sample/EducationTokenBuilder2Sample.java
new file mode 100644
index 0000000..d0c02d0
--- /dev/null
+++ b/src/main/java/io/agora/sample/EducationTokenBuilder2Sample.java
@@ -0,0 +1,24 @@
+package io.agora.sample;
+
+import io.agora.education.EducationTokenBuilder2;
+
+public class EducationTokenBuilder2Sample {
+ private static String appId = "970CA35de60c44645bbae8a215061b33";
+ private static String appCertificate = "5CFd2fd1755d40ecb72977518be15d3b";
+ private static String roomUuid = "123";
+ private static String userUuid = "2882341273";
+ private static Short role = 1;
+ private static int expire = 600;
+
+ public static void main(String[] args) {
+ EducationTokenBuilder2 tokenBuilder = new EducationTokenBuilder2();
+ String roomUserToken = tokenBuilder.buildRoomUserToken(appId, appCertificate, roomUuid, userUuid, role, expire);
+ System.out.printf("Education room user token: %s\n", roomUserToken);
+
+ String userToken = tokenBuilder.buildUserToken(appId, appCertificate, userUuid, expire);
+ System.out.printf("Education user token: %s\n", userToken);
+
+ String appToken = tokenBuilder.buildAppToken(appId, appCertificate, expire);
+ System.out.printf("Education app token: %s\n", appToken);
+ }
+}
diff --git a/src/main/java/io/agora/sample/FpaTokenBuilderSample.java b/src/main/java/io/agora/sample/FpaTokenBuilderSample.java
new file mode 100644
index 0000000..24973fb
--- /dev/null
+++ b/src/main/java/io/agora/sample/FpaTokenBuilderSample.java
@@ -0,0 +1,14 @@
+package io.agora.sample;
+
+import io.agora.media.FpaTokenBuilder;
+
+public class FpaTokenBuilderSample {
+ static String appId = "970CA35de60c44645bbae8a215061b33";
+ static String appCertificate = "5CFd2fd1755d40ecb72977518be15d3b";
+
+ public static void main(String[] args) {
+ FpaTokenBuilder token = new FpaTokenBuilder();
+ String result = token.buildToken(appId, appCertificate);
+ System.out.println("Token with FPA service: " + result);
+ }
+}
diff --git a/src/main/java/io/agora/sample/RtcTokenBuilder2Sample.java b/src/main/java/io/agora/sample/RtcTokenBuilder2Sample.java
new file mode 100644
index 0000000..93f7821
--- /dev/null
+++ b/src/main/java/io/agora/sample/RtcTokenBuilder2Sample.java
@@ -0,0 +1,29 @@
+package io.agora.sample;
+
+import io.agora.media.RtcTokenBuilder2;
+import io.agora.media.RtcTokenBuilder2.Role;
+
+public class RtcTokenBuilder2Sample {
+ static String appId = "970CA35de60c44645bbae8a215061b33";
+ static String appCertificate = "5CFd2fd1755d40ecb72977518be15d3b";
+ static String channelName = "7d72365eb983485397e3e3f9d460bdda";
+ static String account = "2082341273";
+ static int uid = 2082341273;
+ static int tokenExpirationInSeconds = 3600;
+ static int privilegeExpirationInSeconds = 3600;
+
+ public static void main(String[] args) {
+ RtcTokenBuilder2 token = new RtcTokenBuilder2();
+ String result = token.buildTokenWithUid(appId, appCertificate, channelName, uid, Role.ROLE_SUBSCRIBER, tokenExpirationInSeconds, privilegeExpirationInSeconds);
+ System.out.println("Token with uid: " + result);
+
+ result = token.buildTokenWithUserAccount(appId, appCertificate, channelName, account, Role.ROLE_SUBSCRIBER, tokenExpirationInSeconds, privilegeExpirationInSeconds);
+ System.out.println("Token with account: " + result);
+
+ result = token.buildTokenWithUid(appId, appCertificate, channelName, uid, privilegeExpirationInSeconds, privilegeExpirationInSeconds, privilegeExpirationInSeconds, privilegeExpirationInSeconds, privilegeExpirationInSeconds);
+ System.out.println("Token with uid and privilege: " + result);
+
+ result = token.buildTokenWithUserAccount(appId, appCertificate, channelName, account, privilegeExpirationInSeconds, privilegeExpirationInSeconds, privilegeExpirationInSeconds, privilegeExpirationInSeconds, privilegeExpirationInSeconds);
+ System.out.println("Token with account and privilege: " + result);
+ }
+}
diff --git a/src/main/java/io/agora/sample/RtcTokenBuilderSample.java b/src/main/java/io/agora/sample/RtcTokenBuilderSample.java
new file mode 100644
index 0000000..8ccb9b2
--- /dev/null
+++ b/src/main/java/io/agora/sample/RtcTokenBuilderSample.java
@@ -0,0 +1,25 @@
+package io.agora.sample;
+
+import io.agora.media.RtcTokenBuilder;
+import io.agora.media.RtcTokenBuilder.Role;
+
+public class RtcTokenBuilderSample {
+ static String appId = "970CA35de60c44645bbae8a215061b33";
+ static String appCertificate = "5CFd2fd1755d40ecb72977518be15d3b";
+ static String channelName = "7d72365eb983485397e3e3f9d460bdda";
+ static String userAccount = "2082341273";
+ static int uid = 2082341273;
+ static int expirationTimeInSeconds = 3600;
+
+ public static void main(String[] args) throws Exception {
+ RtcTokenBuilder token = new RtcTokenBuilder();
+ int timestamp = (int)(System.currentTimeMillis() / 1000 + expirationTimeInSeconds);
+ String result = token.buildTokenWithUserAccount(appId, appCertificate,
+ channelName, userAccount, Role.Role_Publisher, timestamp);
+ System.out.println(result);
+
+ result = token.buildTokenWithUid(appId, appCertificate,
+ channelName, uid, Role.Role_Publisher, timestamp);
+ System.out.println(result);
+ }
+}
diff --git a/src/main/java/io/agora/sample/RtmTokenBuilder2Sample.java b/src/main/java/io/agora/sample/RtmTokenBuilder2Sample.java
new file mode 100644
index 0000000..b50c236
--- /dev/null
+++ b/src/main/java/io/agora/sample/RtmTokenBuilder2Sample.java
@@ -0,0 +1,16 @@
+package io.agora.sample;
+
+import io.agora.rtm.RtmTokenBuilder2;
+
+public class RtmTokenBuilder2Sample {
+ private static String appId = "970CA35de60c44645bbae8a215061b33";
+ private static String appCertificate = "5CFd2fd1755d40ecb72977518be15d3b";
+ private static String userId = "test_user_id";
+ private static int expirationInSeconds = 3600;
+
+ public static void main(String[] args) {
+ RtmTokenBuilder2 token = new RtmTokenBuilder2();
+ String result = token.buildToken(appId, appCertificate, userId, expirationInSeconds);
+ System.out.println("Rtm Token: " + result);
+ }
+}
diff --git a/src/main/java/io/agora/sample/RtmTokenBuilderSample.java b/src/main/java/io/agora/sample/RtmTokenBuilderSample.java
new file mode 100644
index 0000000..01be794
--- /dev/null
+++ b/src/main/java/io/agora/sample/RtmTokenBuilderSample.java
@@ -0,0 +1,17 @@
+package io.agora.sample;
+
+import io.agora.rtm.RtmTokenBuilder;
+import io.agora.rtm.RtmTokenBuilder.Role;
+
+public class RtmTokenBuilderSample {
+ private static String appId = "970CA35de60c44645bbae8a215061b33";
+ private static String appCertificate = "5CFd2fd1755d40ecb72977518be15d3b";
+ private static String userId = "2882341273";
+ private static int expireTimestamp = 0;
+
+ public static void main(String[] args) throws Exception {
+ RtmTokenBuilder token = new RtmTokenBuilder();
+ String result = token.buildToken(appId, appCertificate, userId, Role.Rtm_User, expireTimestamp);
+ System.out.println(result);
+ }
+}
diff --git a/src/main/java/io/agora/sample/SignalingTokenSample.java b/src/main/java/io/agora/sample/SignalingTokenSample.java
new file mode 100644
index 0000000..5f89cd6
--- /dev/null
+++ b/src/main/java/io/agora/sample/SignalingTokenSample.java
@@ -0,0 +1,21 @@
+package io.agora.sample;
+
+import io.agora.signal.SignalingToken;
+
+import java.security.NoSuchAlgorithmException;
+import java.util.Date;
+
+public class SignalingTokenSample {
+
+ public static void main(String []args) throws NoSuchAlgorithmException{
+
+ String appId = "970ca35de60c44645bbae8a215061b33";
+ String certificate = "5cfd2fd1755d40ecb72977518be15d3b";
+ String account = "TestAccount";
+ //Use the current time plus an available time to guarantee the only time it is obtained
+ int expiredTsInSeconds = 1446455471 + (int) (new Date().getTime()/1000l);
+ String result = SignalingToken.getToken(appId, certificate, account, expiredTsInSeconds);
+ System.out.println(result);
+
+ }
+}
diff --git a/src/main/java/io/agora/sample/Verifier5.java b/src/main/java/io/agora/sample/Verifier5.java
new file mode 100644
index 0000000..8135049
--- /dev/null
+++ b/src/main/java/io/agora/sample/Verifier5.java
@@ -0,0 +1,90 @@
+package io.agora.sample;
+
+import io.agora.media.DynamicKey5;
+import org.apache.commons.codec.binary.Hex;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Created by liwei on 8/2/17.
+ */
+public class Verifier5 {
+ public static void main(String[] args) throws Exception {
+ if (args.length < 5) {
+ System.out.println("java io.agora.media.sample.Verifier5 appID appCertificate channelName uid channelKey");
+ return;
+ }
+
+ String appID = args[0];
+ String appCertificate = args[1];
+ String channelName = args[2];
+ int uid = Integer.parseInt(args[3]);
+ String channelKey = args[4];
+
+ DynamicKey5 key5 = new DynamicKey5();
+ if (! key5.fromString(channelKey)) {
+ System.out.println("Faile to parse key");
+ return ;
+ }
+
+ System.out.println("signature " + key5.content.signature);
+ System.out.println("appID " + new String(Hex.encodeHex(key5.content.appID, false)));
+ System.out.println("unixTs " + key5.content.unixTs);
+ System.out.println("randomInt " + key5.content.salt);
+ System.out.println("expiredTs " + key5.content.expiredTs);
+ System.out.println("extra [" + toString(key5.content.extra) + "]");
+ System.out.println("service " + key5.content.serviceType);
+
+ System.out.println();
+ System.out.println("Original \t\t " + channelKey);
+
+ if (key5.content.serviceType == DynamicKey5.MEDIA_CHANNEL_SERVICE) {
+ System.out.println("Uid = 0 \t\t " + DynamicKey5.generateMediaChannelKey(appID, appCertificate, channelName, key5.content.unixTs, key5.content.salt, 0, key5.content.expiredTs));
+ System.out.println("Uid = " + uid + " \t " + DynamicKey5.generateMediaChannelKey(appID, appCertificate, channelName, key5.content.unixTs, key5.content.salt, uid, key5.content.expiredTs));
+ } else if (key5.content.serviceType == DynamicKey5.RECORDING_SERVICE) {
+ System.out.println("Uid = 0 \t\t " + DynamicKey5.generateRecordingKey(appID, appCertificate, channelName, key5.content.unixTs, key5.content.salt, 0, key5.content.expiredTs));
+ System.out.println("Uid = " + uid + " \t " + DynamicKey5.generateRecordingKey(appID, appCertificate, channelName, key5.content.unixTs, key5.content.salt, uid, key5.content.expiredTs));
+ } else if (key5.content.serviceType == DynamicKey5.IN_CHANNEL_PERMISSION) {
+ String permission = key5.content.extra.get(DynamicKey5.ALLOW_UPLOAD_IN_CHANNEL);
+ if (permission != DynamicKey5.noUpload && permission != DynamicKey5.audioVideoUpload) {
+ System.out.println("Unknown in channel upload permission " + permission + " in extra [" + toString(key5.content.extra) + "]");
+ return ;
+ }
+ System.out.println("Uid = 0 \t\t " + DynamicKey5.generateInChannelPermissionKey(appID, appCertificate, channelName, key5.content.unixTs, key5.content.salt, 0, key5.content.expiredTs, permission));
+ System.out.println("Uid = " + uid + " \t " + DynamicKey5.generateInChannelPermissionKey(appID, appCertificate, channelName, key5.content.unixTs, key5.content.salt, uid, key5.content.expiredTs, permission));
+ } else {
+ System.out.println("Unknown service type " + key5.content.serviceType);
+ }
+
+ String signature = DynamicKey5.generateSignature(appCertificate,
+ key5.content.serviceType,
+ appID,
+ key5.content.unixTs,
+ key5.content.salt,
+ channelName,
+ uid,
+ 0,
+ key5.content.extra
+ );
+ System.out.println("generated signature " + signature);
+ }
+
+ private static String toString(TreeMap extra) {
+ String s = "";
+
+ String separator = "";
+
+ for (Map.Entry v : extra.entrySet()) {
+ s += separator;
+ s += v.getKey();
+ s += ":";
+ s += v.getValue();
+ separator = ", ";
+ }
+
+ return s;
+ }
+
+
+}
diff --git a/src/main/java/io/agora/signal/SignalingToken.java b/src/main/java/io/agora/signal/SignalingToken.java
new file mode 100644
index 0000000..6965d31
--- /dev/null
+++ b/src/main/java/io/agora/signal/SignalingToken.java
@@ -0,0 +1,33 @@
+package io.agora.signal;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class SignalingToken {
+
+ public static String getToken(String appId, String certificate, String account, int expiredTsInSeconds) throws NoSuchAlgorithmException {
+
+ StringBuilder digest_String = new StringBuilder().append(account).append(appId).append(certificate).append(expiredTsInSeconds);
+ MessageDigest md5 = MessageDigest.getInstance("MD5");
+ md5.update(digest_String.toString().getBytes());
+ byte[] output = md5.digest();
+ String token = hexlify(output);
+ String token_String = new StringBuilder().append("1").append(":").append(appId).append(":").append(expiredTsInSeconds).append(":").append(token).toString();
+ return token_String;
+ }
+
+ public static String hexlify(byte[] data) {
+
+ char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5',
+ '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ char[] toDigits = DIGITS_LOWER;
+ int l = data.length;
+ char[] out = new char[l << 1];
+ // two characters form the hex value.
+ for (int i = 0, j = 0; i < l; i++) {
+ out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
+ out[j++] = toDigits[0x0F & data[i]];
+ }
+ return String.valueOf(out);
+ }
+}
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
new file mode 100644
index 0000000..3a7f635
--- /dev/null
+++ b/src/main/resources/application.yaml
@@ -0,0 +1,22 @@
+server:
+ servlet:
+ context-path: /chat/api
+ port: 9093
+spring:
+ datasource:
+ driver-class-name: org.h2.Driver
+ schema: classpath:db/schema-h2.sql
+ data: classpath:db/data-h2.sql
+ url: jdbc:h2:mem:test
+ username: root
+ password: test
+jwt:
+ header: Authorization
+ secret: linkskk
+ expiration: 604800 #(60*60*24)
+ prefix: Bearer
+agora:
+ app-id: 809529cf18814549a0249802512a7508
+ app-certificate: ec33f39f9a56486a9b7ddc331394322e
+ token-expiration: 3600
+ privilege-expiration: 3600
\ No newline at end of file
diff --git a/src/main/resources/db/data-h2.sql b/src/main/resources/db/data-h2.sql
new file mode 100644
index 0000000..26bcb63
--- /dev/null
+++ b/src/main/resources/db/data-h2.sql
@@ -0,0 +1,4 @@
+DELETE FROM user;
+INSERT INTO user (id, username, nickname, email, password)
+VALUES (1, 'test1', 'Jone', 'test1@baomidou.com', '123456'),
+ (2, 'test2', 'Jack', 'test2@baomidou.com', '123456');
\ No newline at end of file
diff --git a/src/main/resources/db/schema-h2.sql b/src/main/resources/db/schema-h2.sql
new file mode 100644
index 0000000..a929681
--- /dev/null
+++ b/src/main/resources/db/schema-h2.sql
@@ -0,0 +1,11 @@
+DROP TABLE IF EXISTS user;
+
+CREATE TABLE user
+(
+ id BIGINT(20) NOT NULL COMMENT '主键ID',
+ username VARCHAR(30) NULL DEFAULT NULL COMMENT '用户名',
+ nickname VARCHAR(30) NULL DEFAULT NULL COMMENT '昵称',
+ email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
+ password VARCHAR(50) NULL DEFAULT NULL COMMENT '密码',
+ PRIMARY KEY (id)
+);
\ No newline at end of file
diff --git a/src/test/java/com/zhangshu/chat/demo/ChatDemoApplicationTests.java b/src/test/java/com/zhangshu/chat/demo/ChatDemoApplicationTests.java
new file mode 100644
index 0000000..17421b0
--- /dev/null
+++ b/src/test/java/com/zhangshu/chat/demo/ChatDemoApplicationTests.java
@@ -0,0 +1,13 @@
+package com.zhangshu.chat.demo;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class ChatDemoApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}