{"id":247,"date":"2026-04-27T00:00:00","date_gmt":"2026-04-26T20:30:00","guid":{"rendered":"https:\/\/www.anynet.ir\/blog\/%d8%a7%d8%b9%d8%aa%d8%a8%d8%a7%d8%b1%d9%86%d8%a7%d9%85%d9%87%d9%87%d8%a7%db%8c-gnsi-%da%86%d8%b1%d8%ae%d8%b4-%d8%ae%d9%88%d8%af%da%a9%d8%a7%d8%b1-%d8%a7%d8%b9%d8%aa%d8%a8%d8%a7%d8%b1%d9%86%d8%a7-2\/"},"modified":"2026-10-19T23:59:08","modified_gmt":"2026-10-19T20:29:08","slug":"%d8%a7%d8%b9%d8%aa%d8%a8%d8%a7%d8%b1%d9%86%d8%a7%d9%85%d9%87%d9%87%d8%a7%db%8c-gnsi-%da%86%d8%b1%d8%ae%d8%b4-%d8%ae%d9%88%d8%af%da%a9%d8%a7%d8%b1-%d8%a7%d8%b9%d8%aa%d8%a8%d8%a7%d8%b1%d9%86%d8%a7-2","status":"publish","type":"post","link":"https:\/\/www.anynet.ir\/blog\/en\/%d8%a7%d8%b9%d8%aa%d8%a8%d8%a7%d8%b1%d9%86%d8%a7%d9%85%d9%87%d9%87%d8%a7%db%8c-gnsi-%da%86%d8%b1%d8%ae%d8%b4-%d8%ae%d9%88%d8%af%da%a9%d8%a7%d8%b1-%d8%a7%d8%b9%d8%aa%d8%a8%d8%a7%d8%b1%d9%86%d8%a7-2\/","title":{"rendered":"gNSI Credentialz \u2014 SSH Credential Rotation on Cisco IOS XR"},"content":{"rendered":"<div><\/div>\n<div>\n<div><\/div>\n<\/div>\n<div>\n<div>\n<div>\n<h2 style=\"color: rgb(26, 26, 26); font-weight: 700\">The Problem<\/h2>\n<p><a href=\"https:\/\/xrdocs.io\/programmability\/blogs\/gnsi-certz-tls-certificate-lifecycle-on-cisco-ios-xr\" style=\"color: rgb(0, 102, 204)\">Certz<\/a> handles TLS for the gRPC Management Plane, but most network devices also have SSH access \u2014 console, VTY, interactive troubleshooting. Rotating SSH Credentials (Passwords, Authorized Keys, Host Keys, Certificates) traditionally means manual file edits, config pushes, or platform-specific CLIs. There is no standard API to rotate SSH material across vendors, no built-in Rollback if something goes wrong, and no way to programmatically restrict which Authentication methods the SSH server allows. If a Host Key rotation fails, operators risk locking themselves out.<\/p>\n<h2 style=\"color: rgb(26, 26, 26); font-weight: 700\">What is Credentialz<\/h2>\n<p><a href=\"https:\/\/github.com\/openconfig\/gnsi\/tree\/main\/credentialz\" style=\"color: rgb(0, 102, 204)\">gNSI Credentialz<\/a> solves this with two streaming RPCs that cover <strong style=\"color: rgb(26, 26, 26)\">account-level<\/strong> and <strong style=\"color: rgb(26, 26, 26)\">host-level<\/strong> SSH Credentials:<\/p>\n<ul>\n<li><strong style=\"color: rgb(26, 26, 26)\">RotateAccountCredentials<\/strong> \u2014 rotate Passwords, Authorized Keys, and Authorized Principals for user accounts<\/li>\n<li><strong style=\"color: rgb(26, 26, 26)\">RotateHostParameters<\/strong> \u2014 rotate Host Keys, Host Certificates, CA Public Keys, allowed Authentication methods, and AuthorizedPrincipalsCommand settings<\/li>\n<\/ul>\n<p>Plus two unary RPCs for preflight:<\/p>\n<ul>\n<li><strong style=\"color: rgb(26, 26, 26)\">CanGenerateKey<\/strong> \u2014 check if the device can generate a Key Pair<\/li>\n<li><strong style=\"color: rgb(26, 26, 26)\">GetPublicKeys<\/strong> \u2014 retrieve the current Public Keys from the host<\/li>\n<\/ul>\n<p>Both streaming RPCs follow the same stage-validate-finalize pattern as Certz Rotate: send the new material, validate it works (e.g., try SSH with the new Key), then finalize. Cancel the stream before finalize to Rollback.<\/p>\n<p>Cisco IOS XR supports Credentialz from <strong style=\"color: rgb(26, 26, 26)\">24.2.1<\/strong> onward. In this article, every command was validated against NCS-540 25.3.1. The OpenConfig contract is <a href=\"https:\/\/github.com\/openconfig\/gnsi\/blob\/main\/credentialz\/credentialz.proto\" style=\"color: rgb(0, 102, 204)\"><code>credentialz.proto<\/code><\/a>.<\/p>\n<p>Within AAA, Credentialz is the <strong style=\"color: rgb(26, 26, 26)\">Authentication<\/strong> plane for SSH \u2014 it manages the host keys, authorized keys, and account passwords that gate SSH login. (Its sibling <a href=\"https:\/\/xrdocs.io\/programmability\/blogs\/gnsi-certz-tls-certificate-lifecycle-on-cisco-ios-xr\" style=\"color: rgb(0, 102, 204)\">Certz<\/a> plays the equivalent role for the gRPC plane.) Only <strong style=\"color: rgb(26, 26, 26)\">one<\/strong> Credentialz RPC can be in progress at a time.<\/p>\n<hr>\n<h2 style=\"color: rgb(26, 26, 26); font-weight: 700\">The four RPCs<\/h2>\n<table class=\"english-numbers\">\n<thead>\n<tr>\n<th>RPC<\/th>\n<th>Type<\/th>\n<th>What it does<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">RotateAccountCredentials<\/strong><\/td>\n<td>Bidirectional stream<\/td>\n<td>Rotate Passwords, Authorized Keys, Authorized Principals for user accounts. Finalize to commit; cancel to Rollback.<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">RotateHostParameters<\/strong><\/td>\n<td>Bidirectional stream<\/td>\n<td>Rotate Host Keys (external or generated), Host Certificates, CA Public Keys, allowed Authentication types, AuthorizedPrincipalsCommand. Finalize to commit; cancel to Rollback.<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">CanGenerateKey<\/strong><\/td>\n<td>Unary<\/td>\n<td>Check whether the device can generate a Key Pair for a given algorithm (RSA 2048\/4096, ECDSA P-256\/P-521, Ed25519).<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">GetPublicKeys<\/strong><\/td>\n<td>Unary<\/td>\n<td>Retrieve the current Public Keys configured on the host.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 style=\"color: rgb(26, 26, 26); font-weight: 700\">Supported Key algorithms (Cisco documentation)<\/h3>\n<table class=\"english-numbers\">\n<thead>\n<tr>\n<th>Algorithm<\/th>\n<th>Key types<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">RSA<\/strong><\/td>\n<td>2048, 4096<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">ECDSA<\/strong><\/td>\n<td>P-256, P-521<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">Ed25519<\/strong><\/td>\n<td>\u2014<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<blockquote>\n<p>The OpenConfig <code>KeyGen<\/code> enum also defines <code>KEY_GEN_SSH_KEY_TYPE_RSA_3072<\/code> and <code>KEY_GEN_SSH_KEY_TYPE_ECDSA_P_384<\/code>. On IOS XR 25.3.1 (NCS-540) the device <strong style=\"color: rgb(26, 26, 26)\">rejects<\/strong> both with <code>Code: InvalidArgument<\/code>, message <code>Can Generate Key:Invaid KeyGen input<\/code> (typo on the device). Pinning to the five algorithms above is required.<\/p>\n<\/blockquote>\n<hr>\n<h2 style=\"color: rgb(26, 26, 26); font-weight: 700\">IOS XR Support<\/h2>\n<table class=\"english-numbers\">\n<thead>\n<tr>\n<th>Release<\/th>\n<th>What changed<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">24.2.1<\/strong><\/td>\n<td>Credentialz introduced \u2014 RotateAccountCredentials, RotateHostParameters, CanGenerateKey, GetPublicKeys. Account and Host SSH Credential Rotation.<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">24.2.11<\/strong><\/td>\n<td>Enhanced real-time Rotation (without reboot), account- and host-specific updates.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong style=\"color: rgb(26, 26, 26)\">Prerequisites:<\/strong><\/p>\n<ul>\n<li>Configure a user account on the router<\/li>\n<li>Configure SSH version 2<\/li>\n<\/ul>\n<hr>\n<h2 style=\"color: rgb(26, 26, 26); font-weight: 700\">IOS XR CLI Reference<\/h2>\n<table class=\"english-numbers\">\n<thead>\n<tr>\n<th>Configuration command<\/th>\n<th>What it does<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>ssh server v2<\/code><\/td>\n<td>Enable SSH version 2 (required for Credentialz)<\/td>\n<\/tr>\n<tr>\n<td><code>username &lt;name&gt; group &lt;group&gt;<\/code><\/td>\n<td>Create a user account for SSH access<\/td>\n<\/tr>\n<tr>\n<td><code>crypto key generate ed25519<\/code><\/td>\n<td>Generate Ed25519 Host Keys (alternative to Credentialz <code>GenerateKeysRequest<\/code>)<\/td>\n<\/tr>\n<tr>\n<td><code>crypto key generate rsa &lt;bits&gt;<\/code><\/td>\n<td>Generate RSA Host Keys<\/td>\n<\/tr>\n<tr>\n<td><code>crypto key generate ecdsa nistp256<\/code><\/td>\n<td>Generate ECDSA P-256 Host Keys<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<table class=\"english-numbers\">\n<thead>\n<tr>\n<th>Show \/ exec command<\/th>\n<th>What it shows<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>show ssh<\/code><\/td>\n<td>Active SSH sessions<\/td>\n<\/tr>\n<tr>\n<td><code>show ssh server<\/code><\/td>\n<td>SSH server configuration and status<\/td>\n<\/tr>\n<tr>\n<td><code>show crypto key mypubkey ed25519<\/code><\/td>\n<td>Ed25519 Host Public Key<\/td>\n<\/tr>\n<tr>\n<td><code>show crypto key mypubkey rsa<\/code><\/td>\n<td>RSA Host Public Key<\/td>\n<\/tr>\n<tr>\n<td><code>show crypto key mypubkey ecdsa<\/code><\/td>\n<td>ECDSA Host Public Key<\/td>\n<\/tr>\n<tr>\n<td><code>show gnsi state<\/code><\/td>\n<td>gNSI subsystem status (Credentialz, RDSFS state)<\/td>\n<\/tr>\n<tr>\n<td><code>show tech-support gnsi<\/code><\/td>\n<td>Full gNSI diagnostic bundle (all services)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<hr>\n<h2 style=\"color: rgb(26, 26, 26); font-weight: 700\">RotateAccountCredentials \u2014 what it can rotate<\/h2>\n<table class=\"english-numbers\">\n<thead>\n<tr>\n<th>Message<\/th>\n<th>What it does<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">PasswordRequest<\/strong><\/td>\n<td>Change the Password for a user account (plaintext or hashed). Version and <code>created_on<\/code> timestamp included.<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">AuthorizedKeysRequest<\/strong><\/td>\n<td><strong style=\"color: rgb(26, 26, 26)\">Replace<\/strong> the Authorized Keys list for password-less SSH. Supported key types: RSA 2048\/4096, ECDSA P-256\/P-521, Ed25519.<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">AuthorizedUsersRequest<\/strong><\/td>\n<td>Set Authorized Principals for Certificate-based SSH Authentication. Uses <code>AuthorizedPrincipalsFile<\/code> semantics (pairs of account names and principal names).<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">FinalizeRequest<\/strong><\/td>\n<td>Commit the rotation. Old Credentials are invalidated.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The stream follows: send one or more credential updates \u2192 validate externally (try SSH with the new material) \u2192 finalize or cancel.<\/p>\n<h2 style=\"color: rgb(26, 26, 26); font-weight: 700\">RotateHostParameters \u2014 what it can rotate<\/h2>\n<table class=\"english-numbers\">\n<thead>\n<tr>\n<th>Message<\/th>\n<th>What it does<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">CaPublicKeyRequest<\/strong><\/td>\n<td>Replace the CA Public Key used to verify client SSH Certificates.<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">ServerKeysRequest<\/strong> (external)<\/td>\n<td>Upload externally generated Host Keys and\/or Host Certificates. Private Key and Certificate are base64-encoded.<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">GenerateKeysRequest<\/strong><\/td>\n<td>Ask the device to generate new Host Keys. Public Key is returned in the response for Certificate signing.<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">AllowedAuthenticationRequest<\/strong><\/td>\n<td>Restrict which SSH Authentication methods are globally allowed (password, pubkey, keyboard-interactive, certificate). Default: all methods.<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">AuthorizedPrincipalCheckRequest<\/strong><\/td>\n<td>Set <code>AuthorizedPrincipalsCommand<\/code> \u2014 <code>TOOL_HIBA_DEFAULT<\/code> selects HIBA binary for dynamic principal Authorization.<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">FinalizeRequest<\/strong><\/td>\n<td>Commit. If validation fails before finalize, cancel the stream to revert to old Host parameters.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<hr>\n<h2 style=\"color: rgb(26, 26, 26); font-weight: 700\">Hands-on: End-to-End Credentialz<\/h2>\n<h3 style=\"color: rgb(26, 26, 26); font-weight: 700\">Prerequisites<\/h3>\n<ul>\n<li>IOS XR router with gRPC and SSH v2 enabled (24.2.1 or later for Credentialz)<\/li>\n<li><a href=\"https:\/\/github.com\/fullstorydev\/grpcurl\" style=\"color: rgb(0, 102, 204)\">grpcurl<\/a> and <code>ssh-keygen<\/code> installed<\/li>\n<li>OpenConfig gNSI protos cloned: <code>git clone https:\/\/github.com\/openconfig\/gnsi.git<\/code><\/li>\n<li>A user account configured on the router<\/li>\n<\/ul>\n<h3 style=\"color: rgb(26, 26, 26); font-weight: 700\">Environment (workstation)<\/h3>\n<pre><code><span><span># Stage the proto so grpcurl's -import-path \/ -proto resolves cleanly<\/span>\n<\/span><span><span>git<\/span> clone https:\/\/github.com\/openconfig\/gnsi.git \/tmp\/gnsi\n<\/span><span><span>mkdir<\/span> <span>-p<\/span>.\/credz-import\/credentialz\n<\/span><span><span>cp<\/span> \/tmp\/gnsi\/credentialz\/credentialz.proto.\/credz-import\/credentialz\/\n<\/span><span><span>export<\/span> <span>GNSI_IMPORT<\/span><span>=<\/span><span>\"<span>${GNSI_IMPORT<span>:-<\/span>$PWD<span>\/<\/span>credz-import}<\/span>\"<\/span>\n<\/span><span><span>export<\/span> <span>XR<\/span><span>=<\/span><span>\"<span>${XR<span>:?<\/span>set to &lt;router-ip&gt;<span>:<\/span>57400 \u2014 required}<\/span>\"<\/span>\n<\/span><span><span>export<\/span> <span>XR_USER<\/span><span>=<\/span><span>\"<span>${XR_USER<span>:-<\/span>cisco}<\/span>\"<\/span>\n<\/span><span><span>export<\/span> <span>XR_PASS<\/span><span>=<\/span><span>\"<span>${XR_PASS<span>:?<\/span>set XR_PASS \u2014 required (Credentialz needs valid AAA on every call)}<\/span>\"<\/span>\n<\/span><span><span>GRPC_MD<\/span><span>=<\/span><span>(<\/span>-H <span>\"username:<span>${XR_USER}<\/span>\"<\/span> <span>-H<\/span> <span>\"password:<span>${XR_PASS}<\/span>\"<\/span><span>)<\/span>\n<\/span><span><span>PROTO_CREDZ<\/span><span>=<\/span><span>\"credentialz\/credentialz.proto\"<\/span>\n<\/span><\/code><\/pre>\n<blockquote>\n<p><code>credentialz.proto<\/code> has no <code>import<\/code> statements, so the <code>-import-path<\/code> only needs the one <code>credentialz\/credentialz.proto<\/code> file under it. There is no need to clone <code>openconfig\/gnmi<\/code>.<\/p>\n<\/blockquote>\n<blockquote>\n<p><strong style=\"color: rgb(26, 26, 26)\">Lab only:<\/strong> All examples below use <code>-plaintext<\/code> (no TLS). In production, replace <code>-plaintext<\/code> with <code>-cacert<\/code>, <code>-cert<\/code>, and <code>-key<\/code> flags. See the <a href=\"https:\/\/xrdocs.io\/programmability\/blogs\/gnsi-certz-tls-certificate-lifecycle-on-cisco-ios-xr\" style=\"color: rgb(0, 102, 204)\">Certz article<\/a> for TLS setup.<\/p>\n<\/blockquote>\n<h3 style=\"color: rgb(26, 26, 26); font-weight: 700\">Router setup (IOS XR CLI)<\/h3>\n<p>Ensure SSH v2 and a user account exist:<\/p>\n<pre><code><span>configure\n<\/span><span> ssh server v2\n<\/span><span> username lab\n<\/span><span> group root-lr\n<\/span><span> group cisco-support\n<\/span><span> commit\n<\/span><span>end\n<\/span><\/code><\/pre>\n<h3 style=\"color: rgb(26, 26, 26); font-weight: 700\">List Services (Reflection)<\/h3>\n<pre><code><span>grpcurl <span>-plaintext<\/span> <span>\\<\/span>\n<\/span><span> -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${GRPC_MD<span>[<\/span>@<span>]<\/span>}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${XR}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> list gnsi.credentialz.v1.Credentialz\n<\/span><\/code><\/pre>\n<p>Expected output:<\/p>\n<pre><code><span>gnsi.credentialz.v1.Credentialz.CanGenerateKey\n<\/span><span>gnsi.credentialz.v1.Credentialz.GetPublicKeys\n<\/span><span>gnsi.credentialz.v1.Credentialz.RotateAccountCredentials\n<\/span><span>gnsi.credentialz.v1.Credentialz.RotateHostParameters\n<\/span><\/code><\/pre>\n<h3 style=\"color: rgb(26, 26, 26); font-weight: 700\">CanGenerateKey (preflight)<\/h3>\n<p>Check if the device can generate Key Pairs for each algorithm:<\/p>\n<pre><code><span>grpcurl <span>-plaintext<\/span> <span>\\<\/span>\n<\/span><span> -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${GRPC_MD<span>[<\/span>@<span>]<\/span>}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-d<\/span> <span>'{\"keyParams\": \"KEY_GEN_SSH_KEY_TYPE_EDDSA_ED25519\"}'<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${XR}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> gnsi.credentialz.v1.Credentialz.CanGenerateKey\n<\/span><\/code><\/pre>\n<p>Router response:<\/p>\n<pre><code><span><span>{<\/span>\n<\/span><span> <span>\"canGenerate\"<\/span><span>:<\/span> <span>true<\/span>\n<\/span><span><span>}<\/span>\n<\/span><\/code><\/pre>\n<p>Repeat for other algorithms \u2014 all return <code>canGenerate: true<\/code> on Cisco 8000:<\/p>\n<pre><code><span><span># RSA 4096<\/span>\n<\/span><span>grpcurl <span>-plaintext<\/span> <span>\\<\/span>\n<\/span><span> -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${GRPC_MD<span>[<\/span>@<span>]<\/span>}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-d<\/span> <span>'{\"keyParams\": \"KEY_GEN_SSH_KEY_TYPE_RSA_4096\"}'<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${XR}<\/span>\"<\/span> gnsi.credentialz.v1.Credentialz.CanGenerateKey\n<\/span><span><span># RSA 2048<\/span>\n<\/span><span>grpcurl <span>-plaintext<\/span> <span>\\<\/span>\n<\/span><span> -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${GRPC_MD<span>[<\/span>@<span>]<\/span>}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-d<\/span> <span>'{\"keyParams\": \"KEY_GEN_SSH_KEY_TYPE_RSA_2048\"}'<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${XR}<\/span>\"<\/span> gnsi.credentialz.v1.Credentialz.CanGenerateKey\n<\/span><span><span># ECDSA P-256<\/span>\n<\/span><span>grpcurl <span>-plaintext<\/span> <span>\\<\/span>\n<\/span><span> -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${GRPC_MD<span>[<\/span>@<span>]<\/span>}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-d<\/span> <span>'{\"keyParams\": \"KEY_GEN_SSH_KEY_TYPE_ECDSA_P_256\"}'<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${XR}<\/span>\"<\/span> gnsi.credentialz.v1.Credentialz.CanGenerateKey\n<\/span><span><span># ECDSA P-521<\/span>\n<\/span><span>grpcurl <span>-plaintext<\/span> <span>\\<\/span>\n<\/span><span> -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${GRPC_MD<span>[<\/span>@<span>]<\/span>}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-d<\/span> <span>'{\"keyParams\": \"KEY_GEN_SSH_KEY_TYPE_ECDSA_P_521\"}'<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${XR}<\/span>\"<\/span> gnsi.credentialz.v1.Credentialz.CanGenerateKey\n<\/span><\/code><\/pre>\n<h3 style=\"color: rgb(26, 26, 26); font-weight: 700\">GetPublicKeys<\/h3>\n<p>Retrieve the current Host Public Keys:<\/p>\n<pre><code><span>grpcurl <span>-plaintext<\/span> <span>\\<\/span>\n<\/span><span> -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${GRPC_MD<span>[<\/span>@<span>]<\/span>}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-d<\/span> <span>'{}'<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${XR}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> gnsi.credentialz.v1.Credentialz.GetPublicKeys\n<\/span><\/code><\/pre>\n<p>Response (trimmed \u2014 one entry per key type, <strong style=\"color: rgb(26, 26, 26)\">as observed on a default NCS-540 25.3.1<\/strong>):<\/p>\n<pre><code><span><span>{<\/span>\n<\/span><span> <span>\"publicKeys\"<\/span><span>:<\/span> <span>[<\/span>\n<\/span><span> <span>{<\/span> <span>\"publicKey\"<\/span><span>:<\/span> <span>\"c3NoLXJzYSBBQUFBQjNOemFD...\"<\/span><span>,<\/span> <span>\"keyType\"<\/span><span>:<\/span> <span>\"KEY_TYPE_RSA_2048\"<\/span> <span>}<\/span><span>,<\/span>\n<\/span><span> <span>{<\/span> <span>\"publicKey\"<\/span><span>:<\/span> <span>\"c3NoLWRzcyBBQUFBQjNOemFD...\"<\/span> <span>}<\/span><span>,<\/span>\n<\/span><span> <span>{<\/span> <span>\"publicKey\"<\/span><span>:<\/span> <span>\"ZWNkc2Etc2hhMi1uaXN0cDUy...\"<\/span><span>,<\/span> <span>\"keyType\"<\/span><span>:<\/span> <span>\"KEY_TYPE_ECDSA_P_521\"<\/span> <span>}<\/span><span>,<\/span>\n<\/span><span> <span>{<\/span> <span>\"publicKey\"<\/span><span>:<\/span> <span>\"ZWNkc2Etc2hhMi1uaXN0cDM4...\"<\/span> <span>}<\/span><span>,<\/span>\n<\/span><span> <span>{<\/span> <span>\"publicKey\"<\/span><span>:<\/span> <span>\"ZWNkc2Etc2hhMi1uaXN0cDI1...\"<\/span><span>,<\/span> <span>\"keyType\"<\/span><span>:<\/span> <span>\"KEY_TYPE_ECDSA_P_256\"<\/span> <span>}<\/span><span>,<\/span>\n<\/span><span> <span>{<\/span> <span>\"publicKey\"<\/span><span>:<\/span> <span>\"c3NoLWVkMjU1MTkgQUFBQUMz...\"<\/span><span>,<\/span> <span>\"keyType\"<\/span><span>:<\/span> <span>\"KEY_TYPE_ED25519\"<\/span> <span>}<\/span>\n<\/span><span> <span>]<\/span>\n<\/span><span><span>}<\/span>\n<\/span><\/code><\/pre>\n<p>Each <code>publicKey<\/code> is base64-encoded OpenSSH format. Decode with <code>echo \"&lt;base64&gt;\" | base64 -d<\/code> to see the raw SSH Public Key.<\/p>\n<blockquote>\n<p><strong style=\"color: rgb(26, 26, 26)\">On 25.3.1:<\/strong> the default response includes a <strong style=\"color: rgb(26, 26, 26)\">DSA<\/strong> (<code>ssh-dss<\/code>) and an <strong style=\"color: rgb(26, 26, 26)\">ECDSA P-384<\/strong> host key alongside the usual four. Both come back with <code>keyType<\/code> <strong style=\"color: rgb(26, 26, 26)\">omitted<\/strong> \u2014 DSA isn&#8217;t modeled by OpenConfig Credentialz, and P-384 is defined in the proto but not populated by the device. If your security baseline disallows <code>ssh-dss<\/code>, remove it via a multi-algo <code>RotateHostParameters: generateKeys<\/code> call (see <a href=\"https:\/\/xrdocs.io\/programmability\/blogs\/gnsi-credentialz-ssh-credential-rotation-on-cisco-ios-xr#generatekeys-is-destructive\" style=\"color: rgb(0, 102, 204)\">GenerateKeys is destructive<\/a>).<\/p>\n<\/blockquote>\n<h3 style=\"color: rgb(26, 26, 26); font-weight: 700\">RotateAccountCredentials: rotate Authorized Keys<\/h3>\n<p>Generate a test SSH Key Pair and push the Public Key as an Authorized Key for a <strong style=\"color: rgb(26, 26, 26)\">non-admin<\/strong> user account (using the same <code>cisco<\/code> user you authenticate gRPC with risks locking yourself out of every flow that follows). The <code>authorizedKey<\/code> field must be base64-encoded:<\/p>\n<pre><code><span>ssh-keygen <span>-t<\/span> ed25519 <span>-f<\/span> \/tmp\/credz-test-key <span>-N<\/span> <span>\"\"<\/span> <span>-C<\/span> <span>\"credz-test@lab\"<\/span>\n<\/span><span><span>PUBKEY_B64<\/span><span>=<\/span><span><span>$(<\/span>base64 <span>&lt;<\/span> \/tmp\/credz-test-key.pub <span>|<\/span> <span>tr<\/span> <span>-d<\/span> <span>'\\n'<\/span><span>)<\/span><\/span>\n<\/span><span><span>CREATED_ON<\/span><span>=<\/span><span><span>$(<\/span><span>date<\/span> +%s<span>)<\/span><\/span>\n<\/span><span>grpcurl <span>-plaintext<\/span> <span>\\<\/span>\n<\/span><span> -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${GRPC_MD<span>[<\/span>@<span>]<\/span>}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-d<\/span> <span>'@'<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${XR}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> gnsi.credentialz.v1.Credentialz.RotateAccountCredentials <span>&lt;&lt;<\/span><span>EOF\n<\/span><\/span><span><span>{\"credential\":{\"credentials\":[{\"account\":\"mike\",\"authorizedKeys\":[{\"authorizedKey\":\"<span>${PUBKEY_B64}<\/span>\",\"keyType\":\"KEY_TYPE_ED25519\"}],\"version\":\"keys-mike-v1\",\"createdOn\":\"<span>${CREATED_ON}<\/span>\"}]}}\n<\/span><\/span><span><span>{\"finalize\":{}}\n<\/span><\/span><span><span>EOF<\/span>\n<\/span><\/code><\/pre>\n<p>Router response:<\/p>\n<pre><code><span><span>{<\/span>\n<\/span><span> <span>\"credential\"<\/span><span>:<\/span> <span>{<\/span><span>}<\/span>\n<\/span><span><span>}<\/span>\n<\/span><span><span>{<\/span><span>}<\/span>\n<\/span><\/code><\/pre>\n<p>The first response acknowledges the Credential update; the second acknowledges the finalize. The old Authorized Keys for this account are replaced.<\/p>\n<blockquote>\n<p><strong style=\"color: rgb(26, 26, 26)\">Validation step:<\/strong> In a production workflow, open a second terminal between the credential update and finalize to test SSH with the new Key. Cancel the stream (Ctrl+C) to Rollback if validation fails.<\/p>\n<\/blockquote>\n<h3 style=\"color: rgb(26, 26, 26); font-weight: 700\">Verify Account Credentials on IOS XR<\/h3>\n<p>After RotateAccountCredentials, verify the Authorized Keys were applied:<\/p>\n<pre><code><span>show ssh server\n<\/span><span>show gnsi state\n<\/span><\/code><\/pre>\n<p>Or verify via gRPC with <code>GetPublicKeys<\/code> (see <a href=\"https:\/\/xrdocs.io\/programmability\/blogs\/gnsi-credentialz-ssh-credential-rotation-on-cisco-ios-xr#verify-on-ios-xr\" style=\"color: rgb(0, 102, 204)\">Verify on IOS XR<\/a> for the full verification block).<\/p>\n<h3 style=\"color: rgb(26, 26, 26); font-weight: 700\">RotateAccountCredentials: rotate Password<\/h3>\n<p>Change the Password for a user account. <strong style=\"color: rgb(26, 26, 26)\">Use a non-admin user<\/strong> (e.g. <code>mike<\/code>, not the gRPC rotate-authority <code>cisco<\/code>) so a bad password does not lock you out of the management plane:<\/p>\n<pre><code><span><span>CREATED_ON<\/span><span>=<\/span><span><span>$(<\/span><span>date<\/span> +%s<span>)<\/span><\/span>\n<\/span><span>grpcurl <span>-plaintext<\/span> <span>\\<\/span>\n<\/span><span> -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${GRPC_MD<span>[<\/span>@<span>]<\/span>}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-d<\/span> <span>'@'<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${XR}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> gnsi.credentialz.v1.Credentialz.RotateAccountCredentials <span>&lt;&lt;<\/span><span>EOF\n<\/span><\/span><span><span>{\"password\":{\"accounts\":[{\"account\":\"mike\",\"password\":{\"plaintext\":\"NewSecurePass123\"},\"version\":\"pwd-mike-v1\",\"createdOn\":\"<span>${CREATED_ON}<\/span>\"}]}}\n<\/span><\/span><span><span>{\"finalize\":{}}\n<\/span><\/span><span><span>EOF<\/span>\n<\/span><\/code><\/pre>\n<p>Router response on IOS XR 25.3.1 (NCS-540):<\/p>\n<pre><code><span><span>{<\/span> <span>\"password\"<\/span><span>:<\/span> <span>{<\/span><span>}<\/span> <span>}<\/span>\n<\/span><span><span>{<\/span><span>}<\/span>\n<\/span><\/code><\/pre>\n<p>The new password is immediately usable for gRPC AAA:<\/p>\n<pre><code><span>grpcurl <span>-plaintext<\/span> -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-H<\/span> <span>\"username:mike\"<\/span> <span>-H<\/span> <span>\"password:NewSecurePass123\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-d<\/span> <span>'{}'<\/span> <span>\"<span>${XR}<\/span>\"<\/span> gnsi.credentialz.v1.Credentialz.GetPublicKeys\n<\/span><span><span># returns publicKeys[\u2026]<\/span>\n<\/span><span>grpcurl <span>-plaintext<\/span> -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-H<\/span> <span>\"username:mike\"<\/span> <span>-H<\/span> <span>\"password:OLD-PASSWORD\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-d<\/span> <span>'{}'<\/span> <span>\"<span>${XR}<\/span>\"<\/span> gnsi.credentialz.v1.Credentialz.GetPublicKeys\n<\/span><span><span># ERROR: Code: Unauthenticated, Message: authentication failed<\/span>\n<\/span><\/code><\/pre>\n<h3 style=\"color: rgb(26, 26, 26); font-weight: 700\">RotateHostParameters: generate new Host Keys<\/h3>\n<blockquote>\n<p><strong style=\"color: rgb(26, 26, 26)\">GenerateKeys is destructive.<\/strong> A single-algorithm <code>generateKeys<\/code> call wipes <strong style=\"color: rgb(26, 26, 26)\">every other<\/strong> algorithm&#8217;s host key, including ones the proto cannot regenerate (DSA, ECDSA-P-384). Always pass <strong style=\"color: rgb(26, 26, 26)\">every algorithm you want to keep<\/strong> in one <code>keyParams<\/code> list.<\/p>\n<\/blockquote>\n<p>Recommended baseline call \u2014 regenerate the four algorithms IOS XR 25.3.1 actually supports through Credentialz, in one transaction:<\/p>\n<pre><code><span>grpcurl <span>-plaintext<\/span> <span>\\<\/span>\n<\/span><span> -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${GRPC_MD<span>[<\/span>@<span>]<\/span>}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-d<\/span> <span>'@'<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${XR}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> gnsi.credentialz.v1.Credentialz.RotateHostParameters <span>&lt;&lt;<\/span><span>'EOF'\n<\/span><\/span><span><span>{\"generateKeys\":{\"keyParams\":[\n<\/span><\/span><span><span> \"KEY_GEN_SSH_KEY_TYPE_RSA_2048\",\n<\/span><\/span><span><span> \"KEY_GEN_SSH_KEY_TYPE_ECDSA_P_256\",\n<\/span><\/span><span><span> \"KEY_GEN_SSH_KEY_TYPE_ECDSA_P_521\",\n<\/span><\/span><span><span> \"KEY_GEN_SSH_KEY_TYPE_EDDSA_ED25519\"]}}\n<\/span><\/span><span><span>{\"finalize\":{}}\n<\/span><\/span><span><span>EOF<\/span>\n<\/span><\/code><\/pre>\n<p>Router response:<\/p>\n<pre><code><span><span>{<\/span>\n<\/span><span> <span>\"generateKeys\"<\/span><span>:<\/span> <span>{<\/span>\n<\/span><span> <span>\"publicKeys\"<\/span><span>:<\/span> <span>[<\/span>\n<\/span><span> <span>{<\/span> <span>\"publicKey\"<\/span><span>:<\/span> <span>\"c3NoLXJzYS...\"<\/span><span>,<\/span> <span>\"keyType\"<\/span><span>:<\/span> <span>\"KEY_TYPE_RSA_2048\"<\/span> <span>}<\/span><span>,<\/span>\n<\/span><span> <span>{<\/span> <span>\"publicKey\"<\/span><span>:<\/span> <span>\"ZWNkc2Etc2hhMi1uaXN0cDI1...\"<\/span><span>,<\/span><span>\"keyType\"<\/span><span>:<\/span> <span>\"KEY_TYPE_ECDSA_P_256\"<\/span> <span>}<\/span><span>,<\/span>\n<\/span><span> <span>{<\/span> <span>\"publicKey\"<\/span><span>:<\/span> <span>\"ZWNkc2Etc2hhMi1uaXN0cDUy...\"<\/span><span>,<\/span><span>\"keyType\"<\/span><span>:<\/span> <span>\"KEY_TYPE_ECDSA_P_521\"<\/span> <span>}<\/span><span>,<\/span>\n<\/span><span> <span>{<\/span> <span>\"publicKey\"<\/span><span>:<\/span> <span>\"c3NoLWVkMjU1MTkg...\"<\/span><span>,<\/span> <span>\"keyType\"<\/span><span>:<\/span> <span>\"KEY_TYPE_ED25519\"<\/span> <span>}<\/span>\n<\/span><span> <span>]<\/span>\n<\/span><span> <span>}<\/span>\n<\/span><span><span>}<\/span>\n<\/span><span><span>{<\/span><span>}<\/span>\n<\/span><\/code><\/pre>\n<p>Each entry in <code>publicKeys<\/code> contains the freshly generated Public Key (base64-encoded). In a production workflow, extract these Keys, sign them with your CA, then send a <code>ServerKeysRequest<\/code> with the Host Certificate before finalizing.<\/p>\n<blockquote>\n<p><strong style=\"color: rgb(26, 26, 26)\">Single-algorithm calls only for greenfield.<\/strong> A <code>generateKeys<\/code> call with one algorithm wipes every other host key \u2014 and DSA \/ ECDSA-P-384 are <strong style=\"color: rgb(26, 26, 26)\">not recoverable<\/strong> through Credentialz because the device&#8217;s <code>CanGenerateKey<\/code> rejects both. Use the multi-algo form above unless you really intend to flatten the host-key store.<\/p>\n<\/blockquote>\n<h3 style=\"color: rgb(26, 26, 26); font-weight: 700\">RotateHostParameters: upload external Host Key<\/h3>\n<blockquote>\n<p><strong style=\"color: rgb(26, 26, 26)\">Destructive partial commit on 25.3.1.<\/strong> The <code>serverKeys<\/code> finalize returns <code>Code: Unknown \/ \"Write Metadata to file failed \u2026 No such file or directory\"<\/code>, <strong style=\"color: rgb(26, 26, 26)\">but the host-key store is still mutated<\/strong> \u2014 every existing host key is wiped and the uploaded private key becomes the only active one. Have a recovery <code>generateKeys<\/code> call ready before you run this (see <a href=\"https:\/\/xrdocs.io\/programmability\/blogs\/gnsi-credentialz-ssh-credential-rotation-on-cisco-ios-xr#generatekeys-is-destructive\" style=\"color: rgb(0, 102, 204)\">GenerateKeys is destructive<\/a>).<\/p>\n<\/blockquote>\n<p>Upload an externally generated SSH Host Key. The <code>privateKey<\/code> field is base64-encoded:<\/p>\n<pre><code><span>ssh-keygen <span>-t<\/span> ed25519 <span>-f<\/span> \/tmp\/credz-ext-hostkey <span>-N<\/span> <span>\"\"<\/span> <span>-C<\/span> <span>\"ext-host@lab\"<\/span>\n<\/span><span><span>PRIVKEY_B64<\/span><span>=<\/span><span><span>$(<\/span>base64 <span>&lt;<\/span> \/tmp\/credz-ext-hostkey <span>|<\/span> <span>tr<\/span> <span>-d<\/span> <span>'\\n'<\/span><span>)<\/span><\/span>\n<\/span><span><span>CREATED_ON<\/span><span>=<\/span><span><span>$(<\/span><span>date<\/span> +%s<span>)<\/span><\/span>\n<\/span><span>grpcurl <span>-plaintext<\/span> <span>\\<\/span>\n<\/span><span> -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${GRPC_MD<span>[<\/span>@<span>]<\/span>}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-d<\/span> <span>'@'<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${XR}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> gnsi.credentialz.v1.Credentialz.RotateHostParameters <span>&lt;&lt;<\/span><span>EOF\n<\/span><\/span><span><span>{\"serverKeys\":{\"authArtifacts\":[{\"privateKey\":\"<span>${PRIVKEY_B64}<\/span>\"}],\"version\":\"hostkey-ext-v1\",\"createdOn\":\"<span>${CREATED_ON}<\/span>\"}}\n<\/span><\/span><span><span>{\"finalize\":{}}\n<\/span><\/span><span><span>EOF<\/span>\n<\/span><\/code><\/pre>\n<p>Documented (spec) response, on a healthy implementation:<\/p>\n<pre><code><span><span>{<\/span> <span>\"serverKeys\"<\/span><span>:<\/span> <span>{<\/span><span>}<\/span> <span>}<\/span>\n<\/span><span><span>{<\/span><span>}<\/span>\n<\/span><\/code><\/pre>\n<p>Observed response on NCS-540 25.3.1:<\/p>\n<pre><code><span><span>{<\/span> <span>\"serverKeys\"<\/span><span>:<\/span> <span>{<\/span><span>}<\/span> <span>}<\/span>\n<\/span><span>ERROR<span>:<\/span>\n<\/span><span> Code<span>:<\/span> Unknown\n<\/span><span> Message<span>:<\/span> Write Metadata to file failed<span>:<\/span> 'SSH' detected the 'fatal' condition 'Failed to open file'<span>:<\/span> No such file or directory\n<\/span><\/code><\/pre>\n<p>After this error the device <strong style=\"color: rgb(26, 26, 26)\">silently retains the uploaded Ed25519<\/strong> as the only host key. Verify with <code>GetPublicKeys<\/code> and decode the returned key:<\/p>\n<pre><code><span>grpcurl <span>..<\/span>. <span>-d<\/span> <span>'{}'<\/span> <span>\"<span>${XR}<\/span>\"<\/span> gnsi.credentialz.v1.Credentialz.GetPublicKeys <span>\\<\/span>\n<\/span><span> <span>|<\/span> jq <span>-r<\/span> <span>'.publicKeys[]|select(.keyType==\"KEY_TYPE_ED25519\").publicKey'<\/span> <span>\\<\/span>\n<\/span><span> <span>|<\/span> base64 <span>-d<\/span> <span>|<\/span> ssh-keygen <span>-lf<\/span> \/dev\/stdin\n<\/span><span><span># fingerprint matches \/tmp\/credz-ext-hostkey.pub<\/span>\n<\/span><span><span># Recover the missing algorithms in one call:<\/span>\n<\/span><span>grpcurl <span>..<\/span>. <span>-d<\/span> @ <span>\"<span>${XR}<\/span>\"<\/span> gnsi.credentialz.v1.Credentialz.RotateHostParameters <span>&lt;&lt;<\/span><span>'EOF'\n<\/span><\/span><span><span>{\"generateKeys\":{\"keyParams\":[\n<\/span><\/span><span><span> \"KEY_GEN_SSH_KEY_TYPE_RSA_2048\",\n<\/span><\/span><span><span> \"KEY_GEN_SSH_KEY_TYPE_ECDSA_P_256\",\n<\/span><\/span><span><span> \"KEY_GEN_SSH_KEY_TYPE_ECDSA_P_521\",\n<\/span><\/span><span><span> \"KEY_GEN_SSH_KEY_TYPE_EDDSA_ED25519\"]}}\n<\/span><\/span><span><span>{\"finalize\":{}}\n<\/span><\/span><span><span>EOF<\/span>\n<\/span><\/code><\/pre>\n<h3 style=\"color: rgb(26, 26, 26); font-weight: 700\">RotateHostParameters: upload CA Public Key<\/h3>\n<p>Upload a CA Public Key for verifying client SSH Certificates:<\/p>\n<pre><code><span>ssh-keygen <span>-t<\/span> ed25519 <span>-f<\/span> \/tmp\/credz-test-ca <span>-N<\/span> <span>\"\"<\/span> <span>-C<\/span> <span>\"test-ca\"<\/span>\n<\/span><span><span>CA_B64<\/span><span>=<\/span><span><span>$(<\/span>base64 <span>&lt;<\/span> \/tmp\/credz-test-ca.pub <span>|<\/span> <span>tr<\/span> <span>-d<\/span> <span>'\\n'<\/span><span>)<\/span><\/span>\n<\/span><span><span>CREATED_ON<\/span><span>=<\/span><span><span>$(<\/span><span>date<\/span> +%s<span>)<\/span><\/span>\n<\/span><span>grpcurl <span>-plaintext<\/span> <span>\\<\/span>\n<\/span><span> -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${GRPC_MD<span>[<\/span>@<span>]<\/span>}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-d<\/span> <span>'@'<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${XR}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> gnsi.credentialz.v1.Credentialz.RotateHostParameters <span>&lt;&lt;<\/span><span>EOF\n<\/span><\/span><span><span>{\"sshCaPublicKey\":{\"sshCaPublicKeys\":[{\"publicKey\":\"<span>${CA_B64}<\/span>\",\"keyType\":\"KEY_TYPE_ED25519\"}],\"version\":\"ca-v1\",\"createdOn\":\"<span>${CREATED_ON}<\/span>\"}}\n<\/span><\/span><span><span>{\"finalize\":{}}\n<\/span><\/span><span><span>EOF<\/span>\n<\/span><\/code><\/pre>\n<p>Router response:<\/p>\n<pre><code><span><span>{<\/span>\n<\/span><span> <span>\"sshCaPublicKey\"<\/span><span>:<\/span> <span>{<\/span><span>}<\/span>\n<\/span><span><span>}<\/span>\n<\/span><span><span>{<\/span><span>}<\/span>\n<\/span><\/code><\/pre>\n<h3 style=\"color: rgb(26, 26, 26); font-weight: 700\">RotateHostParameters: restrict Authentication methods<\/h3>\n<p>Restrict which SSH Authentication methods are globally allowed. Available types: <code>AUTHENTICATION_TYPE_PASSWORD<\/code>, <code>AUTHENTICATION_TYPE_PUBKEY<\/code>, <code>AUTHENTICATION_TYPE_KBDINTERACTIVE<\/code>.<\/p>\n<p>Allow all three methods (safe default):<\/p>\n<pre><code><span>grpcurl <span>-plaintext<\/span> <span>\\<\/span>\n<\/span><span> -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${GRPC_MD<span>[<\/span>@<span>]<\/span>}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-d<\/span> <span>'@'<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${XR}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> gnsi.credentialz.v1.Credentialz.RotateHostParameters <span>&lt;&lt;<\/span><span>'EOF'\n<\/span><\/span><span><span>{\"authenticationAllowed\":{\"authenticationTypes\":[\"AUTHENTICATION_TYPE_PASSWORD\",\"AUTHENTICATION_TYPE_PUBKEY\",\"AUTHENTICATION_TYPE_KBDINTERACTIVE\"]}}\n<\/span><\/span><span><span>{\"finalize\":{}}\n<\/span><\/span><span><span>EOF<\/span>\n<\/span><\/code><\/pre>\n<p>Router response:<\/p>\n<pre><code><span><span>{<\/span>\n<\/span><span> <span>\"authenticationAllowed\"<\/span><span>:<\/span> <span>{<\/span><span>}<\/span>\n<\/span><span><span>}<\/span>\n<\/span><span><span>{<\/span><span>}<\/span>\n<\/span><\/code><\/pre>\n<blockquote>\n<p><strong style=\"color: rgb(26, 26, 26)\">Caution:<\/strong> To restrict to pubkey-only, pass <code>[\"AUTHENTICATION_TYPE_PUBKEY\"]<\/code>. This disables password and keyboard-interactive \u2014 validate SSH access before finalizing to avoid lockout.<\/p>\n<\/blockquote>\n<h3 style=\"color: rgb(26, 26, 26); font-weight: 700\">RotateHostParameters: set AuthorizedPrincipalsCommand (HIBA)<\/h3>\n<p>Enable dynamic principal Authorization using the HIBA binary:<\/p>\n<pre><code><span>grpcurl <span>-plaintext<\/span> <span>\\<\/span>\n<\/span><span> -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${GRPC_MD<span>[<\/span>@<span>]<\/span>}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-d<\/span> <span>'@'<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${XR}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> gnsi.credentialz.v1.Credentialz.RotateHostParameters <span>&lt;&lt;<\/span><span>'EOF'\n<\/span><\/span><span><span>{\"authorizedPrincipalCheck\":{\"tool\":\"TOOL_HIBA_DEFAULT\"}}\n<\/span><\/span><span><span>{\"finalize\":{}}\n<\/span><\/span><span><span>EOF<\/span>\n<\/span><\/code><\/pre>\n<p>Router response:<\/p>\n<pre><code><span><span>{<\/span>\n<\/span><span> <span>\"authorizedPrincipalCheck\"<\/span><span>:<\/span> <span>{<\/span><span>}<\/span>\n<\/span><span><span>}<\/span>\n<\/span><span><span>{<\/span><span>}<\/span>\n<\/span><\/code><\/pre>\n<p>To clear HIBA and revert to static Authorization, use <code>\"tool\":\"TOOL_UNSPECIFIED\"<\/code>.<\/p>\n<h3 style=\"color: rgb(26, 26, 26); font-weight: 700\">Verify on IOS XR<\/h3>\n<p>After any Rotation, verify with CLI:<\/p>\n<pre><code><span>show ssh\n<\/span><span>show ssh server\n<\/span><span>show crypto key mypubkey ed25519\n<\/span><span>show gnsi state\n<\/span><\/code><\/pre>\n<p>Verify via gRPC \u2014 confirm the Host Keys reflect the latest Rotation:<\/p>\n<pre><code><span>grpcurl <span>-plaintext<\/span> <span>\\<\/span>\n<\/span><span> -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${GRPC_MD<span>[<\/span>@<span>]<\/span>}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> <span>-d<\/span> <span>'{}'<\/span> <span>\\<\/span>\n<\/span><span> <span>\"<span>${XR}<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> gnsi.credentialz.v1.Credentialz.GetPublicKeys\n<\/span><\/code><\/pre>\n<p>Response after an external key upload:<\/p>\n<pre><code><span><span>{<\/span>\n<\/span><span> <span>\"publicKeys\"<\/span><span>:<\/span> <span>[<\/span>\n<\/span><span> <span>{<\/span>\n<\/span><span> <span>\"publicKey\"<\/span><span>:<\/span> <span>\"c3NoLWVkMjU1MTkgQUFBQUMzTnph...\"<\/span><span>,<\/span>\n<\/span><span> <span>\"keyType\"<\/span><span>:<\/span> <span>\"KEY_TYPE_ED25519\"<\/span>\n<\/span><span> <span>}<\/span>\n<\/span><span> <span>]<\/span>\n<\/span><span><span>}<\/span>\n<\/span><\/code><\/pre>\n<p>Decode the <code>publicKey<\/code> to confirm it matches the uploaded material:<\/p>\n<pre><code><span><span>echo<\/span> <span>\"c3NoLWVkMjU1MTkgQUFBQUMzTnph...\"<\/span> <span>|<\/span> base64 <span>-d<\/span>\n<\/span><span><span># ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... ext-host@lab<\/span>\n<\/span><\/code><\/pre>\n<h3 style=\"color: rgb(26, 26, 26); font-weight: 700\">grpcurl Debugging Aids<\/h3>\n<p>Use <code>describe<\/code> and <code>-msg-template<\/code> to explore the message structure:<\/p>\n<pre><code><span>grpcurl -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> describe gnsi.credentialz.v1.Credentialz.RotateAccountCredentials\n<\/span><span>grpcurl -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> -msg-template describe gnsi.credentialz.v1.RotateAccountCredentialsRequest\n<\/span><span>grpcurl -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> -msg-template describe gnsi.credentialz.v1.RotateHostParametersRequest\n<\/span><span>grpcurl -import-path <span>\"<span>$GNSI_IMPORT<\/span>\"<\/span> <span>-proto<\/span> <span>\"<span>$PROTO_CREDZ<\/span>\"<\/span> <span>\\<\/span>\n<\/span><span> -msg-template describe gnsi.credentialz.v1.CanGenerateKeyRequest\n<\/span><\/code><\/pre>\n<hr>\n<h2 style=\"color: rgb(26, 26, 26); font-weight: 700\">SPIFFE Mapping Path<\/h2>\n<p>The SPIFFE ID-based Authentication and Authorization workflow uses a mapping file at:<\/p>\n<pre><code><span>\/misc\/config\/grpc\/gnsi\/credentialz\/spiffe-user-map.json\n<\/span><\/code><\/pre>\n<p>If the file is missing or invalid, mapping is empty. When present, SPIFFE IDs are resolved to usernames for gRPC Authentication. See the <a href=\"https:\/\/www.cisco.com\/c\/en\/us\/td\/docs\/iosxr\/cisco8000\/programmability\/b-programmability-configuration-guide-cisco8000\/m-grpc-authentication.html\" style=\"color: rgb(0, 102, 204)\">SPIFFE ID-based Authentication section<\/a> in the Cisco guide.<\/p>\n<hr>\n<h2 style=\"color: rgb(26, 26, 26); font-weight: 700\">Monitoring with YANG Telemetry<\/h2>\n<p>The OpenConfig YANG model <strong style=\"color: rgb(26, 26, 26)\"><code>openconfig-gnsi-credentialz<\/code><\/strong> is intended to expose Credential metadata and access Counters.<\/p>\n<blockquote>\n<p><strong style=\"color: rgb(26, 26, 26)\">On 25.3.1:<\/strong> <code>gnmic capabilities<\/code> advertises <code>openconfig-gnsi-credentialz, 0.6.0<\/code>, but <code>gnmi Get<\/code> returns <code>NotFound<\/code> on every documented Credentialz leaf (<code>active-host-key-version<\/code>, <code>\u2026\/counters\/access-{accepts,rejects}<\/code>, the per-user <code>password-version<\/code>, etc.). The only Credentialz signal currently exposed is the per-RPC counter view under <code>openconfig-gnsi-authz:authz-policy-counters<\/code> (rows for <code>gnsi.credentialz.v1.Credentialz\/{CanGenerateKey,GetPublicKeys,RotateAccountCredentials,RotateHostParameters}<\/code>). The leaves below are documented for reference \u2014 treat them as forward-looking on this build.<\/p>\n<\/blockquote>\n<p><strong style=\"color: rgb(26, 26, 26)\">SSH server Credential metadata<\/strong> (base: <code>\/system\/ssh-server\/state<\/code>):<\/p>\n<table class=\"english-numbers\">\n<thead>\n<tr>\n<th>YANG path<\/th>\n<th>What it tells<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>\/system\/ssh-server\/state\/active-trusted-user-ca-keys-version<\/code><\/td>\n<td>Version of the trusted CA Keys for client Certificate validation<\/td>\n<\/tr>\n<tr>\n<td><code>\/system\/ssh-server\/state\/active-trusted-user-ca-keys-created-on<\/code><\/td>\n<td>When the CA Keys were installed<\/td>\n<\/tr>\n<tr>\n<td><code>\/system\/ssh-server\/state\/active-host-certificate-version<\/code><\/td>\n<td>Version of the Host Certificate<\/td>\n<\/tr>\n<tr>\n<td><code>\/system\/ssh-server\/state\/active-host-certificate-created-on<\/code><\/td>\n<td>When the Host Certificate was installed<\/td>\n<\/tr>\n<tr>\n<td><code>\/system\/ssh-server\/state\/active-host-key-version<\/code><\/td>\n<td>Version of the Host Public Key<\/td>\n<\/tr>\n<tr>\n<td><code>\/system\/ssh-server\/state\/active-host-key-created-on<\/code><\/td>\n<td>When the Host Key was installed<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong style=\"color: rgb(26, 26, 26)\">SSH server access Counters<\/strong> (base: <code>\/system\/ssh-server\/state\/counters<\/code>):<\/p>\n<table class=\"english-numbers\">\n<thead>\n<tr>\n<th>YANG path<\/th>\n<th>What it tells<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>\/system\/ssh-server\/state\/counters\/access-accepts<\/code><\/td>\n<td>Total successful SSH connections<\/td>\n<\/tr>\n<tr>\n<td><code>\/system\/ssh-server\/state\/counters\/access-rejects<\/code><\/td>\n<td>Total denied SSH connections<\/td>\n<\/tr>\n<tr>\n<td><code>\/system\/ssh-server\/state\/counters\/last-access-accept<\/code><\/td>\n<td>Timestamp of the most recent successful SSH connection<\/td>\n<\/tr>\n<tr>\n<td><code>\/system\/ssh-server\/state\/counters\/last-access-reject<\/code><\/td>\n<td>Timestamp of the most recent SSH rejection<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong style=\"color: rgb(26, 26, 26)\">Per-user Credential metadata<\/strong> (base: <code>\/system\/aaa\/authentication\/users\/user[username=&lt;user&gt;]\/state<\/code>):<\/p>\n<table class=\"english-numbers\">\n<thead>\n<tr>\n<th>YANG path (relative to base)<\/th>\n<th>What it tells<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>...\/authorized-keys-list-version<\/code><\/td>\n<td>Version of the Authorized Keys for this user<\/td>\n<\/tr>\n<tr>\n<td><code>...\/authorized-keys-list-created-on<\/code><\/td>\n<td>When the Authorized Keys were installed<\/td>\n<\/tr>\n<tr>\n<td><code>...\/authorized-principals-list-version<\/code><\/td>\n<td>Version of the Authorized Principals for this user<\/td>\n<\/tr>\n<tr>\n<td><code>...\/authorized-principals-list-created-on<\/code><\/td>\n<td>When the Authorized Principals were installed<\/td>\n<\/tr>\n<tr>\n<td><code>...\/password-version<\/code><\/td>\n<td>Version of the Password for this user<\/td>\n<\/tr>\n<tr>\n<td><code>...\/password-created-on<\/code><\/td>\n<td>When the Password was set<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Subscribe to <code>access-rejects<\/code> and <code>last-access-reject<\/code> to detect SSH Authentication failures after a Credential Rotation. A spike in rejects after a RotateHostParameters usually means the new Host Key or CA Key is not matching \u2014 verify with <code>GetPublicKeys<\/code>.<\/p>\n<p>The <code>*-version<\/code> and <code>*-created-on<\/code> leaves mirror the exact strings passed during the Rotate RPCs, so Telemetry pipelines can correlate SSH access patterns with specific Credential deployments.<\/p>\n<blockquote>\n<p><strong style=\"color: rgb(26, 26, 26)\">Note:<\/strong> When Credentialz is enabled, the standard YANG leaves for SSH keys and passwords under <code>\/system\/aaa\/authentication\/users\/user\/config\/<\/code> are treated as invalid. Credentialz owns those Credentials.<\/p>\n<\/blockquote>\n<h3 style=\"color: rgb(26, 26, 26); font-weight: 700\">Quick Telemetry check with gnmic<\/h3>\n<p>Confirm what the device actually advertises and what it returns for the documented paths:<\/p>\n<pre><code><span><span># 1) What model + revision does the device claim to support?<\/span>\n<\/span><span>gnmic <span>-a<\/span> <span>\"<span>${XR}<\/span>\"<\/span> <span>--insecure<\/span> <span>-u<\/span> <span>\"<span>${XR_USER}<\/span>\"<\/span> <span>-p<\/span> <span>\"<span>${XR_PASS}<\/span>\"<\/span> capabilities <span>\\<\/span>\n<\/span><span> <span>|<\/span> <span>grep<\/span> <span>-i<\/span> credentialz\n<\/span><span><span># openconfig-gnsi-credentialz, 0.6.0 (gnmi)<\/span>\n<\/span><span><span># cisco-xr-openconfig-gnsi-credentialz-deviations, 2024-08-29<\/span>\n<\/span><span><span># 2) Try the documented leaves \u2014 these will all be NotFound on 25.3.1:<\/span>\n<\/span><span>gnmic <span>-a<\/span> <span>\"<span>${XR}<\/span>\"<\/span> <span>--insecure<\/span> <span>-u<\/span> <span>\"<span>${XR_USER}<\/span>\"<\/span> <span>-p<\/span> <span>\"<span>${XR_PASS}<\/span>\"<\/span> get <span>\\<\/span>\n<\/span><span> <span>--path<\/span> <span>\"\/system\/ssh-server\/state\/active-host-key-version\"<\/span> <span>\\<\/span>\n<\/span><span> <span>--path<\/span> <span>\"\/system\/ssh-server\/state\/counters\/access-rejects\"<\/span> <span>||<\/span> <span>true<\/span>\n<\/span><span><span># 3) Working fallback \u2014 gRPC-server RPC counters via openconfig-gnsi-authz:<\/span>\n<\/span><span>gnmic <span>-a<\/span> <span>\"<span>${XR}<\/span>\"<\/span> <span>--insecure<\/span> <span>-u<\/span> <span>\"<span>${XR_USER}<\/span>\"<\/span> <span>-p<\/span> <span>\"<span>${XR_PASS}<\/span>\"<\/span> get <span>\\<\/span>\n<\/span><span> <span>--path<\/span> <span>\"\/system\/grpc-servers\/grpc-server\/network-instance\/state\/authz-policy-counters\/rpcs\/rpc[name=\/gnsi.credentialz.v1.Credentialz\/RotateHostParameters]\/state\"<\/span>\n<\/span><\/code><\/pre>\n<p>The third call is the practical signal for &#8220;did anyone hit Credentialz?&#8221; today; the first two are placeholders until the YANG implementation lands.<\/p>\n<hr>\n<h2 style=\"color: rgb(26, 26, 26); font-weight: 700\">Things to watch out for<\/h2>\n<ul>\n<li><strong style=\"color: rgb(26, 26, 26)\">One RPC at a time.<\/strong> Overlapping RotateAccountCredentials or RotateHostParameters calls are rejected. Serialize across both RPCs.<\/li>\n<li><strong style=\"color: rgb(26, 26, 26)\">Validate before finalize.<\/strong> Always test SSH (or gRPC, for the password rotate) with the new Credentials in a second terminal before sending FinalizeRequest. Cancel the stream to Rollback if validation fails.<\/li>\n<li><strong style=\"color: rgb(26, 26, 26)\">Authorized Keys are replaced, not merged.<\/strong> AuthorizedKeysRequest replaces the entire Authorized Keys list for the account. Include all Keys that should remain valid. Sending an empty list is the documented &#8220;remove&#8221; path.<\/li>\n<li><strong style=\"color: rgb(26, 26, 26)\">Restricting Authentication methods<\/strong> can lock out users who rely on the disabled method. Always keep at least one working method.<\/li>\n<li><strong style=\"color: rgb(26, 26, 26)\">HIBA support.<\/strong> Setting <code>TOOL_HIBA_DEFAULT<\/code> in AuthorizedPrincipalCheckRequest enables dynamic principal Authorization via the HIBA binary. Clearing it (<code>TOOL_UNSPECIFIED<\/code>) reverts to static Authorization.<\/li>\n<li><strong style=\"color: rgb(26, 26, 26)\"><code>PasswordRequest<\/code> works on 25.3.1.<\/strong> Earlier images returned <code>Unimplemented<\/code>; the live build we tested re-binds the password and the new password is immediately usable for gRPC AAA. Always rotate against a non-admin user (e.g. <code>mike<\/code>), not the gRPC rotate-authority you are currently authenticated as.<\/li>\n<li><strong style=\"color: rgb(26, 26, 26)\"><code>GenerateKeys<\/code> is destructive across all algorithms.<\/strong> Even a single-algo <code>keyParams<\/code> wipes RSA, ECDSA-P-256\/P-384\/P-521, DSA and Ed25519. DSA and ECDSA-P-384 are <strong style=\"color: rgb(26, 26, 26)\">irreversible<\/strong> through gRPC (the device&#8217;s <code>CanGenerateKey<\/code> rejects both). Always pass every algorithm you want to keep in the same <code>keyParams<\/code> list. See <a href=\"https:\/\/xrdocs.io\/programmability\/blogs\/gnsi-credentialz-ssh-credential-rotation-on-cisco-ios-xr#generatekeys-is-destructive\" style=\"color: rgb(0, 102, 204)\">GenerateKeys is destructive<\/a>.<\/li>\n<li><strong style=\"color: rgb(26, 26, 26)\"><code>ServerKeysRequest<\/code> finalize bug (25.3.1).<\/strong> Finalize returns <code>Code: Unknown<\/code> with an internal <code>Failed to open file<\/code> message, <strong style=\"color: rgb(26, 26, 26)\">but the host-key store is still mutated<\/strong> \u2014 the uploaded private key replaces every existing host key. Have a multi-algo <code>generateKeys<\/code> recovery call ready.<\/li>\n<li><strong style=\"color: rgb(26, 26, 26)\"><code>CanGenerateKey<\/code> rejects two values that are in the proto enum.<\/strong> <code>KEY_GEN_SSH_KEY_TYPE_RSA_3072<\/code> and <code>KEY_GEN_SSH_KEY_TYPE_ECDSA_P_384<\/code> return <code>InvalidArgument: \"Can Generate Key:Invaid KeyGen input\"<\/code> (typo on the device). Stick to RSA-2048\/4096, ECDSA-P-256\/P-521, Ed25519.<\/li>\n<li><strong style=\"color: rgb(26, 26, 26)\">Default host-key inventory includes DSA and ECDSA-P-384.<\/strong> <code>GetPublicKeys<\/code> on a default NCS-540 lists 6 keys, two of which (<code>ssh-dss<\/code>, <code>ECDSA-P-384<\/code>) come back with <code>keyType<\/code> field omitted. DSA in particular is deprecated by OpenSSH; remove it via a clean multi-algo <code>generateKeys<\/code> call.<\/li>\n<li><strong style=\"color: rgb(26, 26, 26)\">Mid-stream cancel works but leaks subsystem name.<\/strong> Closing the bidi stream before sending <code>finalize<\/code> aborts the candidate, but the device returns <code>Code: Canceled, Message: \"Credz stream failure: EOF\"<\/code>. Treat this as expected; a follow-up call with the same payload + <code>finalize<\/code> succeeds cleanly.<\/li>\n<li><strong style=\"color: rgb(26, 26, 26)\">YANG telemetry is advertised but not implemented (25.3.1).<\/strong> Every documented Credentialz leaf returns <code>NotFound<\/code>. Use <code>openconfig-gnsi-authz:authz-policy-counters<\/code> for RPC-level visibility until the YANG implementation lands. Application-layer errors (<code>InvalidArgument<\/code>, <code>Unknown<\/code>) do <strong style=\"color: rgb(26, 26, 26)\">not<\/strong> increment <code>access-rejects<\/code> on those counters.<\/li>\n<li><strong style=\"color: rgb(26, 26, 26)\">Certz vs Credentialz.<\/strong> <a href=\"https:\/\/xrdocs.io\/programmability\/blogs\/gnsi-certz-tls-certificate-lifecycle-on-cisco-ios-xr\" style=\"color: rgb(0, 102, 204)\">Certz<\/a> handles gRPC TLS (Certificates, Trust Bundles, CRLs for the gRPC server). Credentialz handles SSH (Host Keys, user Keys, Passwords for interactive access). They are separate planes.<\/li>\n<\/ul>\n<h2 style=\"color: rgb(26, 26, 26); font-weight: 700\">Conclusion<\/h2>\n<p>Credentialz closes the loop between programmatic gRPC Management and interactive SSH access. Keys, Passwords, and Host parameters rotate through a standard gRPC API with built-in Rollback \u2014 no file edits, no reloads, no vendor-specific CLIs. Together with <a href=\"https:\/\/xrdocs.io\/programmability\/blogs\/gnsi-certz-tls-certificate-lifecycle-on-cisco-ios-xr\" style=\"color: rgb(0, 102, 204)\">Certz<\/a> for TLS Authentication, Credentialz covers both Authentication planes. Adding <a href=\"https:\/\/xrdocs.io\/programmability\/blogs\/grpc-authorization-with-authz\" style=\"color: rgb(0, 102, 204)\">Authz<\/a> for Service-level Authorization, <a href=\"https:\/\/xrdocs.io\/programmability\/blogs\/securing-gnmi-with-pathz\" style=\"color: rgb(0, 102, 204)\">Pathz<\/a> for Path-level Authorization, and <a href=\"https:\/\/xrdocs.io\/programmability\/blogs\/gnsi-acctz-management-plane-accounting-on-cisco-ios-xr\" style=\"color: rgb(0, 102, 204)\">Acctz<\/a> for Accounting completes the gNSI Security Lifecycle on IOS XR.<\/p>\n<ul>\n<li>Credentialz rotates <strong style=\"color: rgb(26, 26, 26)\">SSH Credentials<\/strong> \u2014 Passwords, Authorized Keys, Host Keys, Certificates, CA Keys, Authentication method restrictions \u2014 without a reload.<\/li>\n<li>Two streaming RPCs: <strong style=\"color: rgb(26, 26, 26)\">RotateAccountCredentials<\/strong> (per-user) and <strong style=\"color: rgb(26, 26, 26)\">RotateHostParameters<\/strong> (per-host). Both use the same stage-validate-finalize Transaction model as Certz.<\/li>\n<li>Two unary RPCs: <strong style=\"color: rgb(26, 26, 26)\">CanGenerateKey<\/strong> (preflight) and <strong style=\"color: rgb(26, 26, 26)\">GetPublicKeys<\/strong> (inspect current Host Keys).<\/li>\n<li>Only <strong style=\"color: rgb(26, 26, 26)\">one<\/strong> Credentialz RPC at a time. Overlapping calls fail.<\/li>\n<li><strong style=\"color: rgb(26, 26, 26)\">Authorized Keys are replaced<\/strong> entirely \u2014 include all valid Keys in the request.<\/li>\n<li><strong style=\"color: rgb(26, 26, 26)\">Host-key rotates are destructive.<\/strong> Both <code>generateKeys<\/code> (single-algo) and <code>serverKeys<\/code> (failed finalize on 25.3.1) wipe every other algorithm. Always rotate host keys with a multi-algo <code>generateKeys<\/code> that lists every algorithm you want to keep.<\/li>\n<li><strong style=\"color: rgb(26, 26, 26)\"><code>PasswordRequest<\/code> works on 25.3.1 NCS-540<\/strong> and the new password is immediately accepted by gRPC AAA \u2014 but always rotate non-admin users, not the gRPC rotate-authority.<\/li>\n<li><strong style=\"color: rgb(26, 26, 26)\"><code>openconfig-gnsi-credentialz<\/code> YANG telemetry is advertised but not implemented<\/strong> on 25.3.1. Use <code>openconfig-gnsi-authz:authz-policy-counters<\/code> for RPC-level visibility today.<\/li>\n<li><a href=\"https:\/\/xrdocs.io\/programmability\/blogs\/gnsi-certz-tls-certificate-lifecycle-on-cisco-ios-xr\" style=\"color: rgb(0, 102, 204)\">Certz<\/a> = gRPC TLS. Credentialz = SSH. Different planes, same Transaction pattern.<\/li>\n<\/ul>\n<p><strong style=\"color: rgb(26, 26, 26)\">What to do next:<\/strong> With Authentication covered (Certz + Credentialz), continue to Authorization with <a href=\"https:\/\/xrdocs.io\/programmability\/blogs\/grpc-authorization-with-authz\" style=\"color: rgb(0, 102, 204)\">Authz<\/a> for gRPC Service-level control.<\/p>\n<hr>\n<h2 style=\"color: rgb(26, 26, 26); font-weight: 700\">Reverting \/ Undo<\/h2>\n<table class=\"english-numbers\">\n<thead>\n<tr>\n<th>Situation<\/th>\n<th>How to revert<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">Cancel a Rotate mid-stream<\/strong><\/td>\n<td>Close the grpcurl stream (<code>Ctrl+C<\/code>) before sending <code>finalize<\/code>. The Candidate is discarded and the previous Credentials stay active. The device may return <code>Code: Canceled, Message: \"Credz stream failure: EOF\"<\/code> \u2014 this is the expected rollback signature on 25.3.1.<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">Remove Authorized Keys<\/strong><\/td>\n<td>Send a <code>RotateAccountCredentials<\/code> with an empty <code>authorizedKeys<\/code> list for the account. This reverts to password-only Authentication for that user.<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">Restore a rotated password<\/strong><\/td>\n<td>Send another <code>RotateAccountCredentials<\/code> with <code>password.accounts[*].password.plaintext<\/code> set to the original value. New password becomes effective on <code>finalize<\/code>.<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">Restore all Authentication methods<\/strong><\/td>\n<td>Send <code>RotateHostParameters<\/code> with all three types: <code>[\"AUTHENTICATION_TYPE_PASSWORD\",\"AUTHENTICATION_TYPE_PUBKEY\",\"AUTHENTICATION_TYPE_KBDINTERACTIVE\"]<\/code>.<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">Clear HIBA<\/strong><\/td>\n<td>Send <code>RotateHostParameters<\/code> with <code>\"tool\":\"TOOL_UNSPECIFIED\"<\/code> to revert to static principal Authorization.<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">Recover from a destructive <code>generateKeys<\/code> or a failed <code>serverKeys<\/code><\/strong><\/td>\n<td>Re-run <code>RotateHostParameters<\/code> with a multi-algo <code>generateKeys<\/code>: <code>[\"KEY_GEN_SSH_KEY_TYPE_RSA_2048\",\"KEY_GEN_SSH_KEY_TYPE_ECDSA_P_256\",\"KEY_GEN_SSH_KEY_TYPE_ECDSA_P_521\",\"KEY_GEN_SSH_KEY_TYPE_EDDSA_ED25519\"]<\/code> followed by <code>finalize<\/code>. <strong style=\"color: rgb(26, 26, 26)\">DSA and ECDSA-P-384 cannot be recovered through Credentialz<\/strong> \u2014 fall back to <code>crypto key generate dsa<\/code> \/ <code>crypto key generate ecdsa nistp384<\/code> from IOS XR CLI if you actually need them.<\/td>\n<\/tr>\n<tr>\n<td><strong style=\"color: rgb(26, 26, 26)\">Replace bad Host Key<\/strong><\/td>\n<td>Upload corrected material through <code>RotateHostParameters<\/code> with <code>serverKeys<\/code>. <strong style=\"color: rgb(26, 26, 26)\">Be aware<\/strong> of the 25.3.1 <code>Write Metadata to file failed<\/code> finalize bug \u2014 verify with <code>GetPublicKeys<\/code> and run a multi-algo <code>generateKeys<\/code> if other algorithms were silently wiped.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 style=\"color: rgb(26, 26, 26); font-weight: 700\">References<\/h2>\n<ol>\n<li>Cisco IOS XR \u2014 <a href=\"https:\/\/www.cisco.com\/c\/en\/us\/td\/docs\/iosxr\/cisco8000\/programmability\/b-programmability-configuration-guide-cisco8000\/m-grpc-authentication.html\" style=\"color: rgb(0, 102, 204)\">Programmability Configuration Guide: gRPC Authentication<\/a> (gNSI Credentialz updates, Rotate Credentialz RPC, Rotate account credentials, Rotate host parameters)<\/li>\n<li>OpenConfig \u2014 <a href=\"https:\/\/github.com\/openconfig\/gnsi\/blob\/main\/credentialz\/credentialz.proto\" style=\"color: rgb(0, 102, 204)\"><code>credentialz.proto<\/code><\/a>, <a href=\"https:\/\/github.com\/openconfig\/gnsi\/blob\/main\/credentialz\/README.md\" style=\"color: rgb(0, 102, 204)\"><code>credentialz\/README.md<\/code><\/a><\/li>\n<li>OpenConfig \u2014 <a href=\"https:\/\/www.netconfcentral.org\/modules\/openconfig-gnsi-credentialz\/2024-09-10\/source\/raw\/\" style=\"color: rgb(0, 102, 204)\"><code>openconfig-gnsi-credentialz<\/code> YANG model v0.7.0<\/a><\/li>\n<li>Series \u2014 <a href=\"https:\/\/xrdocs.io\/programmability\/blogs\/introducing-gnsi-grpc-native-aaa-for-cisco-ios-xr\" style=\"color: rgb(0, 102, 204)\">gNSI on IOS XR overview<\/a><\/li>\n<\/ol>\n<hr>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>The Problem Certz handles TLS for the gRPC Management Plane, but most network devices also have SSH access \u2014 console, VTY, interactive troubleshooting. Rotating\u2026<\/p>\n","protected":false},"author":1,"featured_media":240,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[111,238,240,69,243,37,223],"tags":[],"class_list":["post-247","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-automation-en","category-gnsi-en","category-grpc-en","category-iosxr-en-en","category-programmability-en","category-security-en","category-xr7-en"],"_links":{"self":[{"href":"https:\/\/www.anynet.ir\/blog\/wp-json\/wp\/v2\/posts\/247","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.anynet.ir\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.anynet.ir\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.anynet.ir\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.anynet.ir\/blog\/wp-json\/wp\/v2\/comments?post=247"}],"version-history":[{"count":1,"href":"https:\/\/www.anynet.ir\/blog\/wp-json\/wp\/v2\/posts\/247\/revisions"}],"predecessor-version":[{"id":249,"href":"https:\/\/www.anynet.ir\/blog\/wp-json\/wp\/v2\/posts\/247\/revisions\/249"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.anynet.ir\/blog\/wp-json\/wp\/v2\/media\/240"}],"wp:attachment":[{"href":"https:\/\/www.anynet.ir\/blog\/wp-json\/wp\/v2\/media?parent=247"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.anynet.ir\/blog\/wp-json\/wp\/v2\/categories?post=247"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.anynet.ir\/blog\/wp-json\/wp\/v2\/tags?post=247"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}