How to perform ott enrollment with client API

I ran into BAD_REQUEST (400) trying to do this manually with Client API REST requests. The spec says the /enroll/ott operation consumes application/pkcs10 and requires a query param token of type uuid. I think the token in question is the token UUID in the enrollment object, not the JWT. I tried sending the request with a raw body pkcs10 CSR with and without escaped newlines as well as JSON body with the escaled newline CSR in property csr to match the request schema of the API session certificate request.

Controller complains when I try to enroll and I suspect I’m not composing the request correctly.

ERROR fabric/controller/api.(*ResponderImpl).RespondWithApiError: {error=[applicationXX509UserCert producer has not yet been implemented] requestId=[oIjaQUSdX]} could not respond with error, producer errored

I know the enrollment is valid because I can get a cert with ziti edge enroll and extract the key/cert from identity JSON file and authenticate to get an API session token that allows me to perform admin operations with ziti CLI. I obtained the token out-of-band and passed it to the CLI: ziti edge login --read-only=false --token {{token}} to verify the API session’s admin privileges.

Here’s how I obtained the newline-escaped CSR. I also tried sending this with unescaped newlines in the raw body of the POST request.

$ openssl req -newkey rsa:2048 -nodes -subj "/C=CA/ST=ON/L=Toronto/O=ACME/CN=admin" -keyout /tmp/kenadmin.key -out /tmp/kenadmin.csr
......+..+...+...+...+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*.+.....+...+......+....+..+.........+....+...+...+.....+......+...+...+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*...........+....+..+....+...+..+...+...+...+......+.+...+.........+..+.+.................+.+.....+.+.........+......+......+..............+...+...+...++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++
..+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*.+..+.+..+...+.......+.....................+......+...+........+....+.........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*..+.....................+.....+....+............+...+.................+...+.+.........+..+.........+......+.......+.....+...+....+...+......+......+........+................+.....+....+..+...+...+.......+...
+...+...............+......+........+.......+........+.+....................+.+.....+.+...+.....+....+..+.+.....+..........+.....+.........+......+....+..............+.+........+......+......+...+.......+..............+.......+..+.......+......+............+............+........+.+..+...+.........+...+...+..........+.....+......+......+....+.....+......+...+......+....+..+.......+..+....+.........+..+....+.....+....
...+............+..+...+...............+.+..+..........+.........+..................+...+...+..+....+...+......+..+....+..............+.........+................+.....+.+.....+...+...+..........+..................+..+....+...+..+..........+.........+..+.......+..+.+........+...............+....+..+...+...+...+....+......+.........+...+..+.+...........+..........+........+.+..+.............+...........+.+........+...
.+........+....+.....+....+............+............+.....+...+.......+...+...+.....+.......+...+..........................+...+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-----

$ openssl req -noout -subject < /tmp/kenadmin.csr                                                                                   
subject=C = CA, ST = ON, L = Toronto, O = ACME, CN = admin

$ CSR=$( < /tmp/kenadmin.csr) bash -c "echo \${CSR//$'\n'/\\\n/}"
-----BEGIN CERTIFICATE REQUEST-----\n/MIICkDCCAXgCAQAwSzELMAkGA1UEBhMCQ0ExCzAJBgNVBAgMAk9OMRAwDgYDVQQH\n/DAdUb3JvbnRvMQ0wCwYDVQQKDARBQ01FMQ4wDAYDVQQDDAVhZG1pbjCCASIwDQYJ\n/KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKfLAbxWZj7x+XHESx3zwRegvhooeCNF\n/E+zvPLd8KCbVKjg/c3zvY5OMD9QSaOhd8cXP65O5dqaUW/aLsqIm8ahgu9fi/2lT\n/0PKw83ZXEmTlm3ikFHU7reGuQGiZEgXzuO8JhEiRejOGnQBjWc2cNlzuXZRY/v9z\n/RK6FphdiBqG/U3GGQoLm502VDW9gY2U5pKrypj5c3T73tNa06LIpxCJ/V4peth5+\n/DPWcCNRFIN/LbFNsrlZGVHyBaxRwmasnULyFIPXxt5KrvS0MacHLLi3errO032dk\n/IjAweYkeZIFzONYqkGnBITxn9RIQOzh3QZFPCqESU720pLoaIiWZYq0CAwEAAaAA\n/MA0GCSqGSIb3DQEBCwUAA4IBAQBY48nc5N5ytacXAj4zIf9htFQQvUI+rEKsHaGR\n/l1lht2NFYudY0RIqviykinR4a8ozUxpPV6N3n92bE68BgtVn4dwxtpYNsyt2KhZ7\n/wGxPWbygwIqS+WbIcF3ou4gLeA//K+Rxl6e2QA1kmq67apmEZCqQsAyo6hsQavYH\n/8/Tq9rN09L56wkgD7Us3mOyawjLTX2bUYfgBbKhalrs48NTLx1ICWnDr1A1kaCYv\n/mOGRADCGyVxf6B7TfBb0wIVt2EBUzZ+itqhSYdaHr7ahcK0LlK36fXi+S11zF4RZ\n/IreYix7sy+/TTKwZA9s4SKd3fnLWRGvS6Vjq6i6BQhC2l2IG\n/-----END CERTIFICATE REQUEST-----

I tried these request headers.

POST /edge/client/v1/enroll/ott?token=7f790ac8-a486-439b-83d5-eec670c6d57c HTTP/1.1

  • Accept: application/x-x509-user-cert with and without this request header
  • Content-Type: application/pkcs10 with raw body and CSR with and without escaped newlines
  • Content-Type: application/json with JSON body with csr prop and CSR with escaped newlines

The token uuid is from the enrollment I created in mgmt API for an existing user. The enollment is visible in mgmt API /enrollments.

{
  "_links": {
    "self": {
      "href": "./enrollments/4672JrUeWhCg6kJ3oEwm9d"
    }
  },
  "createdAt": "0001-01-01T00:00:00.000Z",
  "id": "4672JrUeWhCg6kJ3oEwm9d",
  "tags": {},
  "updatedAt": "0001-01-01T00:00:00.000Z",
  "expiresAt": "2033-03-06T15:51:35.000Z",
  "identity": {
    "_links": {
      "auth-policies": {
        "href": "./auth-policies/default"
      },
      "authenticators": {
        "href": "./identities/q4KhUbRJB/authenticators"
      },
      "edge-router-policies": {
        "href": "./identities/q4KhUbRJB/edge-router-policies"
      },
      "edge-routers": {
        "href": "./identities/q4KhUbRJB/edge-routers"
      },
      "enrollments": {
        "href": "./identities/q4KhUbRJB/enrollments"
      },
      "failed-service-requests": {
        "href": "./identities/q4KhUbRJB/failed-service-requests"
      },
      "posture-data": {
        "href": "./identities/q4KhUbRJB/posture-data"
      },
      "self": {
        "href": "./identities/q4KhUbRJB"
      },
      "service-configs": {
        "href": "./identities/q4KhUbRJB/service-configs"
      },
      "service-policies": {
        "href": "./identities/q4KhUbRJB/service-policies"
      },
      "services": {
        "href": "./identities/q4KhUbRJB/services"
      }
    },
    "entity": "identities",
    "id": "q4KhUbRJB",
    "name": "Default Admin"
  },
  "identityId": "q4KhUbRJB",
  "jwt": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbSI6Im90dCIsImV4cCI6MTk5MzczNzA5NSwiaXNzIjoiaHR0cHM6Ly9taW5pY29udHJvbGxlci56aXRpOjQ0MyIsImp0aSI6IjdmNzkwYWM4LWE0ODYtNDM5Yi04M2Q1LWVlYzY3MGM2ZDU3YyIsInN1YiI6InE0S2hVYlJKQiJ9.I0TbZmT9-cS6C519L1vG0H_8_8JHJM-JO6Gxd5I5IFszhPflxfI5uFXrqw_9siRglvoBCZyQF_kMRmem719ccz6DtrHfjFFePgWTlNNUlXXHhHeOCN2vP5k6DUBHsWbGqXugTpZDw2Am7vBEMuEt2fqA4PlnCShFSRoWapAuQ2uMLkix4TOfEzzy7IlGb0qmB2FfI7sAamqqQMA-_p5ANFhDd8Usgq33BNmUmTE_UWDHgS9OEmgRPedu8kPomKf0O9pYfieUaZTQrwfDFj8oEwE5FKk7YN58lnvg29GiZUAJei3lrE5YS0lxppwq6Nm2unXQaz2qHwRO90xD3-Plwv4K6saU-VoMjj_c174zeKgg72Y5E-M0mKHHpf7xSZdoxUVjZB3U3D9wybZjLEg3veBCei4pt4QyzoCd2OxQ3GtUkqBQvIYKHLlHyD-8r05YwbAuotyo2q1LcpGfGmmwYQV8k4aka_7yGQ6u-GqFkxOMOCydB2BHQ9g8REht7D7cf5-Mgmh-9lijylkiQwWlraJUYBIWLE8lSg_h0EF7CA0koqogAGx2EjeUvgdgUPBYXWG-Pm6p_WnfDXy3DwK06BkVrvmQhYwRcxjjmRuCSXEhg3daF5dLjTKP0sClD8NeMTD59ft20hEW42mWX1QXMHH85tZxu40_hGMlZu5Q5pg",
  "method": "ott",
  "token": "7f790ac8-a486-439b-83d5-eec670c6d57c"
}

Looking at the code, please use the /enroll endpoint with the following query parameters:

token=<uuid>
method=ott

For headers, send in:

content-type: application/x-pem-file

For a payload, send in the PEM-formatted CSR

POST /enroll?method=ott&token=<uuid>

content-type: application/x-pem/file

-----BEGIN NEW CERTIFICATE REQUEST-----
MIIDVDCCAr0CAQAweTEeMBwGA1UEAxMVd3d3Lmpvc2VwaGNoYXBtYW4uY29tM
...
 ZSZTusPFTLKaqValdnS9Uw+6Vq7/I4ouDA8QBIuaTFtPOp+8wEGBHQ==
-----END NEW CERTIFICATE REQUEST-----

The endpoint you are referring to (/enroll/ott) needs to be removed or updated. I don’t think it will work as documented.

That worked! I got a long-lived certificate for the ott enrollment I created in mgmt API. I’d initially ignored that enroll operation in the spec because it’s named “Legacy enrollment endpoint.”