Skip to content

Commit c9f6f52

Browse files
committed
Updated build.
1 parent 5c35836 commit c9f6f52

File tree

15 files changed

+598
-123
lines changed

15 files changed

+598
-123
lines changed

bin/jsencrypt.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bin/jsencrypt.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/examples.md

Lines changed: 289 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ Real-world examples showing how to use JSEncrypt for common encryption scenarios
1919

2020
1. TOC
2121
{:toc}
22-
2322
---
2423

2524
## Basic Encryption/Decryption
@@ -89,6 +88,290 @@ fetch('/api/login', {
8988

9089
---
9190

91+
## SHA-256 Digital Signatures
92+
93+
### Using the Convenience Methods
94+
95+
JSEncrypt provides convenient `signSha256()` and `verifySha256()` methods that automatically handle SHA-256 hashing for you.
96+
97+
```javascript
98+
import { JSEncrypt } from 'jsencrypt';
99+
100+
// Initialize with your key pair
101+
const crypt = new JSEncrypt();
102+
crypt.setPrivateKey(privateKey);
103+
crypt.setPublicKey(publicKey);
104+
105+
// Simple message signing with SHA-256
106+
const message = "Important data to sign";
107+
const signature = crypt.signSha256(message);
108+
console.log('Signature:', signature);
109+
const isValid = crypt.verifySha256(message, signature);
110+
console.log('Signature valid:', isValid); // true
111+
```
112+
113+
### API Token Validation
114+
115+
```javascript
116+
class APITokenManager {
117+
constructor(privateKey, publicKey) {
118+
this.crypt = new JSEncrypt();
119+
this.privateKey = privateKey;
120+
this.publicKey = publicKey;
121+
}
122+
123+
// Generate a signed API token
124+
generateToken(userId, permissions, expiresIn = 3600) {
125+
const tokenData = {
126+
userId: userId,
127+
permissions: permissions,
128+
issuedAt: Math.floor(Date.now() / 1000),
129+
expiresAt: Math.floor(Date.now() / 1000) + expiresIn
130+
};
131+
132+
const payload = JSON.stringify(tokenData);
133+
this.crypt.setPrivateKey(this.privateKey);
134+
const signature = this.crypt.signSha256(payload);
135+
136+
// Return token as base64 encoded payload + signature
137+
const token = Buffer.from(JSON.stringify({
138+
payload: payload,
139+
signature: signature
140+
})).toString('base64');
141+
142+
return token;
143+
}
144+
145+
// Validate an API token
146+
validateToken(token) {
147+
try {
148+
// Decode the token
149+
const decoded = JSON.parse(Buffer.from(token, 'base64').toString());
150+
const { payload, signature } = decoded;
151+
152+
// Verify signature using convenience method
153+
this.crypt.setPublicKey(this.publicKey);
154+
const isValidSignature = this.crypt.verifySha256(payload, signature);
155+
156+
if (!isValidSignature) {
157+
return { valid: false, reason: 'Invalid signature' };
158+
}
159+
160+
// Check expiration
161+
const tokenData = JSON.parse(payload);
162+
const now = Math.floor(Date.now() / 1000);
163+
164+
if (tokenData.expiresAt < now) {
165+
return { valid: false, reason: 'Token expired' };
166+
}
167+
168+
return {
169+
valid: true,
170+
data: tokenData
171+
};
172+
} catch (error) {
173+
return { valid: false, reason: 'Invalid token format' };
174+
}
175+
}
176+
}
177+
178+
// Usage
179+
const tokenManager = new APITokenManager(privateKey, publicKey);
180+
181+
// Generate a token for user
182+
const token = tokenManager.generateToken(12345, ['read', 'write'], 7200); // 2 hours
183+
console.log('Generated token:', token);
184+
185+
// Later, validate the token
186+
const validation = tokenManager.validateToken(token);
187+
if (validation.valid) {
188+
console.log('Token valid for user:', validation.data.userId);
189+
console.log('Permissions:', validation.data.permissions);
190+
} else {
191+
console.log('Token invalid:', validation.reason);
192+
}
193+
```
194+
195+
### Message Integrity Verification
196+
197+
```javascript
198+
class MessageIntegrityChecker {
199+
constructor(privateKey, publicKey) {
200+
this.signer = new JSEncrypt();
201+
this.verifier = new JSEncrypt();
202+
this.signer.setPrivateKey(privateKey);
203+
this.verifier.setPublicKey(publicKey);
204+
}
205+
206+
// Create a signed message with metadata
207+
createSignedMessage(content, metadata = {}) {
208+
const messageData = {
209+
content: content,
210+
timestamp: Date.now(),
211+
metadata: metadata
212+
};
213+
214+
const messageString = JSON.stringify(messageData);
215+
const signature = this.signer.signSha256(messageString);
216+
217+
return {
218+
message: messageData,
219+
signature: signature
220+
};
221+
}
222+
223+
// Verify a signed message
224+
verifyMessage(signedMessage) {
225+
const { message, signature } = signedMessage;
226+
const messageString = JSON.stringify(message);
227+
228+
// Verify using SHA-256 convenience method
229+
const isValid = this.verifier.verifySha256(messageString, signature);
230+
231+
if (!isValid) {
232+
return { valid: false, reason: 'Signature verification failed' };
233+
}
234+
235+
return {
236+
valid: true,
237+
content: message.content,
238+
timestamp: new Date(message.timestamp),
239+
metadata: message.metadata
240+
};
241+
}
242+
}
243+
244+
// Usage Example
245+
const checker = new MessageIntegrityChecker(privateKey, publicKey);
246+
247+
// Create a signed message
248+
const signedMessage = checker.createSignedMessage(
249+
"This is a secure message",
250+
{ sender: "Alice", priority: "high" }
251+
);
252+
253+
console.log('Signed message:', signedMessage);
254+
255+
// Verify the message
256+
const verification = checker.verifyMessage(signedMessage);
257+
if (verification.valid) {
258+
console.log('Message verified:', verification.content);
259+
console.log('Sent by:', verification.metadata.sender);
260+
} else {
261+
console.log('Verification failed:', verification.reason);
262+
}
263+
```
264+
265+
### Simple JWT Implementation
266+
267+
Here's a straightforward JWT implementation using the `signSha256` and `verifySha256` methods:
268+
269+
```javascript
270+
class SimpleJWT {
271+
constructor(privateKey, publicKey) {
272+
this.signer = new JSEncrypt();
273+
this.verifier = new JSEncrypt();
274+
this.signer.setPrivateKey(privateKey);
275+
this.verifier.setPublicKey(publicKey);
276+
}
277+
278+
// Create a simple JWT
279+
createToken(payload, expiresInSeconds = 3600) {
280+
const header = {
281+
alg: 'RS256',
282+
typ: 'JWT'
283+
};
284+
285+
const claims = {
286+
...payload,
287+
iat: Math.floor(Date.now() / 1000),
288+
exp: Math.floor(Date.now() / 1000) + expiresInSeconds
289+
};
290+
291+
// Encode header and payload
292+
const encodedHeader = this.base64UrlEncode(JSON.stringify(header));
293+
const encodedPayload = this.base64UrlEncode(JSON.stringify(claims));
294+
295+
// Create signing input
296+
const signingInput = `${encodedHeader}.${encodedPayload}`;
297+
const signature = this.signer.signSha256(signingInput);
298+
const encodedSignature = this.base64UrlEncode(signature);
299+
300+
return `${signingInput}.${encodedSignature}`;
301+
}
302+
303+
// Verify and decode a JWT
304+
verifyToken(token) {
305+
const parts = token.split('.');
306+
if (parts.length !== 3) {
307+
throw new Error('Invalid JWT format');
308+
}
309+
310+
const [encodedHeader, encodedPayload, encodedSignature] = parts;
311+
const signingInput = `${encodedHeader}.${encodedPayload}`;
312+
313+
// Decode signature and verify using convenience method
314+
const signature = this.base64UrlDecode(encodedSignature);
315+
const isValid = this.verifier.verifySha256(signingInput, signature);
316+
317+
if (!isValid) {
318+
throw new Error('Invalid signature');
319+
}
320+
321+
// Decode and check expiration
322+
const payload = JSON.parse(this.base64UrlDecode(encodedPayload));
323+
const now = Math.floor(Date.now() / 1000);
324+
325+
if (payload.exp && payload.exp < now) {
326+
throw new Error('Token expired');
327+
}
328+
329+
return payload;
330+
}
331+
332+
// Base64 URL encoding/decoding utilities
333+
base64UrlEncode(str) {
334+
return Buffer.from(str, 'utf8')
335+
.toString('base64')
336+
.replace(/\+/g, '-')
337+
.replace(/\//g, '_')
338+
.replace(/=/g, '');
339+
}
340+
341+
base64UrlDecode(str) {
342+
// Add padding
343+
str += '='.repeat((4 - str.length % 4) % 4);
344+
// Convert back from URL-safe base64
345+
const standardBase64 = str.replace(/-/g, '+').replace(/_/g, '/');
346+
return Buffer.from(standardBase64, 'base64').toString('utf8');
347+
}
348+
}
349+
350+
// Usage Example
351+
const jwt = new SimpleJWT(privateKey, publicKey);
352+
353+
// Create a token
354+
const token = jwt.createToken({
355+
userId: 12345,
356+
username: 'john.doe',
357+
role: 'admin'
358+
}, 7200); // 2 hours
359+
360+
console.log('JWT Token:', token);
361+
362+
// Verify the token
363+
try {
364+
const decoded = jwt.verifyToken(token);
365+
console.log('Token valid! User:', decoded.username);
366+
console.log('Role:', decoded.role);
367+
console.log('Expires:', new Date(decoded.exp * 1000));
368+
} catch (error) {
369+
console.error('Token verification failed:', error.message);
370+
}
371+
```
372+
373+
---
374+
92375
## JWT Token Signing with RSA
93376

94377
### Creating JWT Tokens with RSA Signatures
@@ -160,16 +443,14 @@ class RSAJWTManager {
160443

161444
signWithRSA(data) {
162445
this.crypt.setPrivateKey(this.privateKey);
163-
const hash = crypto.createHash('sha256').update(data).digest();
164-
const signature = this.crypt.sign(hash.toString('base64'), 'sha256', 'base64');
446+
const signature = this.crypt.signSha256(data);
165447
return this.base64UrlEncode(signature);
166448
}
167449

168450
verifyRSASignature(data, signature) {
169451
this.crypt.setPublicKey(this.publicKey);
170-
const hash = crypto.createHash('sha256').update(data).digest();
171452
const decodedSignature = this.base64UrlDecode(signature);
172-
return this.crypt.verify(hash.toString('base64'), decodedSignature, 'sha256');
453+
return this.crypt.verifySha256(data, decodedSignature);
173454
}
174455

175456
base64UrlEncode(str) {
@@ -355,7 +636,7 @@ class DocumentSigner {
355636

356637
// Sign the payload
357638
const payloadString = JSON.stringify(signaturePayload);
358-
const signature = this.crypt.sign(payloadString, 'sha256', 'base64');
639+
const signature = this.crypt.signSha256(payloadString);
359640

360641
return {
361642
document: documentContent,
@@ -382,7 +663,8 @@ class DocumentSigner {
382663

383664
// Verify signature
384665
const payloadString = JSON.stringify(signaturePayload);
385-
const isValidSignature = this.crypt.verify(payloadString, signature, 'sha256');
666+
// Use the new verifySha256 convenience method
667+
const isValidSignature = this.crypt.verifySha256(payloadString, signature);
386668

387669
if (!isValidSignature) {
388670
return { valid: false, reason: 'Invalid signature' };

lib/JSEncrypt.d.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,13 @@ export declare class JSEncrypt {
7878
* @return {string} the signature encoded in base64
7979
* @public
8080
*/
81-
sign(str: string, digestMethod: (str: string) => string, digestName: string): string | false;
81+
sign(str: string, digestMethod?: (str: string) => string, digestName?: string): string | false;
82+
/**
83+
* Signs a string using the SHA-256 hash algorithm.
84+
* @param str the string to sign
85+
* @returns the base64 encoded signature or false on failure
86+
*/
87+
signSha256(str: string): string | false;
8288
/**
8389
* Proxy method for RSAKey object's verify.
8490
* @param {string} str the string to verify
@@ -87,7 +93,14 @@ export declare class JSEncrypt {
8793
* @return {boolean} whether the data and signature match
8894
* @public
8995
*/
90-
verify(str: string, signature: string, digestMethod: (str: string) => string): boolean;
96+
verify(str: string, signature: string, digestMethod?: (str: string) => string): boolean;
97+
/**
98+
* Verifies a string using the SHA-256 hash algorithm.
99+
* @param str the string to verify
100+
* @param signature the base64 encoded signature to compare against
101+
* @returns whether the signature is valid
102+
*/
103+
verifySha256(str: string, signature: string): boolean;
91104
/**
92105
* Getter for the current JSEncryptRSAKey object. If it doesn't exists a new object
93106
* will be created and returned

0 commit comments

Comments
 (0)