Rakshitjan commited on
Commit
3b5eda5
·
verified ·
1 Parent(s): ef4f573

Create crypto_utils_b2c.js

Browse files
Files changed (1) hide show
  1. crypto_utils_b2c.js +204 -0
crypto_utils_b2c.js ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const crypto = require("crypto");
2
+
3
+ /**
4
+ * crypto_utils.js
5
+ * Hybrid Encryption: AES-256-GCM + RSA-OAEP + SHA256withRSA
6
+ *
7
+ * Functions Exposed:
8
+ * encryptString(text)
9
+ * decryptString(encryptedText)
10
+ *
11
+ * Required ENV variables:
12
+ * PRIVATE_KEY (PKCS8 private key)
13
+ * PUBLIC_CERT (X.509 certificate)
14
+ */
15
+
16
+ /* -------------------------------------------------------
17
+ PEM NORMALIZATION HELPERS (for raw Base64 input)
18
+ -------------------------------------------------------- */
19
+
20
+ function wrapPrivateKey(key) {
21
+ if (!key) throw new Error("Missing PRIVATE_KEY");
22
+
23
+ if (key.includes("BEGIN")) return key; // Already PEM
24
+
25
+ const clean = key.replace(/\s+/g, "");
26
+ return `-----BEGIN PRIVATE KEY-----
27
+ ${clean.match(/.{1,64}/g).join("\n")}
28
+ -----END PRIVATE KEY-----`;
29
+ }
30
+
31
+ function wrapCertificate(cert) {
32
+ if (!cert) throw new Error("Missing PUBLIC_CERT");
33
+
34
+ if (cert.includes("BEGIN")) return cert; // Already PEM
35
+
36
+ const clean = cert.replace(/\s+/g, "");
37
+ return `-----BEGIN CERTIFICATE-----
38
+ ${clean.match(/.{1,64}/g).join("\n")}
39
+ -----END CERTIFICATE-----`;
40
+ }
41
+
42
+ /* -------------------------------------------------------
43
+ LOAD KEYS
44
+ -------------------------------------------------------- */
45
+ const PRIVATE_KEY = wrapPrivateKey(process.env.PRIVATE_KEY);
46
+ const PUBLIC_CERT = wrapCertificate(process.env.PUBLIC_CERT);
47
+
48
+ /* -------------------------------------------------------
49
+ AES-256-GCM IMPLEMENTATION
50
+ -------------------------------------------------------- */
51
+
52
+ function aesEncrypt(plainText, key, iv) {
53
+ const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
54
+
55
+ const encrypted = Buffer.concat([
56
+ cipher.update(plainText, "utf8"),
57
+ cipher.final()
58
+ ]);
59
+
60
+ const tag = cipher.getAuthTag();
61
+
62
+ return Buffer.concat([encrypted, tag]).toString("base64");
63
+ }
64
+
65
+ function aesDecrypt(cipherBase64, key, iv) {
66
+ const buffer = Buffer.from(cipherBase64, "base64");
67
+
68
+ const tag = buffer.slice(buffer.length - 16);
69
+ const ciphertext = buffer.slice(0, buffer.length - 16);
70
+
71
+ const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
72
+ decipher.setAuthTag(tag);
73
+
74
+ const decrypted = Buffer.concat([
75
+ decipher.update(ciphertext),
76
+ decipher.final()
77
+ ]);
78
+
79
+ return decrypted.toString("utf8");
80
+ }
81
+
82
+ /* -------------------------------------------------------
83
+ RSA HELPERS
84
+ -------------------------------------------------------- */
85
+
86
+ function rsaEncryptBase64(str) {
87
+ return crypto
88
+ .publicEncrypt(
89
+ {
90
+ key: PUBLIC_CERT,
91
+ padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
92
+ },
93
+ Buffer.from(str, "utf8")
94
+ )
95
+ .toString("base64");
96
+ }
97
+
98
+ function rsaDecryptToString(b64) {
99
+ return crypto
100
+ .privateDecrypt(
101
+ {
102
+ key: PRIVATE_KEY,
103
+ padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
104
+ },
105
+ Buffer.from(b64, "base64")
106
+ )
107
+ .toString("utf8");
108
+ }
109
+
110
+ /* -------------------------------------------------------
111
+ SIGN / VERIFY HELPERS
112
+ -------------------------------------------------------- */
113
+
114
+ function signData(data) {
115
+ const sign = crypto.createSign("RSA-SHA256");
116
+ sign.update(data);
117
+ sign.end();
118
+ return sign.sign(PRIVATE_KEY).toString("base64");
119
+ }
120
+
121
+ function verifyData(data, signatureB64) {
122
+ const verify = crypto.createVerify("RSA-SHA256");
123
+ verify.update(data);
124
+ verify.end();
125
+ return verify.verify(PUBLIC_CERT, Buffer.from(signatureB64, "base64"));
126
+ }
127
+
128
+ /* -------------------------------------------------------
129
+ PUBLIC FUNCTIONS
130
+ -------------------------------------------------------- */
131
+
132
+ /**
133
+ * Encrypt a plain string using:
134
+ * AES-256-GCM + RSA-OAEP + SHA256withRSA
135
+ */
136
+ function encryptStringB2C(text) {
137
+ try {
138
+ const aesKey = crypto.randomBytes(32); // 256-bit
139
+ const iv = crypto.randomBytes(16); // 128-bit
140
+
141
+ // AES encrypt
142
+ const aesCipher = aesEncrypt(text, aesKey, iv);
143
+
144
+ // Signature over AES ciphertext (Base64)
145
+ const signature = signData(aesCipher);
146
+
147
+ // RSA encrypted AES key
148
+ const encryptedKey = rsaEncryptBase64(aesKey.toString("base64"));
149
+
150
+ // Assemble: header : iv : cipher : signature
151
+ const payload = [
152
+ encryptedKey,
153
+ iv.toString("base64"),
154
+ aesCipher,
155
+ signature
156
+ ].join(":");
157
+
158
+ // Final base64 wrapper
159
+ return Buffer.from(payload, "utf8").toString("base64");
160
+ } catch (err) {
161
+ console.error("Encryption error:", err.message);
162
+ throw new Error("Failed to encrypt data");
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Decrypt the hybrid AES/RSA encrypted Base64 payload
168
+ */
169
+ function decryptStringB2C(encryptedText) {
170
+ try {
171
+ const decoded = Buffer.from(encryptedText, "base64").toString("utf8");
172
+
173
+ const parts = decoded.split(":");
174
+ if (parts.length < 4) throw new Error("Invalid encrypted payload");
175
+
176
+ const [encryptedKey, iv64, cipher64, signature64] = parts;
177
+
178
+ // RSA decrypt AES key
179
+ const aesKeyBase64 = rsaDecryptToString(encryptedKey);
180
+ const aesKey = Buffer.from(aesKeyBase64, "base64");
181
+
182
+ // Verify signature
183
+ if (!verifyData(cipher64, signature64)) {
184
+ throw new Error("Signature verification failed");
185
+ }
186
+
187
+ const iv = Buffer.from(iv64, "base64");
188
+
189
+ // AES decrypt
190
+ return aesDecrypt(cipher64, aesKey, iv);
191
+ } catch (err) {
192
+ console.error("Decryption error:", err.message);
193
+ throw new Error("Failed to decrypt data");
194
+ }
195
+ }
196
+
197
+ /* -------------------------------------------------------
198
+ EXPORTS
199
+ -------------------------------------------------------- */
200
+
201
+ module.exports = {
202
+ encryptStringB2C,
203
+ decryptStringB2C
204
+ };