A simple solution that doesn't check for the length of the string:
\documentclass{article}
\makeatletter
\newcommand{\printpgp}[1]{\print@pgp#1\@nil}
\def\print@pgp#1#2#3#4#5\@nil{%
#1#2#3#4%
\ifx\\#5\\%
\expandafter\@gobble
\else
\expandafter\@firstofone
\fi
{~\print@pgp#5\@nil}%
}
\makeatother
\begin{document}
$|$\printpgp{47D6459B2DCEE5C1439C53330403A28B2D8DE8FB}$|$
\end{document}
I added two bars at the sides just to show that no spurious space has been added.

If you want to print the fingerprint in monospaced font, just change the first definition into
\newcommand{\printpgp}[1]{\texttt{\print@pgp#1\@nil}}

More features. If the length is wrong, then “INVALID PGP” is printed. Spaces in the strings are ignored. You can specify a key-value interface: break is by default true, but with break=false the key is printed on one line. With font you can specify a font choice (default \ttfamily).
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\printpgp}{O{}m}
{
\par\noindent
\group_begin:
\keys_set:nn { xxiidecembre/printpgp } { #1 }
\xxiidecembre_printpgp:n { #2 }
\par
\group_end:
}
\keys_define:nn { xxiidecembre/printpgp }
{
font .tl_set:N = \l_xxiidecembre_pgpfont_tl,
font .initial:n = \ttfamily,
break .bool_set:N = \l_xxiidecembre_pgpbreak_bool,
break .initial:n = true,
}
\tl_new:N \l__xiidecembre_pgp_tl
\int_new:N \l__xxiidecembre_step_int
\int_new:N \l__xxiidecembre_group_int
\cs_new_protected:Npn \xxiidecembre_printpgp:n #1
{
\tl_use:N \l_xxiidecembre_pgpfont_tl
\tl_set:Nn \l__xiidecembre_pgp_tl { #1 }
\tl_replace_all:Nnn \l__xiidecembre_pgp_tl { ~ } { }
\int_compare:nTF { \tl_count:N \l__xiidecembre_pgp_tl = 40 }
{
\__xxiidecembre_printpgp:V \l__xiidecembre_pgp_tl
}
{
INVALID~PGP
}
}
\cs_new_protected:Npn \__xxiidecembre_printpgp:n #1
{
\tl_map_inline:nn { #1 }
{
##1
\int_incr:N \l__xxiidecembre_step_int
\int_compare:nT { \l__xxiidecembre_step_int = 4 }
{
\int_incr:N \l__xxiidecembre_group_int
\int_compare:nTF { \l__xxiidecembre_group_int = 5 }
{
\bool_if:NTF \l_xxiidecembre_pgpbreak_bool { \\ } { ~ }
}
{
\c_space_tl
}
\int_zero:N \l__xxiidecembre_step_int
}
}
}
\cs_generate_variant:Nn \__xxiidecembre_printpgp:n { V }
\ExplSyntaxOff
\begin{document}
\printpgp{47D6459B2DCEE5C1439C53330403A28B2D8DE8FB}
\medskip
\printpgp[break=false]{47D6459B2DCEE5C1439C53330403A28B2D8DE8FB}
\medskip
\printpgp[font=\ttfamily\LARGE]{47D6 459B 2DCE E5C1 439C 5333 0403 A28B 2D8D E8FB}
\medskip
\printpgp{0000 1111 2222}
\end{document}

First spaces are removed, then the input is mapped character by character, incrementing a counter at each step; if the counter is at 4, the group counter is stepped; a space is issued, but \\ is used if the group counter is 5 and break=true is in force.
\documentclass{...}and ending with\end{document}. What are all those wiggly bits? Is there any rhyme or reason to the transformation? (Apart from the spaces. Why so many things->5?) – cfr Jan 31 '15 at 23:23