or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

errors.mdindex.mdinterfaces.mdjwa.mdjwk.mdjws.mdutilities.md

jws.mddocs/

0

# JSON Web Signature (JWS)

1

2

Complete signing and verification workflow implementing the JSON Web Signature specification (RFC 7515). Provides comprehensive support for creating, serializing, and verifying digitally signed JSON messages with customizable headers and multiple serialization formats.

3

4

## Capabilities

5

6

### JWS Class

7

8

Main class for creating and managing JSON Web Signatures with support for signing, verification, and serialization.

9

10

```python { .api }

11

class JWS:

12

"""JSON Web Signature implementation"""

13

14

@classmethod

15

def sign(cls, payload: bytes, **kwargs) -> 'JWS':

16

"""

17

Create a new JWS by signing payload. The key and algorithm are passed via kwargs.

18

19

Parameters:

20

- payload: Message bytes to sign

21

- **kwargs: Signing parameters including 'key' (JWK), 'alg' (JWASignature),

22

and other header fields

23

24

Returns:

25

JWS: New JWS instance with signature

26

"""

27

28

def verify(self, key: Optional[JWK] = None) -> bool:

29

"""

30

Verify JWS signature.

31

32

Parameters:

33

- key: JWK instance containing verification key (optional if key in header)

34

35

Returns:

36

bool: True if signature is valid, False otherwise

37

38

Raises:

39

josepy.errors.Error: If signature verification fails

40

"""

41

42

def json_dumps(self, **kwargs) -> str:

43

"""Serialize JWS to JSON string"""

44

45

@classmethod

46

def json_loads(cls, json_string: str) -> 'JWS':

47

"""Deserialize JWS from JSON string"""

48

49

def to_compact(self) -> bytes:

50

"""

51

Serialize JWS to compact format.

52

53

Returns:

54

bytes: Compact JWS serialization (header.payload.signature)

55

56

Raises:

57

AssertionError: If JWS doesn't have exactly one signature or algorithm not in protected header

58

"""

59

60

@classmethod

61

def from_compact(cls, compact: bytes) -> 'JWS':

62

"""

63

Deserialize JWS from compact format.

64

65

Parameters:

66

- compact: Compact JWS serialization bytes

67

68

Returns:

69

JWS: Deserialized JWS instance

70

71

Raises:

72

josepy.errors.DeserializationError: If compact format is invalid

73

"""

74

75

@property

76

def signature(self) -> 'Signature':

77

"""

78

Get singleton signature component.

79

80

Returns:

81

Signature: The single signature component

82

83

Raises:

84

AssertionError: If JWS doesn't have exactly one signature

85

"""

86

87

# JWS components

88

payload: bytes # Message payload

89

signatures: List['Signature'] # List of signature components

90

```

91

92

#### Usage Examples

93

94

```python

95

from josepy import JWS, Header, JWKRSA, RS256

96

from cryptography.hazmat.primitives.asymmetric import rsa

97

from cryptography.hazmat.backends import default_backend

98

99

# Generate RSA key pair

100

private_key = rsa.generate_private_key(65537, 2048, default_backend())

101

jwk = JWKRSA(key=private_key)

102

public_jwk = jwk.public_key()

103

104

# Create and sign JWS

105

payload = b'{"user": "alice", "scope": "read:profile"}'

106

jws = JWS.sign(payload, key=jwk, alg=RS256)

107

108

# Serialize to JSON

109

jws_json = jws.json_dumps()

110

print(f"JWS JSON: {jws_json}")

111

112

# Deserialize and verify

113

loaded_jws = JWS.json_loads(jws_json)

114

verified_payload = loaded_jws.verify(public_jwk)

115

print(f"Verified payload: {verified_payload.decode()}")

116

117

# Sign with custom headers

118

custom_jws = JWS.sign(

119

payload,

120

key=jwk,

121

alg=RS256,

122

kid="key-1", # Key ID

123

typ="JWT" # Token type

124

)

125

```

126

127

### Header Class

128

129

JOSE header management supporting registered header parameters with type-safe field handling.

130

131

```python { .api }

132

class Header:

133

"""JOSE Header for JWS"""

134

135

def __init__(self, alg=None, **kwargs):

136

"""

137

Initialize JOSE header.

138

139

Parameters:

140

- alg: Signature algorithm (JWASignature instance)

141

- **kwargs: Additional header parameters

142

"""

143

144

def not_omitted(self) -> Dict[str, Any]:

145

"""Get header fields that would not be omitted in JSON"""

146

147

@classmethod

148

def from_json(cls, jobj: Any) -> 'Header':

149

"""Deserialize header from JSON object"""

150

151

def to_partial_json(self) -> Any:

152

"""Serialize header to JSON-compatible dictionary"""

153

154

# Standard header fields

155

alg: Optional[JWASignature] # Algorithm

156

jku: Optional[bytes] # JWK Set URL

157

jwk: Optional[JWK] # JSON Web Key

158

kid: Optional[str] # Key ID

159

x5u: Optional[bytes] # X.509 URL

160

x5c: Tuple[x509.Certificate, ...] # X.509 Certificate Chain

161

x5t: Optional[bytes] # X.509 Certificate SHA-1 Thumbprint

162

x5tS256: Optional[bytes] # X.509 Certificate SHA-256 Thumbprint

163

typ: Optional[MediaType] # Type (Media Type)

164

cty: Optional[MediaType] # Content Type

165

crit: Tuple[Any, ...] # Critical extensions

166

```

167

168

#### Usage Examples

169

170

```python

171

from josepy import Header, RS256, JWKRSA

172

from cryptography import x509

173

174

# Basic header with algorithm

175

header = Header(alg=RS256)

176

177

# Header with key ID

178

header_with_kid = Header(alg=RS256, kid="rsa-key-1")

179

180

# Header with embedded JWK

181

jwk = JWKRSA(key=private_key)

182

header_with_jwk = Header(alg=RS256, jwk=jwk.public_key())

183

184

# Header with X.509 certificate chain

185

# (assuming you have certificates)

186

header_with_x5c = Header(

187

alg=RS256,

188

x5c=(cert1, cert2), # x509.Certificate objects

189

x5t=cert_thumbprint # SHA-1 thumbprint bytes

190

)

191

192

# Custom header fields

193

header_custom = Header(

194

alg=RS256,

195

typ="JWT",

196

custom_field="custom_value" # Non-standard fields supported

197

)

198

199

# Serialize header

200

header_json = header.json_dumps()

201

print(f"Header JSON: {header_json}")

202

```

203

204

### Signature Class

205

206

Individual signature component supporting JWS signature operations.

207

208

```python { .api }

209

class Signature:

210

"""JWS Signature component"""

211

212

def __init__(self, signature: bytes, header: Header):

213

"""

214

Initialize signature component.

215

216

Parameters:

217

- signature: Raw signature bytes

218

- header: JOSE header for this signature

219

"""

220

221

@classmethod

222

def from_json(cls, jobj: Any) -> 'Signature':

223

"""Deserialize signature from JSON object"""

224

225

def to_partial_json(self) -> Any:

226

"""Serialize signature to JSON-compatible dictionary"""

227

228

signature: bytes # Raw signature bytes

229

header: Header # JOSE header

230

```

231

232

### Media Type Handling

233

234

Utility class for handling JOSE media type encoding and decoding.

235

236

```python { .api }

237

class MediaType:

238

"""Media Type field encoder/decoder"""

239

240

PREFIX = "application/" # MIME prefix

241

242

@classmethod

243

def decode(cls, value: str) -> str:

244

"""

245

Decode media type from JOSE format.

246

Adds 'application/' prefix if not present.

247

"""

248

249

@classmethod

250

def encode(cls, value: str) -> str:

251

"""

252

Encode media type to JOSE format.

253

Removes 'application/' prefix if present.

254

"""

255

```

256

257

## Complete Workflow Examples

258

259

### JWT-style Token Creation

260

261

```python

262

from josepy import JWS, Header, JWKRSA, RS256

263

import json

264

import time

265

266

# Create JWT payload

267

payload_dict = {

268

"sub": "1234567890",

269

"name": "John Doe",

270

"iat": int(time.time()),

271

"exp": int(time.time()) + 3600 # 1 hour expiration

272

}

273

payload_json = json.dumps(payload_dict).encode()

274

275

# Sign with RS256

276

private_key = rsa.generate_private_key(65537, 2048, default_backend())

277

jwk = JWKRSA(key=private_key)

278

279

jws = JWS.sign(

280

payload_json,

281

key=jwk,

282

alg=RS256,

283

typ="JWT",

284

kid="key-1"

285

)

286

287

# The result is a complete JWS that can be transmitted

288

token = jws.json_dumps()

289

```

290

291

### Multi-Step Verification Process

292

293

```python

294

# Receive JWS token (e.g., from HTTP request)

295

received_token = '{"header": {...}, "payload": "...", "signature": "..."}'

296

297

try:

298

# Parse JWS

299

jws = JWS.json_loads(received_token)

300

301

# Extract key ID from header

302

key_id = jws.header.kid

303

304

# Look up verification key (application-specific)

305

verification_jwk = get_public_key_by_id(key_id) # Your function

306

307

# Verify signature and get payload

308

verified_payload = jws.verify(verification_jwk)

309

310

# Parse payload as needed

311

payload_dict = json.loads(verified_payload.decode())

312

313

# Additional validation (expiration, audience, etc.)

314

if payload_dict.get('exp', 0) < time.time():

315

raise ValueError("Token expired")

316

317

print(f"Valid token for user: {payload_dict.get('sub')}")

318

319

except Exception as e:

320

print(f"Token verification failed: {e}")

321

```

322

323

### Multiple Algorithm Support

324

325

```python

326

from josepy import RS256, ES256, HS256, JWKRSA, JWKEC, JWKOct

327

328

# Create keys for different algorithms

329

rsa_key = rsa.generate_private_key(65537, 2048, default_backend())

330

ec_key = ec.generate_private_key(ec.SECP256R1(), default_backend())

331

hmac_key = os.urandom(32)

332

333

# Create JWKs

334

rsa_jwk = JWKRSA(key=rsa_key)

335

ec_jwk = JWKEC(key=ec_key)

336

hmac_jwk = JWKOct(key=hmac_key)

337

338

payload = b'{"message": "Hello, multi-algorithm world!"}'

339

340

# Sign with different algorithms

341

rsa_jws = JWS.sign(payload, key=rsa_jwk, alg=RS256)

342

ec_jws = JWS.sign(payload, key=ec_jwk, alg=ES256)

343

hmac_jws = JWS.sign(payload, key=hmac_jwk, alg=HS256)

344

345

# Verify with corresponding keys

346

rsa_verified = rsa_jws.verify(rsa_jwk.public_key())

347

ec_verified = ec_jws.verify(ec_jwk.public_key())

348

hmac_verified = hmac_jws.verify(hmac_jwk) # Symmetric key

349

350

print("All signatures verified successfully!")

351

```

352

353

## Command Line Interface

354

355

JOSEPY provides a comprehensive command-line tool for JWS operations through the `jws` command:

356

357

```python { .api }

358

class CLI:

359

"""JWS Command Line Interface"""

360

361

@classmethod

362

def sign(cls, args: argparse.Namespace) -> None:

363

"""Execute JWS signing operation from command line arguments"""

364

365

@classmethod

366

def verify(cls, args: argparse.Namespace) -> bool:

367

"""Execute JWS verification operation from command line arguments"""

368

369

@classmethod

370

def run(cls, args: Optional[List[str]] = None) -> Optional[bool]:

371

"""Parse command line arguments and execute sign/verify operations"""

372

```

373

374

### CLI Usage Examples

375

376

**Basic Signing:**

377

```bash

378

# Sign payload from stdin with RSA key

379

echo "Hello, World!" | jws sign --key private_key.pem --alg RS256

380

381

# Sign with compact serialization

382

echo "Hello, World!" | jws --compact sign --key private_key.pem --alg RS256

383

384

# Sign with protected headers

385

echo "Hello, World!" | jws sign --key private_key.pem --alg RS256 --protect alg --protect kid

386

```

387

388

**Verification:**

389

```bash

390

# Verify JWS token from stdin using key

391

echo '{"header": {...}, "payload": "...", "signature": "..."}' | jws verify --key public_key.pem --kty RSA

392

393

# Verify compact JWS

394

echo "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...." | jws --compact verify --key public_key.pem --kty RSA

395

396

# Verify with embedded key (no --key needed)

397

echo '{"header": {"jwk": {...}}, ...}' | jws verify

398

```

399

400

**Available Options:**

401

- `--compact`: Use compact serialization format

402

- `--key`: Path to key file (PEM/DER format)

403

- `--alg`: Signature algorithm (RS256, ES256, HS256, etc.)

404

- `--protect`: Header parameters to include in protected header

405

- `--kty`: Key type for verification (RSA, EC, oct)

406

407

## Error Handling

408

409

```python

410

from josepy.errors import Error, DeserializationError

411

412

try:

413

# Invalid JWS structure

414

jws = JWS.json_loads('{"invalid": "structure"}')

415

except DeserializationError as e:

416

print(f"JWS parsing failed: {e}")

417

418

try:

419

# Signature verification failure

420

payload = jws.verify(wrong_key)

421

except Error as e:

422

print(f"Signature verification failed: {e}")

423

424

# Check header for critical extensions

425

if jws.header.crit:

426

print("Warning: JWS contains critical extensions")

427

# Handle or reject based on your security policy

428

```