{"id":28113199,"url":"https://github.com/tomitribe/http-signatures-java","last_synced_at":"2026-01-25T11:40:13.895Z","repository":{"id":26755312,"uuid":"30213161","full_name":"tomitribe/http-signatures-java","owner":"tomitribe","description":"Java Client Library for HTTP Signatures","archived":false,"fork":false,"pushed_at":"2022-08-23T09:41:20.000Z","size":224,"stargazers_count":92,"open_issues_count":8,"forks_count":40,"subscribers_count":23,"default_branch":"master","last_synced_at":"2025-11-07T02:00:47.275Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","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/tomitribe.png","metadata":{"files":{"readme":"README.adoc","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}},"created_at":"2015-02-02T22:54:23.000Z","updated_at":"2025-10-10T13:57:43.000Z","dependencies_parsed_at":"2022-07-13T13:20:26.312Z","dependency_job_id":null,"html_url":"https://github.com/tomitribe/http-signatures-java","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/tomitribe/http-signatures-java","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomitribe%2Fhttp-signatures-java","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomitribe%2Fhttp-signatures-java/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomitribe%2Fhttp-signatures-java/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomitribe%2Fhttp-signatures-java/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tomitribe","download_url":"https://codeload.github.com/tomitribe/http-signatures-java/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomitribe%2Fhttp-signatures-java/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28752668,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-25T10:25:12.305Z","status":"ssl_error","status_checked_at":"2026-01-25T10:25:11.933Z","response_time":113,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":[],"created_at":"2025-05-14T05:01:32.745Z","updated_at":"2026-01-25T11:40:13.879Z","avatar_url":"https://github.com/tomitribe.png","language":"Java","funding_links":[],"categories":["安全"],"sub_categories":[],"readme":"= HTTP Signatures Java Client\n:showtitle:\n\nHTTP signatures provide a mechanism by which a shared secret key can be used to digitally \"sign\" a HTTP message to both verify the\nidentity of the sender and verify that the message has not been tampered with in-transit.\n\nThis is done by concatenating the desired HTTP Headers together to form a string called a \"Signing String\".  An encrypted \nhash (digitial signature) is made of the Signing String using either an asymmetric algorithm (`rsa-sha256`) with a \npublic/private key pair or a symmetric algorithm (`hmac-sha256`) with a shared secret key.\n\nFinally, the encrypted bytes are Base64 encoded and added to the `Authorization` header, along with the `keyId`, signing algorithm, header names, and the signature itself.\n\nFor example, the following Signing String:\n\n[source]\n----\ndigest: SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=\ndate: Tue, 07 Jun 2014 20:51:35 GMT\n(request-target): get /foo/Bar\n----\n\nif encrypted using the `hmac-sha256` algorithm with the secret +don't tell+, and then encoded with Base64 would yield the result:\n\n[source]\n----\n6aq7lLvqJlYRhEBkvl0+qMuSbMyxalPICsBh1qV6V/s=\n----\n\nThis would then be applied to the Authorization header as below:\n\n[source]\n----\nAuthorization: Signature keyId=\"myusername:mykey\",algorithm=\"hmac-sha256\",headers=\"digest \ndate (request-target)\",signature=\"6aq7lLvqJlYRhEBkvl0+qMuSbMyxalPICsBh1qV6V/s=\"\n----\n\n== Maven Dependency\n\nAdd the following dependency to your Maven pom file.\n\n[source,xml]\n----\n\u003cdependency\u003e\n  \u003cgroupId\u003eorg.tomitribe\u003c/groupId\u003e\n  \u003cartifactId\u003etomitribe-http-signatures\u003c/artifactId\u003e\n  \u003cversion\u003e1.5\u003c/version\u003e\n\u003c/dependency\u003e\n----\n\n== Library Overview\n\nThere are 2 key classes in this library:\n\n* `Signature` - defines the headers that make up the signature - this must as a minimum include the headers that the server requires to be part of the signature\n* `Signer` - computes the signature value using the headers/values defined on the +Signature+ classes\n\n=== Creating a Signer\n\nThe `Signer` instance is intended to be created once and reused to sign all the HTTP messages that require HTTP Signature Authentication. \n\nIt is immutable, fully thread-safe and optimized to check and verify the supplied Key on construction.\n\nThe first steps to creating a Signer instance are as follows:\n\n[source,java]\n----\nfinal Signature signature = new Signature(\"key-alias\", \"hmac-sha256\", null, \"(request-target)\"); // \u003c1\u003e\nfinal Key key = new SecretKeySpec(passphrase.getBytes(), \"HmacSHA256\");\t // \u003c2\u003e\nfinal Signer signer = new Signer(key, signature); // \u003c3\u003e\n----\n\n\u003c1\u003e Define a new `Signature` object.  This `Signature` object isn't fully computed signature, but rather a template for the Signatures\nthat will be created by the `Signer` for specific HTTP messages.  It requires a key alias (1st parameter), the signature algorithm (2nd parameter - usually \"hmac-sha256\") and a var-arg list of header names indicating which headers will require signing (4th - nth parameters).\n\u003c2\u003e Define a SecretKeySpec instance, this needs the shared secret passphrase, and the algorithm used to store it in the keystore (usually \"HmacSHA256\").\n\u003c3\u003e Initialize a new Signer object with the key and signature from \u003c1\u003e and \u003c2\u003e above\n\n=== Signing an HTTP Message\n\nOnce you have a `Signer`, signing an HTTP Message is as simple as passing in the respective parts and letting the signer do the\nmagic. The library is not specific to any HTTP Client Java library such as Apache HttpClient, therefore data must be copied from\nthe request object of your specifc HTTP Client library and passed to the `Signer` in simple `String` and `Map` form.\n\n[source,java]\n----\nfinal String method = \"GET\";\n\nfinal String uri = \"/foo/Bar\";\n\nfinal Map\u003cString, String\u003e headers = new HashMap\u003cString, String\u003e();\nheaders.put(\"Host\", \"example.org\");\nheaders.put(\"Date\", \"Tue, 07 Jun 2014 20:51:35 GMT\");\nheaders.put(\"Content-Type\", \"application/json\");\nheaders.put(\"Digest\", \"SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=\");\nheaders.put(\"Accept\", \"*/*\");\nheaders.put(\"Content-Length\", \"18\");\n\n// Here it is!\nfinal Signature signed = signer.sign(method, uri, headers);\n----\n\nThe returned `Signature` object represents a full HTTP Signature.  Simply call `toString()` on it to get a fully formatted `Authorization` header value.\n\nCalling `toString()` on the above `Signature` instance will yeild the following:\n\n[source]\n----\nSignature keyId=\"my-key-name\",\"algorithm=\"hmac-sha256\",headers=\"content-length host date (request-target)\",signature=\"yT/NrPI9mKB5R7FTLRyFWvB+QLQOEAvbGmauC0tI+Jg=\"\n----\n\nThe output of +signature.toString()+ should be used as the value the +Authorization+ header for the request.\n\n== Scenarios\n\nThe following sections demonstrate a few common scenarios using http-signatures-java.\n\n=== Simple (request-target)\n\nThis is the simplest request. Only the request-target (URI) is used to build the signature.\n\n[source,java]\n----\nfinal Signature signature = new Signature(\"key-alias\", \"hmac-sha256\", null, \"(request-target)\"); // \u003c1\u003e\nfinal Key key = new SecretKeySpec(passphrase.getBytes(), \"HmacSHA256\");\nfinal Signer signer = new Signer(key, signature);\nfinal Map\u003cString, String\u003e headers = new HashMap\u003c\u003e();\nreturn signer.sign(method, uri, headers); // \u003c2\u003e\n----\n\n\u003c1\u003e Define the +Signature+ object using just the \"(request-target)\" (note the use of parenthesis) element.\n\u003c2\u003e Use the +Signer+ class with the method, URI, and an empty header map to create the signature.\n\n=== (request-target) date (with date validation)\n\nThis is similar to the the previous example, but expands on it by adding the date header to the signature. The date should be created in the \"EEE, dd MMM yyyy HH:mm:ss zzz\" format, and the exact same date should be passed to the +Signer+ as is used on the +Date+ header.\n\n[source,java]\n----\nfinal Date today = new Date(); // default window is 1 hour\nfinal String stringToday = new SimpleDateFormat(\"EEE, dd MMM yyyy HH:mm:ss zzz\", Locale.US).format(today);\n\nfinal Signature signature = new Signature(\"key-alias\", \"hmac-sha256\", null, \"(request-target)\", \"date\");  // \u003c1\u003e\nfinal Key key = new SecretKeySpec(passphrase.getBytes(), \"HmacSHA256\");\nfinal Signer signer = new Signer(key, signature);\nfinal Map\u003cString, String\u003e headers = new HashMap\u003c\u003e();\ns.put(\"Date\", stringToday);\t // \u003c2\u003e\nreturn signer.sign(method, uri, headers);\t\t\t\t\n----\n\n\u003c1\u003e Define the +Signature+ object with the \"(request-target)\" and \"date\" headers\n\u003c2\u003e Include the date in the headers map\n\n=== Message body digest\n\n[source,java]\n----\nfinal byte[] digest = MessageDigest.getInstance(\"SHA-256\").digest(payload.getBytes()); // \u003c1\u003e\nfinal String digestHeader = \"SHA-256=\" + new String(Base64.encodeBase64(digest));\n\nfinal Signature signature = new Signature(\"key-alias\", \"hmac-sha256\", null, \"(request-target)\", \"digest\"); // \u003c2\u003e\nfinal Key key = new SecretKeySpec(passphrase.getBytes(), \"HmacSHA256\");\nfinal Signer signer = new Signer(key, signature);\nfinal Map\u003cString, String\u003e headers = new HashMap\u003c\u003e();\nheaders.put(\"digest\", digestHeader);\nreturn signer.sign(method, uri, headers);\n----\n\n\u003c1\u003e Define the +Signature+ object with the \"(request-target)\" and \"digest\" headers\n\u003c2\u003e Include the digest in the headers map\n\n== References\n\nSigning HTTP Messages (Internet Draft) https://tools.ietf.org/html/draft-ietf-httpbis-message-signatures-00\n\nInstance Digests in HTTP http://tools.ietf.org/html/rfc3230\n\n= Signing Examples\n\n== Java 8\n\n[source,java]\n----\nimport javax.crypto.Mac;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.util.Base64;\n\npublic class SigningExample {\n\n    public static void main(String... s) throws Exception {\n\n        final String key = \"don't tell\";\n\n        final String signingString = \"digest: SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=\\n\" +\n                \"date: Tue, 07 Jun 2014 20:51:35 GMT\\n\" +\n                \"(request-target): get /foo/Bar\";\n\n        final Mac mac = Mac.getInstance(\"HmacSHA256\");\n        mac.init(new SecretKeySpec(key.getBytes(\"UTF-8\"), \"HmacSHA256\"));\n        final byte[] signedBytes = mac.doFinal(signingString.getBytes(\"UTF-8\"));\n        final Base64.Encoder encoder = Base64.getEncoder();\n\n        final String result = new String(encoder.encode(signedBytes), \"UTF-8\");\n\n        if (!\"6aq7lLvqJlYRhEBkvl0+qMuSbMyxalPICsBh1qV6V/s=\".equals(result)) {\n            throw new IllegalStateException(\"Signing failed\");\n        }\n\n        System.out.println(result);\n    }\n}\n----\n\n== Bash\n\n[source,java]\n----\n#!/bin/bash\n\n# Secret Key\nKEY=\"don't tell\"\n\n# Create the Signing string from the required headers\nSTRING='digest: SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=\ndate: Tue, 07 Jun 2014 20:51:35 GMT\n(request-target): get /foo/Bar'\n\n# Sign the string, base64\necho -n \"$STRING\" | openssl dgst -binary -sha256 -hmac \"$KEY\" | base64\n----\n\nRunning this will print:\n\n----\n6aq7lLvqJlYRhEBkvl0+qMuSbMyxalPICsBh1qV6V/s=\n----\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomitribe%2Fhttp-signatures-java","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftomitribe%2Fhttp-signatures-java","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomitribe%2Fhttp-signatures-java/lists"}