I ultimately decided it would be easiest to just call ditto
as a subprocess, since it will extract the aliases properly
var dittoProcess = new Process();
var args = $"-k -x \"{archivePath}\" \"{outDirectory}\"";
dittoProcess.StartInfo = new ProcessStartInfo("/usr/bin/ditto", args);
dittoProcess.Start();
dittoProcess.WaitForExit();