Invalid Parameter - signature length error, 0 < length < 150

Hi Everyone,

I am trying to create a zoom meeting and then join it as host. I generate the toke and then create the meeting just fine but when I try to join in I get the error above. I am including screenshots showing the meeting being created and the error I am receiving when trying to join it.

Any help is appreciated.

Hey @john8,

Happy to help! :slight_smile: Can you please share your Web SDK signature for a test meeting to developersupport@zoom.us, so I can debug it?

Thanks,
Tommy

Hi Tommy,

The signature is in both of the images above. Let me know if you need anything else.

Thanks

Hi Tommy,

Here is the error I am receiving

Any help is appreciated.

Thanks

John

Hey @john8,

After base64 decoding your signature it looks like you are generating it incorrectly:

Decoded, it should be in the following format:

jwtKey.meetingNumber.expiryTime.role.encryptedJwtSecret

Thanks,
Tommy

Hi

I have exactly the same issue, I think I am generating the signature wrong. Could you have a look at the code and potentially help me find the issue?

const timestamp = new Date().getTime() - 30000
const msg = Base64.encode(config.apiKey + config.meetingNumber + timestamp + config.role)
const hash = Base64.encode(CryptoJS.HmacSHA256(msg, API_SECRET).toString(CryptoJS.enc.Hex))
const signature = Base64.encode(`${config.apiKey}.${config.meetingNumber}.${timestamp}.${config.role}.${hash}`)

Hi, I just got this working then I was called off to do something else so the code may be a little sloppy but here are rough steps to get a token, then create, start and join a meeting. This is in ASP.NET MVC with C# and jQuery.

First, generate a token

static readonly char padding = { ā€˜=ā€™ };

public static string GenerateToken(string apiKey, string apiSecret, string meetingNumber, string ts, string role)

{

string message = String.Format("{0}{1}{2}{3}", apiKey, meetingNumber, ts, role);

apiSecret = apiSecret ?? ā€œā€;

var encoding = new System.Text.ASCIIEncoding();

byte keyByte = encoding.GetBytes(apiSecret);

byte messageBytesTest = encoding.GetBytes(message);

string msgHashPreHmac = System.Convert.ToBase64String(messageBytesTest);

byte messageBytes = encoding.GetBytes(msgHashPreHmac);

using (var hmacsha256 = new HMACSHA256(keyByte))

{

byte hashmessage = hmacsha256.ComputeHash(messageBytes);

string msgHash = System.Convert.ToBase64String(hashmessage);

string token = String.Format("{0}.{1}.{2}.{3}.{4}", apiKey, meetingNumber, ts, role, msgHash);

var tokenBytes = System.Text.Encoding.UTF8.GetBytes(token);

return System.Convert.ToBase64String(tokenBytes).TrimEnd(padding);

}

}

Next, create a meeting. This is fetched in a .js file. The token passed in is the token created above. From the json, you can parse out the Meeting ID (id) and Meeting Password (password) for the new meeting and store it in the fields MeetingNumber and Password on the page.

public async Task CreateMeetingAsync(string token)

{

var stringContent = ā€œ{ā€œtopicā€: ā€œTest Meetingā€,ā€œtypeā€: ā€œ1ā€,ā€œtimezoneā€: ā€œAmerica/Chicagoā€,ā€œpasswordā€: ā€œpasswordā€,ā€œagendaā€: ā€œTest Agendaā€,ā€ +

ā€œā€œsettingsā€: {ā€œhost_videoā€: ā€œtrueā€,ā€œparticipant_videoā€: ā€œfalseā€,ā€œcn_meetingā€: ā€œfalseā€,ā€œin_meetingā€: ā€œfalseā€,ā€œjoin_before_hostā€: ā€œfalseā€,ā€ +

ā€œā€œmute_upon_entryā€: ā€œfalseā€,ā€œwatermarkā€: ā€œfalseā€,ā€œuse_pmiā€: ā€œfalseā€,ā€œapproval_typeā€: ā€œ0ā€,ā€œregistration_typeā€: ā€œ1ā€,ā€œaudioā€: ā€œbothā€,ā€œauto_recordingā€: ā€œnoneā€}}ā€;

HttpClient client = new HttpClient();

client.BaseAddress = new Uri(ā€œhttps://api.zoom.us/v2/users/yourZoom@emailAddress.com/meetingsā€);

client.DefaultRequestHeaders

.Accept

.Add(new MediaTypeWithQualityHeaderValue(ā€œapplication/jsonā€));//ACCEPT header

client.DefaultRequestHeaders.Add(ā€œAuthorizationā€, "Bearer " + token);

HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, ā€œmeetingsā€);

request.Content = new StringContent(stringContent,

Encoding.UTF8,

ā€œapplication/jsonā€);//CONTENT-TYPE header

HttpResponseMessage response = new HttpResponseMessage();

await client.SendAsync(request)

.ContinueWith(responseTask =>

{

Console.WriteLine(ā€œResponse: {0}ā€, responseTask.Result);

response = responseTask.Result;

});

var json = response.Content.ReadAsStringAsync();

return Json(json);

}

Then you will need to generate an access token for the meeting id you just received. Again, this is fetched in the .js file. I store this in a field called MeetingToken on the page.

public ActionResult GetMeetingAccessToken(string meetingNumber)

{

String ts = (ToTimestamp(DateTime.UtcNow.ToUniversalTime()) - 30000).ToString();

string role = ā€œ1ā€;

var result = GenerateToken(apiKey, apiSecret, meetingNumber, ts, role);

return Json(result);

}

Now you should have all the info you need to join the meeting via ZoomMtg. Below is a snippet of code from the .js file, $(ā€˜#Roleā€™) is 1

function joinMeeting() {

meetConfig.apiKey = API_KEY;

meetConfig.signature = $(ā€™#MeetingTokenā€™).val();

meetConfig.userName = $(ā€™#Nameā€™).val();

meetConfig.passWord = $(ā€™#Passwordā€™).val();

meetConfig.role = $(ā€™#Roleā€™).val();

meetConfig.meetingNumber = $(ā€™#MeetingNumberā€™).val();

ZoomMtg.init({

leaveUrl: meetConfig.leaveUrl,

isSupportAV: true,

debug: true,

success: function (res) {

ZoomMtg.join({

signature: meetConfig.signature,

apiKey: meetConfig.apiKey,

meetingNumber: meetConfig.meetingNumber,

userEmail: meetConfig.userEmail,

userName: meetConfig.userName,

passWord: meetConfig.passWord,

success: function (res) {

console.log(ā€˜successā€™);

},

error: function (res) {

console.log(ā€˜errorā€™);

console.log(res);

},

})

},

error: function (error) {

console.log(ā€˜errorā€™);

}

});

ZoomMtg.inMeetingServiceListener(ā€˜onUserJoinā€™, function (data) {

console.log(ā€˜inMeetingServiceListener onUserJoinā€™, data);

});

ZoomMtg.inMeetingServiceListener(ā€˜onUserLeaveā€™, function (data) {

console.log(ā€˜inMeetingServiceListener onUserLeaveā€™, data);

});

ZoomMtg.inMeetingServiceListener(ā€˜onUserIsInWaitingRoomā€™, function (data) {

console.log(ā€˜inMeetingServiceListener onUserIsInWaitingRoomā€™, data);

});

ZoomMtg.inMeetingServiceListener(ā€˜onMeetingStatusā€™, function (data) {

// {status: 1(connecting), 2(connected), 3(disconnected), 4(reconnecting)}

console.log(ā€˜inMeetingServiceListener onMeetingStatusā€™, data);

});

}

Hope this helps!

Thanks a lot. I just got it working by simply using the method already available in the API:

ZoomMtg.generateSignature(...)

Hey @sdk_testing,

Iā€™m glad to hear that @john8ā€™s post was helpful and that you were able to get it figured out. As always, feel free to reach out if you encounter any further issues or questions.

Thanks,
Max

hiā€¦ i used the sample code in PHP from zoom link https://marketplace.zoom.us/docs/sdk/native-sdks/web/build/signature to generate signature but gives me error message that " signatrue length error, 0 < length < 150" ā€¦

i tried this formula jwtKey.meetingNumber.expiryTime.role.encryptedJwtSecret
by doing this
$_sig = $api_key . ā€œ.ā€ . $meeting_number . ā€œ.ā€ . $time . ā€œ.ā€ . $role . ā€œ.ā€ . api_secret;** **return rtrim(strtr(base64_encode(sig), ā€˜+/ā€™, '-ā€™), ā€˜=ā€™);
but i still get the same error
any help?

Hi qwakuaa,

Did you use the following to generate your signature?

function generate_signature ( $api_key, $api_secret, $meeting_number, $role){

	$time = time() * 1000 - 30000;//time in milliseconds (or close enough)
	
	$data = base64_encode($api_key . $meeting_number . $time . $role);
	
	$hash = hash_hmac('sha256', $data, $api_secret, true);
	
	$_sig = $api_key . "." . $meeting_number . "." . $time . "." . $role . "." . base64_encode($hash);
	
	//return signature, url safe base64 encoded
	return rtrim(strtr(base64_encode($_sig), '+/', '-_'), '=');
}

You can test if it is correct by decoding it here https://www.base64decode.org/

Thanks

working nowā€¦ i changed return to echo

1 Like

Happy to hear you got it working @qwakuaa ! :slight_smile:

Thanks for your suggestion @john8 !

Thanks,
Tommy

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.