Understanding the HS512 Algorithm
HS512 is one of three HMAC (Hash-based Message Authentication Code) algorithms defined in RFC 7518 for JSON Web Tokens. All HMAC variants use a shared symmetric secret and differ only in the underlying hash function: HS256 uses SHA-256, HS384 uses SHA-384, and HS512 uses SHA-512. The number after HS refers to the hash's output size in bits.
When an issuer signs a JWT with HS512, it takes the base64url-encoded header and payload joined by a dot, then runs HMAC-SHA-512 over that string with the shared secret. The 64-byte (512-bit) output is base64url-encoded and appended as the third segment. Verifiers reverse the process: they recompute the HMAC and byte-compare it against the segment.
When to Choose HS512 Over HS256 or HS384
For most applications, HS256 is the right default — it has strong security, small tokens, and universal library support. Reasons to specifically choose HS512 include:
- Compliance mandates — FIPS 140-3 and some government/financial standards prefer SHA-384/512 for long-term data protection.
- Long-lived tokens — refresh tokens or offline tokens that must remain valid for years benefit from the larger security margin against future cryptanalytic advances.
- Very large secrets — if you naturally have 64+ byte secrets (from an HSM, a KMS derivation, or a shared master key), matching the algorithm to the key size makes cryptographic sense.
- Defence in depth — some architectures want the maximum available symmetric strength "because we can," and HS512 is a reasonable choice when token size is not a concern.
The Structure of an HS512 JWT
An HS512 JWT has the same three-segment structure as any JWT:
eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9 <- header
.
eyJzdWIiOiIxMjM0NSIsIm5hbWUiOiJKYW5lIn0 <- payload
.
5jP...86chars_of_base64url_for_64_bytes <- signatureThe header decodes to {"alg":"HS512","typ":"JWT"}. The signature segment is longer than HS256 (86 characters vs 43) because SHA-512 produces 64 bytes of output vs 32 bytes for SHA-256.
Signing and Verifying HS512 in Every Major Language
Node.js — jose
import { SignJWT, jwtVerify } from 'jose';
const secret = new TextEncoder().encode(process.env.JWT_SECRET); // 64+ bytes
// Sign
const jwt = await new SignJWT({ sub: '12345', role: 'admin' })
.setProtectedHeader({ alg: 'HS512' })
.setIssuedAt()
.setExpirationTime('1h')
.sign(secret);
// Verify
const { payload } = await jwtVerify(jwt, secret, {
algorithms: ['HS512'], // pin the algorithm
});Python — PyJWT
import jwt
import secrets
SECRET = secrets.token_urlsafe(64) # 64-byte random secret
# Sign
token = jwt.encode(
{"sub": "12345", "role": "admin"},
SECRET,
algorithm="HS512",
)
# Verify
payload = jwt.decode(
token,
SECRET,
algorithms=["HS512"], # pin the algorithm
)Go — golang-jwt/jwt
import "github.com/golang-jwt/jwt/v5"
// Sign
token := jwt.NewWithClaims(jwt.SigningMethodHS512, jwt.MapClaims{
"sub": "12345",
"role": "admin",
"exp": time.Now().Add(time.Hour).Unix(),
})
tokenString, err := token.SignedString(secret)
// Verify
parsed, err := jwt.Parse(tokenString, func(t *jwt.Token) (any, error) {
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected alg: %v", t.Header["alg"])
}
// ADDITIONALLY confirm it is HS512 specifically:
if t.Method.Alg() != "HS512" {
return nil, fmt.Errorf("expected HS512, got %s", t.Method.Alg())
}
return secret, nil
})Java — jjwt
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
// Generate a suitable 64-byte key for HS512
SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS512);
// Sign
String jws = Jwts.builder()
.setSubject("12345")
.signWith(key, SignatureAlgorithm.HS512)
.compact();
// Verify
Claims claims = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(jws)
.getBody();Common HS512 Pitfalls
- Using a short secret. A 16-character password with HS512 is not more secure than the same password with HS256 — HMAC pads the key. Use 64+ bytes of random entropy.
- Accepting multiple algorithms. If your verify call accepts both HS512 and HS256, an attacker can substitute a token signed with the weaker one. Always pin the exact algorithm.
- Trusting the alg header. Never let the JWT itself dictate the verification algorithm — pin it in your code. The
algorithms: ["HS512"]option in every library enforces this. - Storing the secret in the codebase. HS512 secrets belong in environment variables or a secrets manager (Vault, AWS Secrets Manager, GCP Secret Manager). Rotate every 90 days.
- Confusing HS512 with RS512. RS512 uses RSA + SHA-512 (asymmetric); HS512 uses HMAC + SHA-512 (symmetric). Different key setup, different verification path.
Generating a Strong HS512 Secret
Generate a fresh 64-byte random secret before your first deploy:
# Shell
openssl rand -base64 64
# Python
python -c "import secrets; print(secrets.token_urlsafe(64))"
# Node
node -e "console.log(require('crypto').randomBytes(64).toString('base64'))"
# Go
go run -e 'package main; import ("crypto/rand"; "encoding/base64"; "fmt"); func main(){b:=make([]byte,64); rand.Read(b); fmt.Println(base64.StdEncoding.EncodeToString(b))}'Store the output in your secrets manager. Never commit it. If it leaks, rotate immediately — during rotation, accept both old and new secrets in the verifier for the token lifetime, then drop the old one.
Related JWT Tools
- JWT HS256 Decoder — the default HMAC variant
- JWT RS256 Decoder — asymmetric RSA-signed JWTs
- Verify JWT Signature — universal signature verifier
- JWT Decoder Online — decode any JWT algorithm
- Hash Generator — compute SHA-512 and other hashes locally