{"id":21354955,"url":"https://github.com/salrashid123/cert_bound_sts_server","last_synced_at":"2025-09-03T20:36:34.526Z","repository":{"id":40245972,"uuid":"443841839","full_name":"salrashid123/cert_bound_sts_server","owner":"salrashid123","description":"Certificate Bound Tokens using Security Token Exchange Server (STS)","archived":false,"fork":false,"pushed_at":"2024-08-15T18:31:33.000Z","size":271,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-16T05:25:50.502Z","etag":null,"topics":["certificate","golang","jwt","ssl","sts","tls"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/salrashid123.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-01-02T18:33:43.000Z","updated_at":"2024-08-15T18:31:37.000Z","dependencies_parsed_at":"2025-01-22T17:45:19.708Z","dependency_job_id":"21a880da-0003-46af-bcf4-67f2764e1fce","html_url":"https://github.com/salrashid123/cert_bound_sts_server","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/salrashid123/cert_bound_sts_server","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salrashid123%2Fcert_bound_sts_server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salrashid123%2Fcert_bound_sts_server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salrashid123%2Fcert_bound_sts_server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salrashid123%2Fcert_bound_sts_server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/salrashid123","download_url":"https://codeload.github.com/salrashid123/cert_bound_sts_server/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salrashid123%2Fcert_bound_sts_server/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273506903,"owners_count":25118066,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-03T02:00:09.631Z","response_time":76,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["certificate","golang","jwt","ssl","sts","tls"],"created_at":"2024-11-22T04:15:24.884Z","updated_at":"2025-09-03T20:36:34.514Z","avatar_url":"https://github.com/salrashid123.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Certificate Bound Tokens using Security Token Exchange Server (STS)\n\nSample demonstration of [Certificate Bound Tokens](https://tools.ietf.org/html/rfc8705) acquired from a [Security Token Exchange Server](https://datatracker.ietf.org/doc/html/rfc8693).\n\nThe basic idea behind bound tokens is that the signed bearer token (eg `JWT`) itself has information embedded within it which defines the transport/TLS client certificate that must be present for the token to be valid in.\n\nFor example , suppose you want to say \"only trust this JWT if it is presented within an mTLS context that used a _specific_ client certificate.\n\nBinding the token to the cert reduces the security risk of bearer tokens since nobody can reuse the bearer token without the client certificate\n\nAs an example, consider an application is in possession of a client certificate with the following hash:\n\n```bash\n$ openssl x509 -in certs/alice.crt -outform DER | openssl dgst -sha256 | cut -d\" \" -f2\n03b88e7242c830fe28f185d6e2fa034f118a82ea1302edfd4ef066b518b68f0e\n\n$ echo \"03b88e7242c830fe28f185d6e2fa034f118a82ea1302edfd4ef066b518b68f0e\" | xxd -r -p - | openssl enc -a \nA7iOckLIMP4o8YXW4voDTxGKguoTAu39TvBmtRi2jw4=\n\n```\n\nIf JWT token is issued with the following standard claim:\n\n\n```json\n{\n  \"alg\": \"RS256\",\n  \"kid\": \"61c8b23ef9f935c0d98cf57bd4862c146e7b9fb7\",\n  \"typ\": \"JWT\"\n}\n{\n  \"aud\": \"https://server.domain.com:8443\",\n  \"exp\": 1671834129,\n  \"iat\": 1641075729,\n  \"iss\": \"https://sts.domain.com\",\n  \"sub\": \"alice\",\n  \"cnf\": {\n    \"x5t#S256\": \"A7iOckLIMP4o8YXW4voDTxGKguoTAu39TvBmtRi2jw4=\"\n  }\n}\n```\n\nthen a server application can only consider that token to be valid if the mTLS session context contains the a peer leaf certificate which has the same hash as declared in the JWT.\n\n---\n\n### Background\n\n`OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens` [rfc 8705](https://tools.ietf.org/html/rfc8705) describes a mechanism where a specific specific claim in a JWT bearer token presented to a server includes the hash of the public certificate that is authorized and corresponds to the mTLS certificate currently used during the connection.\n\nFor example, if the public cert used by the client is `certs/alice.crt`, then the thumbprint is calculate as such and gets included into the JWT by the token issuer\n\n```text\n3.1.  JWT Certificate Thumbprint Confirmation Method\n\n   When access tokens are represented as JSON Web Tokens (JWT)[RFC7519],\n   the certificate hash information SHOULD be represented using the\n   \"x5t#S256\" confirmation method member defined herein.\n\n   To represent the hash of a certificate in a JWT, this specification\n   defines the new JWT Confirmation Method [RFC7800] member \"x5t#S256\"\n   for the X.509 Certificate SHA-256 Thumbprint.  The value of the\n   \"x5t#S256\" member is a base64url-encoded [RFC4648] SHA-256 [SHS] hash\n   (a.k.a. thumbprint, fingerprint or digest) of the DER encoding [X690]\n   of the X.509 certificate [RFC5280].  The base64url-encoded value MUST\n   omit all trailing pad '=' characters and MUST NOT include any line\n   breaks, whitespace, or other additional characters.\n```\n\nWhich eventually is sealed into a bearer token (JWT in this case) using the following claim:\n\n```json\n{\n  \"cnf\": {\n    \"x5t#S256\": \"A7iOckLIMP4o8YXW4voDTxGKguoTAu39TvBmtRi2jw4=\"\n  }\n}\n```\n\nFor more information, see\n\n* [Serverless Security Token Exchange Server(STS) and gRPC STS credentials](https://github.com/salrashid123/sts_server)\n* [Envoy WASM and LUA filters for Certificate Bound Tokens](https://github.com/salrashid123/envoy_cert_bound_token)\n* [golang-jwt for Trusted Platform Module (TPM)](https://github.com/salrashid123/golang-jwt-tpm)\n* [golang-jwt for PKCS11](https://github.com/salrashid123/golang-jwt-pkcs11)\n* [golang-jwt for post quantum cryptography](https://github.com/salrashid123/golang-jwt-pqc.git)\n\n---\n\nThis repo shows how to setup a sample STS server that checks the mtls connection and returns a bound JWT.  The resource REST server can verify the mtls session against the presented JWT:\n\n\n![images/bound_token.png](images/bound_token.png)\n\n\n### Setup\n\nFirst override the hosts value as shown below.  This is done just for TLS SNI simplicity\n\n`/etc/hosts`:\n\n```\n127.0.0.1  grpc.domain.com sts.domain.com server.domain.com\n```\n\n#### STS server\n\nRun the STS server.  \n\nThe keyID is just an opaque sha1 hash of the sts.crt's public key and just use in the JWK (in reality, it can be any value since its just used to lookup the key in `jwk.json`\n\n```bash\ncd sts_server/\ngo run sts_server.go --port :8081 --tlsCA ../certs/tls-ca.crt \\\n  --tlsCert ../certs/sts.crt --tlsKey ../certs/sts.key \\\n  --jwtPrivateKey ../certs/jwt.key --jwtKeyID 61c8b23ef9f935c0d98cf57bd4862c146e7b9fb7\n```\n\n### curl\n\nYou can test the STS server by itself by using curl and mTLS:\n\n```bash\n# test as alice\n$ curl -s -X POST -H \"Content-Type: application/json\" -d @curl/sts_req_alice.json  \\\n    -H \"host: sts.domain.com\"   --resolve  sts.domain.com:8081:127.0.0.1 \\\n    --cert certs/alice.crt    --key certs/alice.key  \\\n    --cacert certs/tls-ca.crt   https://sts.domain.com:8081/token | jq '.'\n\n# test as bob\n$ curl -s -X POST -H \"Content-Type: application/json\" -d @curl/sts_req_bob.json  \\\n    -H \"host: sts.domain.com\"   --resolve  sts.domain.com:8081:127.0.0.1 \\\n    --cert certs/bob.crt    --key certs/bob.key  \\\n    --cacert certs/tls-ca.crt   https://sts.domain.com:8081/token | jq '.'\n```\n\nThe JWT returned has the certificate hash burned into it as shown in the claims above.\n\n### REST\n\nTo test REST, first run the backend server\n\n```bash\ncd http/\ngo run src/server/server.go --port :8443 --tlsCA ../certs/tls-ca.crt  \\\n    --tlsCert ../certs/server.crt \\\n    --tlsKey ../certs/server.key --jwkFile ../certs/jwk.json\n```\n\nThen run the client\n\n```bash\ncd http/\ngo run src/client/client.go --tlsCA ../certs/tls-ca.crt  \\\n    --tlsCert ../certs/alice.crt \\\n    --tlsKey ../certs/alice.key --stsaddress https://sts.domain.com:8081/token\n```\n\nWhat the client does on startup is contact the STS server over mTLS.\n\nIt then presents the initial token for the client\n\nThe STS server checks the mTLS client certificate, extracts its hash, and then if the initial token is valid, it will reissue a new JWT but one with the certificate hash burned into the claim.\n\nFrom there, the client will emit that token to the resource server again over the mTLS using the same client certificate it used on the STS server  The resource will check the JWT, extract the claims that describes the client cert its bound to and then check the actual TLS context for the certificate presented.\n\nIf all goes well, the api call is allowed through.\n\nIf you want to test a failure, you can uncomment the code section in `client.go` which tries to use alice's bound token with bob's client certificate.  This second api call should fail since there is a mismatch\n\n### gRPC\n\ngRPC natively support `STS` credentials see [grpc.credentials.sts](https://pkg.go.dev/google.golang.org/grpc/credentials/sts).\n\nI began to write a simple client server similar to REST that used STS and certificate bound tokens but ran into an issue:\n\nWhen gRPC contacts an STS server on its own, it does NOT use client certificates and simply uses the generic http TLS system certs:\n\nsee [sts.go](https://github.com/grpc/grpc-go/blob/master/credentials/sts/sts.go#L195-L204)\n\nThis is problem since the STS server needs to know about the client cert for all this to work anyway. [Issue#5099](https://github.com/grpc/grpc-go/issues/5099)\n\nFor now, i've just forked the pending PR above into `\"github.com/salrashid123/sts_server/sts\"` which supports the custom `HTTPClient`\n\n```bash\necho -n iamtheeggman \u003e /tmp/cred.txt\n\ngo run src/grpc_server.go --grpcport :50051 \\\n   --tlsCert ../certs/grpc.crt \\\n   --tlsKey ../certs/grpc.key \\\n   --tlsCA ../certs/tls-ca.crt  \\\n   --jwkFile ../certs/jwk.json\n\ngo run src/grpc_client.go --host localhost:50051 \\\n   --servername grpc.domain.com --tlsCA ../certs/tls-ca.crt \\\n   --tlsCert ../certs/alice.crt --tlsKey ../certs/alice.key \\\n   --stsaddress https://sts.domain.com:8081/token --stsCred /tmp/cred.txt \\\n   --stsSNIServerName sts.domain.com\n```\n\n### Embedding private keys into Hardware\n\nIn the example above, the TLS certificates are plain PEM files on disk.  Users are free to embed the mTLS certificate into Hardware\nlike `Trusted Platform Module (TPM)` or `Yubikey` or other HSMs.  That way, at least the client certificate is more secure.\n\n\nFor additional information, see:\n\n* [mTLS with TPM bound private key](https://github.com/salrashid123/go_tpm_https_embed)\n* [golang-jwt library for Trusted Platform Module (TPM)](https://blog.salrashid.dev/articles/2021/go-jwt-tpm/)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsalrashid123%2Fcert_bound_sts_server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsalrashid123%2Fcert_bound_sts_server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsalrashid123%2Fcert_bound_sts_server/lists"}