typepki-strconv 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +22 -0
  3. package/dist/import/conv.d.mts +193 -0
  4. package/dist/import/conv.d.mts.map +1 -0
  5. package/dist/import/conv_arybuf.d.mts +30 -0
  6. package/dist/import/conv_arybuf.d.mts.map +1 -0
  7. package/dist/import/conv_ba.d.mts +27 -0
  8. package/dist/import/conv_ba.d.mts.map +1 -0
  9. package/dist/import/conv_ip.d.mts +69 -0
  10. package/dist/import/conv_ip.d.mts.map +1 -0
  11. package/dist/import/conv_iso88591.d.mts +19 -0
  12. package/dist/import/conv_iso88591.d.mts.map +1 -0
  13. package/dist/import/conv_pem.d.mts +109 -0
  14. package/dist/import/conv_pem.d.mts.map +1 -0
  15. package/dist/import/conv_ucs2.d.mts +14 -0
  16. package/dist/import/conv_ucs2.d.mts.map +1 -0
  17. package/dist/import/conv_uricmp.d.mts +29 -0
  18. package/dist/import/conv_uricmp.d.mts.map +1 -0
  19. package/dist/import/conv_zulu.d.mts +120 -0
  20. package/dist/import/conv_zulu.d.mts.map +1 -0
  21. package/dist/import/index.d.mts +22 -0
  22. package/dist/import/index.d.mts.map +1 -0
  23. package/dist/import/index.mjs +4 -0
  24. package/dist/require/index.cjs +4 -0
  25. package/dist/require/index.d.cts +2 -0
  26. package/dist/require/index.d.cts.map +1 -0
  27. package/package.json +72 -0
  28. package/src/conv.mts +367 -0
  29. package/src/conv.test.mts +99 -0
  30. package/src/conv_arybuf.mts +53 -0
  31. package/src/conv_arybuf.test.mts +16 -0
  32. package/src/conv_ba.mts +52 -0
  33. package/src/conv_ip.mts +226 -0
  34. package/src/conv_iso88591.mts +63 -0
  35. package/src/conv_pem.mts +160 -0
  36. package/src/conv_ucs2.mts +47 -0
  37. package/src/conv_uricmp.mts +48 -0
  38. package/src/conv_uricmp.test.mts +16 -0
  39. package/src/conv_zulu.mts +208 -0
  40. package/src/conv_zulu.test.mts +49 -0
  41. package/src/index.cts +1 -0
  42. package/src/index.mts +100 -0
  43. package/tsconfig.json +19 -0
@@ -0,0 +1,99 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ Dictionary,
5
+ VERSION,
6
+ aryval,
7
+ b64tob64u,
8
+ b64tohex,
9
+ b64utob64,
10
+ b64utohex,
11
+ binstrtobitstr,
12
+ hextob64,
13
+ hextob64u,
14
+ hextorstr,
15
+ inttobitstr,
16
+ namearraytobinstr,
17
+ rstrtob64,
18
+ rstrtohex,
19
+ strpad,
20
+ } from "./index.mts";
21
+
22
+ // ==== VERSION ===========================================
23
+ test("VERSION", () => {
24
+ expect(VERSION.substr(0, 16)).toBe("typepki-strconv ");
25
+ });
26
+
27
+ // ==== hex / base64 ======================================
28
+ test("b64tohex", () => {
29
+ expect(b64tohex("YWFh")).toBe("616161");
30
+ });
31
+
32
+ test("hextob64", () => {
33
+ expect(hextob64("616161")).toBe("YWFh");
34
+ });
35
+
36
+ // ==== hex / base64u =====================================
37
+ test("b64utohex", () => {
38
+ expect(b64utohex("YWFh")).toBe("616161");
39
+ });
40
+
41
+ test("hextob64u", () => {
42
+ expect(hextob64u("616161")).toBe("YWFh");
43
+ });
44
+
45
+ // ==== base64 / base64u ==================================
46
+
47
+ test("b64tob64u", () => {
48
+ expect(b64tob64u("ab+c3f/==")).toBe("ab-c3f_");
49
+ expect(b64utob64("ab-c3f_")).toBe("ab+c3f/="); // ?? いいの?
50
+ });
51
+
52
+ test("binstrtobitstr", () => {
53
+ expect(binstrtobitstr("101")).toBe("05a0");
54
+ });
55
+
56
+ test("inttobitstr", () => {
57
+ expect(inttobitstr(0b11001)).toBe("03c8");
58
+ expect(inttobitstr(0b101)).toBe("05a0");
59
+ expect(inttobitstr(0b101000001)).toBe("07a080");
60
+ });
61
+
62
+ test("strpad", () => {
63
+ expect(strpad("1234", 10, "0")).toBe("0000001234");
64
+ expect(strpad("1234", 10, " ")).toBe(" 1234");
65
+ expect(strpad("1234", 10)).toBe("0000001234");
66
+ });
67
+
68
+ test("namearraytobinstr", () => {
69
+ const db: Dictionary<number> = { a: 0, b: 3, c: 8, d: 9, e: 17, f: 19 };
70
+ expect(namearraytobinstr(["a", "c", "d"], db)).toBe("1000000011");
71
+ expect(namearraytobinstr(["c", "b"], db)).toBe("000100001");
72
+ });
73
+
74
+ test("aryval", () => {
75
+ const p = {
76
+ fruit: "apple",
77
+ info: [{ toy: 4 }, { pen: 6 }],
78
+ };
79
+ expect(aryval(p, "fruit")).toBe("apple");
80
+ expect(aryval(p, "info")).toEqual([{ toy: 4 }, { pen: 6 }]);
81
+ expect(aryval(p, "info.1")).toEqual({ pen: 6 });
82
+ expect(aryval(p, "info.1.pen")).toBe(6);
83
+ expect(aryval(p, "money.amount")).toBe(undefined);
84
+ expect(aryval(p, "money.amount", null)).toBe(null);
85
+ });
86
+
87
+ test("hextorstr", () => {
88
+ expect(hextorstr("616161")).toBe("aaa");
89
+ expect(hextorstr("000000")).toBe("\x00\x00\x00");
90
+ });
91
+
92
+ test("rstrtohex", () => {
93
+ expect(rstrtohex("aaa")).toBe("616161");
94
+ expect(rstrtohex("\x00\x00\x00")).toBe("000000");
95
+ });
96
+
97
+ test("rstrtob64", () => {
98
+ expect(rstrtob64("aaa")).toBe("YWFh");
99
+ });
@@ -0,0 +1,53 @@
1
+ /**
2
+ * convert a hexadecimal string to an ArrayBuffer
3
+ * @param hex - hexadecimal string
4
+ * @return ArrayBuffer
5
+ *
6
+ * @description
7
+ * This function converts from a hexadecimal string to an ArrayBuffer.
8
+ *
9
+ * @example
10
+ * hextoArrayBuffer("fffa01") -> ArrayBuffer of [255, 250, 1]
11
+ */
12
+ export function hextoArrayBuffer(hex: string): ArrayBuffer {
13
+ if (hex.length % 2 !== 0) throw "input is not even length";
14
+ if (hex.match(/^[0-9A-Fa-f]+$/) == null) throw "input is not hexadecimal";
15
+
16
+ const buffer = new ArrayBuffer(hex.length / 2);
17
+ const view = new DataView(buffer);
18
+
19
+ for (let i = 0; i < hex.length / 2; i++) {
20
+ view.setUint8(i, parseInt(hex.substr(i * 2, 2), 16));
21
+ }
22
+
23
+ return buffer;
24
+ }
25
+
26
+ // ==== ArrayBuffer / hex =================================
27
+
28
+ /**
29
+ * convert an ArrayBuffer to a hexadecimal string
30
+ * @param buffer - ArrayBuffer
31
+ * @return hexadecimal string
32
+ *
33
+ * @description
34
+ * This function converts from an ArrayBuffer to a hexadecimal string.
35
+ *
36
+ * @example
37
+ * var buffer = new ArrayBuffer(3);
38
+ * var view = new DataView(buffer);
39
+ * view.setUint8(0, 0xfa);
40
+ * view.setUint8(1, 0xfb);
41
+ * view.setUint8(2, 0x01);
42
+ * ArrayBuffertohex(buffer) -> "fafb01"
43
+ */
44
+ export function ArrayBuffertohex(buffer: ArrayBuffer): string {
45
+ let hex = "";
46
+ const view = new DataView(buffer);
47
+
48
+ for (let i = 0; i < buffer.byteLength; i++) {
49
+ hex += `00${view.getUint8(i).toString(16)}`.slice(-2);
50
+ }
51
+
52
+ return hex;
53
+ }
@@ -0,0 +1,16 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { ArrayBuffertohex, hextoArrayBuffer } from "./index.mts";
4
+
5
+ const buf = new ArrayBuffer(3);
6
+ const ui8a = new Uint8Array(buf);
7
+ ui8a[0] = 1;
8
+ ui8a[2] = 1;
9
+
10
+ test("hextoArrayBuffer", () => {
11
+ expect(hextoArrayBuffer("010001")).toStrictEqual(buf);
12
+ });
13
+
14
+ test("ArrayBuffertohex", () => {
15
+ expect(ArrayBuffertohex(buf)).toBe("010001");
16
+ });
@@ -0,0 +1,52 @@
1
+ /**
2
+ * convert a ASCII string to a hexadecimal string of ASCII codes.
3
+ * @description
4
+ * NOTE: This can't be used for non ASCII characters.
5
+ * @param s - ASCII string
6
+ * @return hexadecimal string
7
+ */
8
+ export function stohex(s: string): string {
9
+ return BAtohex(stoBA(s));
10
+ }
11
+
12
+ /**
13
+ * convert an array of bytes(Number) to hexadecimal string.
14
+ * @param a - a array of bytes
15
+ * @return hexadecimal string
16
+ */
17
+ export function BAtohex(a: Array<number>): string {
18
+ let s = "";
19
+ for (let i = 0; i < a.length; i++) {
20
+ let hex1 = a[i].toString(16);
21
+ if (hex1.length === 1) hex1 = `0${hex1}`;
22
+ s = s + hex1;
23
+ }
24
+ return s;
25
+ }
26
+
27
+ // ==== string / byte array ================================
28
+ /**
29
+ * convert a string to an array of character codes
30
+ * @param s - string
31
+ * @return array of character code value
32
+ */
33
+ export function stoBA(s: string): Array<number> {
34
+ const a = new Array();
35
+ for (let i = 0; i < s.length; i++) {
36
+ a[i] = s.charCodeAt(i);
37
+ }
38
+ return a;
39
+ }
40
+
41
+ /**
42
+ * convert an array of character codes to a string
43
+ * @param a - a array of character codes
44
+ * @return string
45
+ */
46
+ export function BAtos(a: Array<number>): string {
47
+ let s = "";
48
+ for (let i = 0; i < a.length; i++) {
49
+ s = s + String.fromCharCode(a[i]);
50
+ }
51
+ return s;
52
+ }
@@ -0,0 +1,226 @@
1
+ /**
2
+ * convert any IPv6 address to a 16 byte hexadecimal string
3
+ * @param sIn - ipv6 address string
4
+ * @return 16 byte hexadecimal string of IPv6 address
5
+ *
6
+ * @description
7
+ * This function converts any IPv6 address representation string
8
+ * to a 16 byte hexadecimal string of address.
9
+ *
10
+ * @example
11
+ */
12
+ export function ipv6tohex(sIn: string): string {
13
+ let s: string = sIn;
14
+ const msgMalformedAddress = "malformed IPv6 address";
15
+ if (!s.match(/^[0-9A-Fa-f:]+$/)) throw msgMalformedAddress;
16
+
17
+ // 1. downcase
18
+ s = s.toLowerCase();
19
+
20
+ // 2. expand ::
21
+ const num_colon = s.split(":").length - 1;
22
+ if (num_colon < 2) throw msgMalformedAddress;
23
+ const colon_replacer = ":".repeat(7 - num_colon + 2);
24
+ s = s.replace("::", colon_replacer);
25
+
26
+ // 3. fill zero
27
+ const a = s.split(":");
28
+ if (a.length !== 8) throw msgMalformedAddress;
29
+ for (let i = 0; i < 8; i++) {
30
+ a[i] = `0000${a[i]}`.slice(-4);
31
+ }
32
+ return a.join("");
33
+ }
34
+
35
+ /**
36
+ * convert a 16 byte hexadecimal string to RFC 5952 canonicalized IPv6 address
37
+ * @param hex - hexadecimal string of 16 byte IPv6 address
38
+ * @return IPv6 address string canonicalized by RFC 5952
39
+ *
40
+ * @description
41
+ * This function converts a 16 byte hexadecimal string to
42
+ * <a href="https://tools.ietf.org/html/rfc5952">RFC 5952</a>
43
+ * canonicalized IPv6 address string.
44
+ *
45
+ * @example
46
+ * hextoipv6("871020010db8000000000000000000000004") -> "2001:db8::4"
47
+ * hextoipv6("871020010db8000000000000000000") -> raise exception
48
+ * hextoipv6("xyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyz") -> raise exception
49
+ */
50
+ export function hextoipv6(hex: string): string {
51
+ let s: string = hex;
52
+ if (!s.match(/^[0-9A-Fa-f]{32}$/))
53
+ throw new Error(`malformed IPv6 address: ${s}`);
54
+
55
+ // 1. downcase
56
+ s = s.toLowerCase();
57
+
58
+ // 2. split 4 > ["0123", "00a4", "0000", ..., "ffff"]
59
+ let a: string[] = s.match(/.{1,4}/g) || [];
60
+
61
+ // 3. trim leading 0 for items and join > "123:a4:0:...:ffff"
62
+ a = a.map((s) => s.replace(/^0+/, ""));
63
+ a = a.map((s) => (s === "" ? "0" : s));
64
+ s = `:${a.join(":")}:`;
65
+
66
+ // 4. find shrinkable candidates :0:0:..:0:
67
+ const aZero = s.match(/:(0:){2,}/g);
68
+
69
+ // 5. no shrinkable
70
+ if (aZero == null) return s.slice(1, -1);
71
+
72
+ // 6. fix max length zero(:0:...:0:)
73
+ const sMaxZero = aZero.sort().slice(-1)[0];
74
+
75
+ // 7. replace shrinked
76
+ s = s.replace(sMaxZero.substr(0, sMaxZero.length - 1), ":");
77
+
78
+ // 8. trim leading ':' if not '::'
79
+ if (s.substr(0, 2) !== "::") s = s.substr(1);
80
+
81
+ // 9. trim tail ':' if not '::'
82
+ if (s.substr(-2, 2) !== "::") s = s.substr(0, s.length - 1);
83
+
84
+ return s;
85
+ }
86
+
87
+ /*
88
+ * @see hextoipv6
89
+ * @see iptohex
90
+ */
91
+ /**
92
+ * convert a hexadecimal string to IP addresss
93
+ * @param s - hexadecimal string of IP address
94
+ * @return IP address string
95
+ *
96
+ * @description
97
+ * This function converts a hexadecimal string of IPv4 or
98
+ * IPv6 address to IPv4 or IPv6 address string.
99
+ * If byte length is not 4 nor 16, this returns a
100
+ * hexadecimal string without conversion.
101
+ *
102
+ * @example
103
+ * hextoip("c0a80101") -> "192.168.1.1"
104
+ * hextoip("871020010db8000000000000000000000004") -> "2001:db8::4"
105
+ * hextoip("c0a80100ffffff00") -> "192.168.1.0/24"
106
+ * hextoip("c0a801010203") -> "c0a801010203" // wrong 6 bytes
107
+ * hextoip("zzz")) -> raise exception because of not hexadecimal
108
+ */
109
+ export function hextoip(s: string): string {
110
+ const malformedErr = new Error("malformed hex value");
111
+ if (!s.match(/^([0-9A-Fa-f][0-9A-Fa-f]){1,}$/)) throw malformedErr;
112
+ if (s.length === 8) {
113
+ // ipv4
114
+ let ip: string;
115
+ try {
116
+ ip = `${parseInt(s.substr(0, 2), 16)}.${parseInt(
117
+ s.substr(2, 2),
118
+ 16,
119
+ )}.${parseInt(s.substr(4, 2), 16)}.${parseInt(s.substr(6, 2), 16)}`;
120
+ return ip;
121
+ } catch (ex) {
122
+ throw malformedErr;
123
+ }
124
+ } else if (s.length === 16) {
125
+ try {
126
+ return `${hextoip(s.substr(0, 8))}/${ipprefixlen(s.substr(8))}`;
127
+ } catch (ex) {
128
+ throw malformedErr;
129
+ }
130
+ } else if (s.length === 32) {
131
+ return hextoipv6(s);
132
+ } else if (s.length === 64) {
133
+ try {
134
+ return `${hextoipv6(s.substr(0, 32))}/${ipprefixlen(s.substr(32))}`;
135
+ } catch (ex) {
136
+ throw malformedErr;
137
+ }
138
+ } else {
139
+ return s;
140
+ }
141
+ }
142
+
143
+ /**
144
+ * convert IPv4/v6 addresss to a hexadecimal string
145
+ * @param sIn - IPv4/v6 address string
146
+ * @return hexadecimal string of IP address
147
+ * @see hextoip
148
+ * @see ipv6tohex
149
+ *
150
+ * @description
151
+ * This function converts IPv4 or IPv6 address string to
152
+ * a hexadecimal string of IPv4 or IPv6 address.
153
+ *
154
+ * @example
155
+ * iptohex("192.168.1.1") -> "c0a80101"
156
+ * iptohex("2001:db8::4") -> "871020010db8000000000000000000000004"
157
+ * iptohex("192.168.1.1/24") -> "c0a80101ffffff00"
158
+ * iptohex("2001:db8::/120") -> "871020010db8000000000000000000000000ffffffffffffffffffffffffffffffffff00"
159
+ * iptohex("zzz")) -> raise exception
160
+ */
161
+ export function iptohex(sIn: string): string {
162
+ let s: string = sIn;
163
+ const malformedErr = new Error("malformed IP address");
164
+ s = s.toLowerCase();
165
+
166
+ if (!s.match(/^[0-9a-f.:/]+$/)) throw malformedErr;
167
+
168
+ if (s.match(/^[0-9.]+$/)) {
169
+ const a = s.split(".");
170
+ if (a.length !== 4) throw malformedErr;
171
+ let hex = "";
172
+ try {
173
+ for (let i = 0; i < 4; i++) {
174
+ const d = parseInt(a[i]);
175
+ hex += `0${d.toString(16)}`.slice(-2);
176
+ }
177
+ return hex;
178
+ } catch (ex) {
179
+ throw malformedErr;
180
+ }
181
+ } else if (s.match(/^[0-9.]+\/[0-9]+$/)) {
182
+ const aItem = s.split("/");
183
+ return iptohex(aItem[0]) + ipnetmask(parseInt(aItem[1]), 32);
184
+ } else if (s.match(/^[0-9a-f:]+$/) && s.indexOf(":") !== -1) {
185
+ return ipv6tohex(s);
186
+ } else if (s.match(/^[0-9a-f:]+\/[0-9]+$/) && s.indexOf(":") !== -1) {
187
+ const aItem = s.split("/");
188
+ return ipv6tohex(aItem[0]) + ipnetmask(parseInt(aItem[1]), 128);
189
+ } else {
190
+ throw malformedErr;
191
+ }
192
+ }
193
+
194
+ /*
195
+ * convert subnet mask hex to ip address prefix length<br/>
196
+ * @param hMask - hexadecimal string of ipv4/6 subnet mask (ex. "ffffff00" for v4 class C)
197
+ * @return ip address prefix length (ex. 24 for IPv4 class C)
198
+ */
199
+ export function ipprefixlen(hMask: string): number {
200
+ const malformedErr = new Error("malformed mask");
201
+ let bMask: string;
202
+ try {
203
+ bMask = parseInt(hMask, 16).toString(2);
204
+ } catch (ex) {
205
+ throw malformedErr;
206
+ }
207
+ if (!bMask.match(/^1*0*$/)) throw malformedErr;
208
+ return bMask.replace(/0+$/, "").length;
209
+ }
210
+
211
+ /*
212
+ * convert ip prefix length to net mask octets<br/>
213
+ * @param prefixlen - ip prefix length value (ex. 24 for IPv4 class C)
214
+ * @param len - ip address length (ex. 32 for IPv4 and 128 for IPv6)
215
+ * @return hexadecimal string of net mask octets
216
+ * @example
217
+ * ipnetmask(24, 32) -> "ffffff00"
218
+ * ipnetmask(120, 128) -> "ffffffffffffffffffffffffffffff00"
219
+ */
220
+ export function ipnetmask(prefixlen: number, len: number): string {
221
+ if (len === 32 && prefixlen === 0) return "00000000"; // v4
222
+ if (len === 128 && prefixlen === 0) return "00000000000000000000000000000000"; // v6
223
+ const b =
224
+ Array(prefixlen + 1).join("1") + Array(len - prefixlen + 1).join("0");
225
+ return parseInt(b, 2).toString(16);
226
+ }
@@ -0,0 +1,63 @@
1
+ import { hextoutf8, utf8tohex } from "./conv.mts";
2
+
3
+ /**
4
+ * convert a hexadecimal ISO 8859-1 latin string to UTF-8 string
5
+ * @param h hexadecimal ISO 8859-1 latin string
6
+ * @return UTF-8 string
7
+ * @see {@link utf8toiso88591hex}
8
+ * @example
9
+ * iso88591hextoutf8("41a9fa") -> "A©ú"
10
+ */
11
+ export function iso88591hextoutf8(h: string): string {
12
+ return hextoutf8(iso88591hextoutf8hex(h));
13
+ }
14
+
15
+ /**
16
+ * convert UTF-8 string to a hexadecimal ISO 8859-1 latin string
17
+ * @param s hexadecimal ISO 8859-1 latin string
18
+ * @return UTF-8 string
19
+ * @see {@link iso88591hextoutf8}
20
+ * @example
21
+ * utf8toiso88591hex("A©ú") -> "41a9fa"
22
+ */
23
+ export function utf8toiso88591hex(s: string): string {
24
+ return utf8hextoiso88591hex(utf8tohex(s));
25
+ }
26
+
27
+ function iso88591hextoutf8hex(h: string): string {
28
+ const a = h.match(/.{1,2}/g);
29
+ if (a == null) return "";
30
+ const a2 = [];
31
+ for (let i = 0; i < a.length; i++) {
32
+ const di = parseInt(a[i], 16);
33
+ if (0xa1 <= di && di <= 0xbf) {
34
+ a2.push("c2");
35
+ a2.push(a[i]);
36
+ } else if (0xc0 <= di && di <= 0xff) {
37
+ a2.push("c3");
38
+ a2.push((di - 64).toString(16));
39
+ } else {
40
+ a2.push(a[i]);
41
+ }
42
+ }
43
+ return a2.join("");
44
+ }
45
+
46
+ function utf8hextoiso88591hex(h: string): string {
47
+ const a = h.match(/.{1,2}/g);
48
+ if (a == null) return "";
49
+ const a2 = [];
50
+ for (let i = 0; i < a.length; i++) {
51
+ if (a[i] === "c2") {
52
+ i++;
53
+ a2.push(a[i]);
54
+ } else if (a[i] === "c3") {
55
+ i++;
56
+ const di = parseInt(a[i], 16) + 64;
57
+ a2.push(di.toString(16));
58
+ } else {
59
+ a2.push(a[i]);
60
+ }
61
+ }
62
+ return a2.join("");
63
+ }
@@ -0,0 +1,160 @@
1
+ import { b64tohex, hextob64 } from "./conv.mts";
2
+
3
+ // === pem / hex ======================
4
+ /**
5
+ * get PEM string from hexadecimal data and header string
6
+ * @param dataHex - hexadecimal string of PEM body
7
+ * @param pemHeader - PEM header string (ex. 'RSA PRIVATE KEY')
8
+ * @param separator - new line string
9
+ * @return PEM formatted string of input data
10
+ *
11
+ * @description
12
+ * This function converts a hexadecimal string to a PEM string with
13
+ * a specified header. Its line break will be CRLF("\r\n").
14
+ *
15
+ * @example
16
+ * hextopem('616161', 'RSA PRIVATE KEY') ->
17
+ * -----BEGIN PRIVATE KEY-----
18
+ * YWFh
19
+ * -----END PRIVATE KEY-----
20
+ */
21
+ export function hextopem(
22
+ dataHex: string,
23
+ pemHeader: string,
24
+ separator = "\r\n",
25
+ ): string {
26
+ return `-----BEGIN ${pemHeader}-----${separator}${foldnl(
27
+ hextob64(dataHex),
28
+ 64,
29
+ )}${separator}-----END ${pemHeader}-----${separator}`;
30
+ }
31
+
32
+ /**
33
+ * get hexacedimal string from PEM format data
34
+ * @param s - PEM formatted string
35
+ * @param sHead - PEM header string without BEGIN/END
36
+ * @return hexadecimal string data of PEM contents
37
+ *
38
+ * @description
39
+ * This static method gets a hexacedimal string of contents
40
+ * from PEM format data. You can explicitly specify PEM header
41
+ * by sHead argument.
42
+ * Any space characters such as white space or new line
43
+ * will be omitted.
44
+ *
45
+ * @example
46
+ * pemtohex("-----BEGIN PUBLIC KEY...") -> "3082..."
47
+ * pemtohex("-----BEGIN CERTIFICATE...", "CERTIFICATE") -> "3082..."
48
+ * pemtohex(" \r\n-----BEGIN DSA PRIVATE KEY...") -> "3082..."
49
+ * pemtohex("-----BEGIN EC PARAMETERS...----BEGIN EC PRIVATE KEY...." -> "3082..."
50
+ */
51
+ export function pemtohex(s: string, sHead?: string): string {
52
+ return b64tohex(pemtob64(s, sHead));
53
+ }
54
+
55
+ /**
56
+ * get Base64 string from PEM format data
57
+ * @param pem - PEM formatted string
58
+ * @param sHead - PEM header string without BEGIN/END
59
+ * @return Base64 string
60
+ *
61
+ * @description
62
+ * This static method gets a Base64 string of contents
63
+ * from PEM format data.
64
+ *
65
+ * @example
66
+ * pemtob64("-----BEGIN CERTIFICATE...", "CERTIFICATE") -> "MIIBvTCC..."
67
+ * pemtob64("-----BEGIN CERTIFICATE...") -> "MIIBvTCC..."
68
+ */
69
+ export function pemtob64(pem: string, sHead?: string): string {
70
+ let s: string = pem;
71
+ if (s.indexOf("-----BEGIN ") === -1) throw new Error("can't find PEM header");
72
+
73
+ if (sHead !== undefined) {
74
+ s = s.replace(new RegExp(`^[^-]*-----BEGIN ${sHead}-----`), "");
75
+ s = s.replace(new RegExp(`-----END ${sHead}-----[^-]*$`), "");
76
+ } else {
77
+ s = s.replace(/^[^-]*-----BEGIN [^-]+-----/, "");
78
+ s = s.replace(/-----END [^-]+-----[^-]*$/, "");
79
+ }
80
+ return s.replace(/\s+/g, "");
81
+ }
82
+
83
+ // === others =========================
84
+
85
+ /**
86
+ * get PEM string from Base64 string
87
+ * @param b64 - Base64 string of PEM body
88
+ * @param pemHeader - PEM header string (ex. 'RSA PRIVATE KEY')
89
+ * @param separator - new line string
90
+ * @return PEM formatted string of input data
91
+ *
92
+ * @description
93
+ * This function converts a Base64 string to a PEM string with
94
+ * a specified header. Its line break will be CRLF("\r\n").
95
+ *
96
+ * @example
97
+ * b64topem('YWFh', 'RSA PRIVATE KEY') ->
98
+ * -----BEGIN PRIVATE KEY-----
99
+ * YWFh
100
+ * -----END PRIVATE KEY-----
101
+ */
102
+ export function b64topem(
103
+ b64: string,
104
+ pemHeader: string,
105
+ separator = "\r\n",
106
+ ): string {
107
+ return `-----BEGIN ${pemHeader}-----${separator}${foldnl(
108
+ b64,
109
+ 64,
110
+ separator,
111
+ )}${separator}-----END ${pemHeader}-----${separator}`;
112
+ }
113
+
114
+ /**
115
+ * convert a Base64 encoded string with new lines to a hexadecimal string
116
+ * @param s - Base64 encoded string with new lines
117
+ * @return hexadecimal string
118
+ *
119
+ * @description
120
+ * This function converts from a Base64 encoded
121
+ * string with new lines to a hexadecimal string.
122
+ * This is useful to handle PEM encoded file.
123
+ * This function removes any non-Base64 characters (i.e. not 0-9,A-Z,a-z,\,+,=)
124
+ * including new line.
125
+ *
126
+ * @example
127
+ * hextob64nl(
128
+ * "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4\r\n" +
129
+ * "OTAxMjM0NTY3ODkwCg==\r\n")
130
+ * ->
131
+ * "123456789012345678901234567890123456789012345678901234567890"
132
+ */
133
+ export function b64nltohex(s: string): string {
134
+ const b64 = s.replace(/[^0-9A-Za-z\/+=]*/g, "");
135
+ const hex = b64tohex(b64);
136
+ return hex;
137
+ }
138
+
139
+ /**
140
+ * wrap string with new lines to fit in specified width
141
+ * @param sIn - string
142
+ * @param n - width
143
+ * @param separator - line separator
144
+ * @return wrapped string with new lines
145
+ *
146
+ * @description
147
+ * This function wrap a string with new lines to fit in specified width.
148
+ *
149
+ * @example
150
+ * foldnl("1234567890", 6)
151
+ * ->
152
+ * 123456
153
+ * 7890
154
+ */
155
+ export function foldnl(sIn: string, n: number, separator = "\r\n"): string {
156
+ let s: string = sIn;
157
+ s = s.replace(new RegExp(`(.{${n}})`, "g"), `$1${separator}`);
158
+ s = s.replace(/\s+$/, "");
159
+ return s;
160
+ }