Your strategy is fine, but your code has a bug, and is more complicated than it needs to be.
The usual problem with using the built-in serial number is that it isn't the same size (bytes) as the "unique IDs" that you might need (ie ethernet addresses, which are 6 bytes.)
The bug:
msg[21]=0x00; //zero terminate string for tx library
arrays are 0-based, so the 21st element of msg is msg[20]; Right now, you're overwriting data one past the end of the array. Who knows what that'll do...
Simpler:
The chip serial number is is normal memory, and is sequential, so you can eliminate the intermediate array and do something like:
#define SERNUM_SIZE 10 // Number of bytes in the on-chip serial number
byte msg[SERNUM_SIZE*2 + 1]; //array for ASCII HEX representation of serial number
const byte *serno = &SIGROW.SERNUM0; // pointer to the sernums
for (byte i=0;i<SERNUM_SIZE;++i) //convert 10 byte serial number to 20 byte ASCII HEX serial number
{
msg[2*i]=table[(serno[i]>>4) &0x0F]; //convert high nibl. Shift right 4 & mask & lookup
msg[2*i+1]=table[serno[i] &0x0F]; //low nibl. Mask & lookup
}
msg[2*SERNUM_SIZE] = 0;