diff --git a/configs/samples/stir_shaken.conf.sample b/configs/samples/stir_shaken.conf.sample
index 1bd260641bb3801764a75b296056d97fafbda4f5..c39bc9726530e0366e4f978ae72b8dd3d2c61658 100644
--- a/configs/samples/stir_shaken.conf.sample
+++ b/configs/samples/stir_shaken.conf.sample
@@ -83,6 +83,3 @@
 ;
 ; Must have an attestation of A, B, or C
 ;attestation=C
-;
-; The origination identifier for the certificate
-;origid=MyAsterisk
diff --git a/doc/UPGRADE-staging/stir_shaken_origid.txt b/doc/UPGRADE-staging/stir_shaken_origid.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f0b897757f49f603174b0d69d4749e45f886add5
--- /dev/null
+++ b/doc/UPGRADE-staging/stir_shaken_origid.txt
@@ -0,0 +1,8 @@
+Subject: STIR/SHAKEN
+
+STIR/SHAKEN originally needed an origid to be specified in
+stir_shaken.conf under the certificate config object in
+order to work. Now, one is automatically created by
+generating a UUID, as recommended by RFC8588. Any origid
+you have in your stir_shaken.conf will need to be removed
+for the module to read in certificates.
diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h
index 0ee11ee5d53a95c646b57ae722df0cfd63a7c024..08120bf2204c4ba0238fd086252f814b1aac8e5c 100644
--- a/include/asterisk/utils.h
+++ b/include/asterisk/utils.h
@@ -276,6 +276,66 @@ int ast_base64decode(unsigned char *dst, const char *src, int max);
  */
 char *ast_base64decode_string(const char *src);
 
+/*!
+ * \brief Decode data from base64 URL
+ *
+ * \param dst The destination buffer
+ * \param src The source buffer
+ * \param max The maximum number of bytes to write into the destination
+ *            buffer. Note that this function will not ensure that the
+ *            destination buffer is NULL terminated. So, in general,
+ *            this parameter should be sizeof(dst) - 1
+ */
+int ast_base64url_decode(unsigned char *dst, const char *src, int max);
+
+/*!
+ * \brief Same as ast_base64encode_full but for base64 URL
+ *
+ * \param dst The destination buffer
+ * \param src The source buffer
+ * \param srclen The number of bytes present in the source buffer
+ * \param max The maximum number of bytes to write into the destination
+ *            buffer, *including* the terminating NULL character.
+ * \param linebreaks Set to 1 if there should be linebreaks inserted
+ *                   in the result
+ */
+int ast_base64url_encode_full(char *dst, const unsigned char *src, int srclen, int max, int linebreaks);
+
+/*!
+ * \brief Encode data in base64 URL
+ *
+ * \param dst The destination buffer
+ * \param src The source data to be encoded
+ * \param srclen The number of bytes present in the source buffer
+ * \param max The maximum number of bytes to write into the destination
+ *            buffer, including the terminating NULL character
+ */
+int ast_base64url_encode(char *dst, const unsigned char *src, int srclen, int max);
+
+/*!
+ * \brief Decode string from base64 URL
+ *
+ * \note The returned string will need to be freed later
+ *
+ * \param src The source buffer
+ *
+ * \retval NULL on failure
+ * \retval Decoded string on success
+ */
+char *ast_base64url_decode_string(const char *src);
+
+/*!
+ * \brief Encode string in base64 URL
+ *
+ * \note The returned string will need to be freed later
+ *
+ * \param src The source data to be encoded
+ *
+ * \retval NULL on failure
+ * \retval Encoded string on success
+ */
+char *ast_base64url_encode_string(const char *src);
+
 #define AST_URI_ALPHANUM     (1 << 0)
 #define AST_URI_MARK         (1 << 1)
 #define AST_URI_UNRESERVED   (AST_URI_ALPHANUM | AST_URI_MARK)
diff --git a/main/utils.c b/main/utils.c
index 827ee2e57aaec3fbb24285e92e7cb7f067007334..c6e71d9fd26f315e6ef41544aa6f94b97c79f290 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -70,8 +70,15 @@
 #define AST_API_MODULE
 #include "asterisk/alertpipe.h"
 
+/* These arrays are global static variables because they are only modified
+ * once - in base64_init. The only purpose they have is to serve as a dictionary
+ * for encoding and decoding base64 and base64 URL, so there's no harm in
+ * accessing these arrays in multiple threads.
+ */
 static char base64[64];
+static char base64url[64];
 static char b2a[256];
+static char b2a_url[256];
 
 AST_THREADSTORAGE(inet_ntoa_buf);
 
@@ -417,28 +424,150 @@ char *ast_base64encode_string(const char *src)
 	return encoded_string;
 }
 
+int ast_base64url_decode(unsigned char *dst, const char *src, int max)
+{
+	int cnt = 0;
+	unsigned int byte = 0;
+	unsigned int bits = 0;
+
+	while (*src && (cnt < max)) {
+		byte <<= 6;
+		byte |= (b2a_url[(int)(*src)]) & 0x3f;
+		bits += 6;
+		src++;
+		if (bits >= 8) {
+			bits -= 8;
+			*dst = (byte >> bits) & 0xff;
+			dst++;
+			cnt++;
+		}
+	}
+	return cnt;
+}
+
+char *ast_base64url_decode_string(const char *src)
+{
+	size_t decoded_len;
+	unsigned char *decoded_string;
+
+	if (ast_strlen_zero(src)) {
+		return NULL;
+	}
+
+	decoded_len = strlen(src) * 3 / 4;
+	decoded_string = ast_malloc(decoded_len + 1);
+	if (!decoded_string) {
+		return NULL;
+	}
+
+	ast_base64url_decode(decoded_string, src, decoded_len);
+	decoded_string[decoded_len] = '\0';
+
+	return (char *)decoded_string;
+}
+
+int ast_base64url_encode_full(char *dst, const unsigned char *src, int srclen, int max, int linebreaks)
+{
+	int cnt = 0;
+	int col = 0;
+	unsigned int byte = 0;
+	int bits = 0;
+	int cntin = 0;
+
+	max--;
+	while ((cntin < srclen) && (cnt < max)) {
+		byte <<= 8;
+		byte |= *(src++);
+		bits += 8;
+		cntin++;
+		if ((bits == 24) && (cnt + 4 <= max)) {
+			*dst++ = base64url[(byte >> 18) & 0x3f];
+			*dst++ = base64url[(byte >> 12) & 0x3f];
+			*dst++ = base64url[(byte >> 6) & 0x3f];
+			*dst++ = base64url[(byte) & 0x3f];
+			cnt += 4;
+			col += 4;
+			bits = 0;
+			byte = 0;
+		}
+		if (linebreaks && (cnt < max) && (col == 64)) {
+			*dst++ = '\n';
+			cnt++;
+			col = 0;
+		}
+	}
+	if (bits && (cnt + 4 <= max)) {
+		byte <<= 24 - bits;
+		*dst++ = base64url[(byte >> 18) & 0x3f];
+		*dst++ = base64url[(byte >> 12) & 0x3f];
+		if (bits == 16) {
+			*dst++ = base64url[(byte >> 6) & 0x3f];
+		}
+		cnt += 4;
+	}
+	if (linebreaks && (cnt < max)) {
+		*dst++ = '\n';
+		cnt++;
+	}
+	*dst = '\0';
+	return cnt;
+}
+
+int ast_base64url_encode(char *dst, const unsigned char *src, int srclen, int max)
+{
+	return ast_base64url_encode_full(dst, src, srclen, max, 0);
+}
+
+char *ast_base64url_encode_string(const char *src)
+{
+	size_t encoded_len;
+	char *encoded_string;
+
+	if (ast_strlen_zero(src)) {
+		return NULL;
+	}
+
+	encoded_len = ((strlen(src) * 4 / 3 + 3) & ~3) + 1;
+	encoded_string = ast_malloc(encoded_len);
+
+	ast_base64url_encode(encoded_string, (const unsigned char *)src, strlen(src), encoded_len);
+
+	return encoded_string;
+}
+
 static void base64_init(void)
 {
 	int x;
 	memset(b2a, -1, sizeof(b2a));
+	memset(b2a_url, -1, sizeof(b2a_url));
 	/* Initialize base-64 Conversion table */
 	for (x = 0; x < 26; x++) {
 		/* A-Z */
 		base64[x] = 'A' + x;
+		base64url[x] = 'A' + x;
 		b2a['A' + x] = x;
+		b2a_url['A' + x] = x;
 		/* a-z */
 		base64[x + 26] = 'a' + x;
+		base64url[x + 26] = 'a' + x;
 		b2a['a' + x] = x + 26;
+		b2a_url['a' + x] = x + 26;
 		/* 0-9 */
 		if (x < 10) {
 			base64[x + 52] = '0' + x;
+			base64url[x + 52] = '0' + x;
 			b2a['0' + x] = x + 52;
+			b2a_url['0' + x] = x + 52;
 		}
 	}
 	base64[62] = '+';
 	base64[63] = '/';
+	base64url[62] = '-';
+	base64url[63] = '_';
 	b2a[(int)'+'] = 62;
 	b2a[(int)'/'] = 63;
+	b2a_url[(int)'-'] = 62;
+	b2a_url[(int)'_'] = 63;
 }
 
 const struct ast_flags ast_uri_http = {AST_URI_UNRESERVED};
diff --git a/res/res_pjsip_stir_shaken.c b/res/res_pjsip_stir_shaken.c
index 351d7ccf29ca0f9ac77b232c61875a23feb474c3..a90b821e553e4feadfc5aeca7bccbb74ae2af483 100644
--- a/res/res_pjsip_stir_shaken.c
+++ b/res/res_pjsip_stir_shaken.c
@@ -146,14 +146,14 @@ static int stir_shaken_incoming_request(struct ast_sip_session *session, pjsip_r
 	}
 
 	encoded_val = strtok_r(identity_hdr_val, ".", &identity_hdr_val);
-	header = ast_base64decode_string(encoded_val);
+	header = ast_base64url_decode_string(encoded_val);
 	if (ast_strlen_zero(header)) {
 		ast_stir_shaken_add_verification(chan, caller_id, "", AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED);
 		return 0;
 	}
 
 	encoded_val = strtok_r(identity_hdr_val, ".", &identity_hdr_val);
-	payload = ast_base64decode_string(encoded_val);
+	payload = ast_base64url_decode_string(encoded_val);
 	if (ast_strlen_zero(payload)) {
 		ast_stir_shaken_add_verification(chan, caller_id, "", AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED);
 		return 0;
@@ -241,7 +241,7 @@ static void add_identity_header(const struct ast_sip_session *session, pjsip_tx_
 
 	header = ast_json_object_get(json, "header");
 	dumped_string = ast_json_dump_string(header);
-	encoded_header = ast_base64encode_string(dumped_string);
+	encoded_header = ast_base64url_encode_string(dumped_string);
 	ast_json_free(dumped_string);
 	if (!encoded_header) {
 		ast_log(LOG_ERROR, "Failed to encode STIR/SHAKEN header\n");
@@ -250,7 +250,7 @@ static void add_identity_header(const struct ast_sip_session *session, pjsip_tx_
 
 	payload = ast_json_object_get(json, "payload");
 	dumped_string = ast_json_dump_string(payload);
-	encoded_payload = ast_base64encode_string(dumped_string);
+	encoded_payload = ast_base64url_encode_string(dumped_string);
 	ast_json_free(dumped_string);
 	if (!encoded_payload) {
 		ast_log(LOG_ERROR, "Failed to encode STIR/SHAKEN payload\n");
diff --git a/res/res_stir_shaken.c b/res/res_stir_shaken.c
index f8eb97fe4fd3617693e14f6e5cb5d837f16d4f3a..dbc2de08c47809c2ce269ff8d40ff1f92f0147e6 100644
--- a/res/res_stir_shaken.c
+++ b/res/res_stir_shaken.c
@@ -104,9 +104,6 @@
 				<configOption name="attestation">
 					<synopsis>Attestation level</synopsis>
 				</configOption>
-				<configOption name="origid" default="">
-					<synopsis>The origination ID</synopsis>
-				</configOption>
 				<configOption name="caller_id_number" default="">
 					<synopsis>The caller ID number to match on.</synopsis>
 				</configOption>
@@ -503,7 +500,7 @@ static int stir_shaken_verify_signature(const char *msg, const char *signature,
 	EVP_MD_CTX *mdctx = NULL;
 	int ret = 0;
 	unsigned char *decoded_signature;
-	size_t signature_length, decoded_signature_length, padding = 0;
+	size_t signature_length, decoded_signature_length;
 
 	mdctx = EVP_MD_CTX_create();
 	if (!mdctx) {
@@ -525,19 +522,12 @@ static int stir_shaken_verify_signature(const char *msg, const char *signature,
 		return -1;
 	}
 
-	/* We need to decode the signature from base64 to bytes. Make sure we have
+	/* We need to decode the signature from base64 URL to bytes. Make sure we have
 	 * at least enough characters for this check */
 	signature_length = strlen(signature);
-	if (signature_length > 2 && signature[signature_length - 1] == '=') {
-		padding++;
-		if (signature[signature_length - 2] == '=') {
-			padding++;
-		}
-	}
-
-	decoded_signature_length = (signature_length / 4 * 3) - padding;
+	decoded_signature_length = (signature_length * 3 / 4);
 	decoded_signature = ast_calloc(1, decoded_signature_length);
-	ast_base64decode(decoded_signature, signature, decoded_signature_length);
+	ast_base64url_decode(decoded_signature, signature, decoded_signature_length);
 
 	ret = EVP_DigestVerifyFinal(mdctx, decoded_signature, decoded_signature_length);
 	if (ret != 1) {
@@ -944,7 +934,7 @@ static unsigned char *stir_shaken_sign(char *json_str, EVP_PKEY *private_key)
 		goto cleanup;
 	}
 
-	/* There are 6 bits to 1 base64 digit, so in order to get the size of the base64 encoded
+	/* There are 6 bits to 1 base64 URL digit, so in order to get the size of the base64 encoded
 	 * signature, we need to multiply by the number of bits in a byte and divide by 6. Since
 	 * there's rounding when doing base64 conversions, add 3 bytes, just in case, and account
 	 * for padding. Add another byte for the NULL-terminator.
@@ -956,7 +946,7 @@ static unsigned char *stir_shaken_sign(char *json_str, EVP_PKEY *private_key)
 		goto cleanup;
 	}
 
-	ast_base64encode((char *)encoded_signature, signature, signature_length, encoded_length);
+	ast_base64url_encode((char *)encoded_signature, signature, signature_length, encoded_length);
 
 cleanup:
 	if (mdctx) {
@@ -1013,20 +1003,22 @@ static int stir_shaken_add_attest(struct ast_json *json, const char *attest)
  * \brief Adds the 'origid' field to the JWT.
  *
  * \param json The JWT
- * \param origid The value to set origid to
  *
  * \retval 0 on success
  * \retval -1 on failure
  */
-static int stir_shaken_add_origid(struct ast_json *json, const char *origid)
+static int stir_shaken_add_origid(struct ast_json *json)
 {
 	struct ast_json *value;
+	char uuid_str[AST_UUID_STR_LEN];
 
-	value = ast_json_string_create(origid);
-	if (!origid) {
+	ast_uuid_generate_str(uuid_str, sizeof(uuid_str));
+	if (strlen(uuid_str) != (AST_UUID_STR_LEN - 1)) {
 		return -1;
 	}
 
+	value = ast_json_string_create(uuid_str);
+
 	return ast_json_object_set(ast_json_object_get(json, "payload"), "origid", value);
 }
 
@@ -1097,7 +1089,7 @@ struct ast_stir_shaken_payload *ast_stir_shaken_sign(struct ast_json *json)
 		goto cleanup;
 	}
 
-	if (stir_shaken_add_origid(json, stir_shaken_certificate_get_origid(cert))) {
+	if (stir_shaken_add_origid(json)) {
 		ast_log(LOG_ERROR, "Failed to add 'origid' to payload\n");
 		goto cleanup;
 	}
diff --git a/res/res_stir_shaken/certificate.c b/res/res_stir_shaken/certificate.c
index f4103f96ecf1821feefbb30e07c0021d351c136b..df4f38b8f76d57cfa1eaaaba3283fda973b739b2 100644
--- a/res/res_stir_shaken/certificate.c
+++ b/res/res_stir_shaken/certificate.c
@@ -40,8 +40,6 @@ struct stir_shaken_certificate {
 		AST_STRING_FIELD(caller_id_number);
 		/*! The attestation level for this certificate */
 		AST_STRING_FIELD(attestation);
-		/*! The origination ID for this certificate */
-		AST_STRING_FIELD(origid);
 	);
 	/*! The private key for the certificate */
 	EVP_PKEY *private_key;
@@ -105,11 +103,6 @@ const char *stir_shaken_certificate_get_attestation(struct stir_shaken_certifica
 	return cert ? cert->attestation : NULL;
 }
 
-const char *stir_shaken_certificate_get_origid(struct stir_shaken_certificate *cert)
-{
-	return cert ? cert->origid : NULL;
-}
-
 EVP_PKEY *stir_shaken_certificate_get_private_key(struct stir_shaken_certificate *cert)
 {
 	return cert ? cert->private_key : NULL;
@@ -378,7 +371,6 @@ int stir_shaken_certificate_load(void)
 		on_load_public_cert_url, public_cert_url_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register_custom(sorcery, CONFIG_TYPE, "attestation", "",
 		on_load_attestation, attestation_to_str, NULL, 0, 0);
-	ast_sorcery_object_field_register(sorcery, CONFIG_TYPE, "origid", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct stir_shaken_certificate, origid));
 	ast_sorcery_object_field_register(sorcery, CONFIG_TYPE, "caller_id_number", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct stir_shaken_certificate, caller_id_number));
 
 	ast_cli_register_multiple(stir_shaken_certificate_cli,
diff --git a/res/res_stir_shaken/certificate.h b/res/res_stir_shaken/certificate.h
index 9574d46795051cf1e2a4842574d6b5fa83902aaa..c95cba56b6f5494598e16b3cab10cfbcd0e6a897 100644
--- a/res/res_stir_shaken/certificate.h
+++ b/res/res_stir_shaken/certificate.h
@@ -54,16 +54,6 @@ const char *stir_shaken_certificate_get_public_cert_url(struct stir_shaken_certi
  */
 const char *stir_shaken_certificate_get_attestation(struct stir_shaken_certificate *cert);
 
-/*!
- * \brief Get the origination ID associated with a certificate
- *
- * \param cert The certificate
- *
- * \retval NULL on failure
- * \retval The origid on success
- */
-const char *stir_shaken_certificate_get_origid(struct stir_shaken_certificate *cert);
-
 /*!
  * \brief Get the private key associated with a certificate
  *