Using xidel I'm extracting the //Assertion//Signature//KeyInfo//X509Certificate/text() from a SAMLResponse, this is a X509 certificate as a long base64 string.
I want to split this string into 64 chars blocks
I tried with tokenize() and replace() but I could make those work,
It seems that replace() does not allow me to use newlines \n in the replacement string:
echo "$SAMLRESPONSE" | base64 -D | xidel --xpath 'replace(//Assertion//Signature//KeyInfo//X509Certificate/text(),"(.{64})","$1\n")' -
**** Processing: stdin:/// ****
Error:
err:FORX0004: Invalid replacement: $1\n after $1\n
Possible backtrace:
$000000010203F668: perhaps TXQTermTryCatch + 222920 ? but unlikely
$0000000102068BBE: perhaps Q{http://www.w3.org/2005/xpath-functions}tokenize + 166350 ? but unlikely
$000000010203FF78: Q{http://www.w3.org/2005/xpath-functions}replace + 376
$0000000101FF853F: TXQTermNamedFunction + 767
$0000000101F71CE7: perhaps ? ? but unlikely
Call xidel with --trace-stack to get an actual backtrace
And tokenize will treat the whole match as separator, and separator are not included in the output
echo "$SAMLRESPONSE" | base64 -D | xidel --xpath 'tokenize(//Assertion//Signature//KeyInfo//X509Certificate/text(),"(?:.{64})")' -
**** Processing: stdin:/// ****
XACcI5tcJbgsvr+ivGPos/WrhywkROwbEBh6OTNXTnaBiiIK
Is there any way to do split a string in fixed width chunks in XPath?
Your first idea wasn't wrong, you just have to use the codepoints-to-string function to generate the newline character:
printf %s "$SAMLRESPONSE" |
base64 -D |
xidel --xpath '
let
$cert := //Assertion//Signature//KeyInfo//X509Certificate
return
"-----BEGIN CERTIFICATE-----" || codepoints-to-string(10) ||
replace( $cert, ".{1,64}", "$0" || codepoints-to-string(10) ) ||
"-----END CERTIFICATE-----" || codepoints-to-string(10)
' -
note: I modified the regex to .{1,64} to make sure that the "replaced" string always ends with a linefeed
ASIDE: In the first place, you don't even need to build the full output with XPath.
{
echo '-----BEGIN CERTIFICATE-----'
printf %s "$SAMLRESPONSE" |
base64 -D |
xidel --xpath '//Assertion//Signature//KeyInfo//X509Certificate' - |
fold -w 64
echo '-----END CERTIFICATE-----'
}
It seems that
replace()does not allow me to use newlines\nin the replacement string:
That's because regular expressions can't be used in the replacement string. You have to use HTML entities or x:cps():
replace(...,"(.{1,64})","$1 ")
replace(...,"(.{1,64})","$1
")
replace(...,"(.{1,64})","$1"||x:cps(10))
And tokenize will treat the whole match as separator
https://www.w3.org/TR/xpath-functions-31/#func-tokenize:
Returns a sequence of strings constructed by splitting the input wherever a separator is found
You want to split the input based on a separator it doesn't have. So tokenize() is unsuitable. Instead, as an alternative to replace(), you could use Xidel's own x:extract(). But above all, together with parse-xml() and x:binary-to-string() this can be done much simpler and all with Xidel:
$ echo "$SAMLRESPONSE" | xidel -se '
"-----BEGIN CERTIFICATE-----",
binary-to-string(base64Binary($raw)) ! extract(
parse-xml(.)//Assertion//Signature//KeyInfo//X509Certificate,
".{1,64}",0,"*"
),
"-----END CERTIFICATE-----"
'
And because a newline is the default value for --output-separator, there's no need for codepoints-to-string(10) either.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With