1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.javascript.host.crypto;
16
17 import java.math.BigInteger;
18 import java.util.Set;
19
20 import org.htmlunit.corejs.javascript.Scriptable;
21 import org.htmlunit.corejs.javascript.ScriptableObject;
22 import org.htmlunit.corejs.javascript.VarScope;
23 import org.htmlunit.corejs.javascript.typedarrays.NativeArrayBuffer;
24 import org.htmlunit.corejs.javascript.typedarrays.NativeArrayBufferView;
25 import org.htmlunit.corejs.javascript.typedarrays.NativeUint8Array;
26 import org.htmlunit.javascript.JavaScriptEngine;
27
28
29
30
31
32
33
34
35 final class RsaHashedKeyAlgorithm {
36
37 static final Set<String> SUPPORTED_NAMES = Set.of("RSASSA-PKCS1-v1_5", "RSA-PSS", "RSA-OAEP");
38 static final Set<String> SUPPORTED_HASH_ALGORITHMS = Set.of("SHA-1", "SHA-256", "SHA-384", "SHA-512");
39
40 private final String name_;
41 private final int modulusLength_;
42 private final byte[] publicExponent_;
43 private final String hash_;
44
45 RsaHashedKeyAlgorithm(final String name, final int modulusLength,
46 final byte[] publicExponent, final String hash) {
47 if (!SUPPORTED_NAMES.contains(name)) {
48 throw new UnsupportedOperationException("RSA " + name);
49 }
50 name_ = name;
51
52 if (modulusLength <= 0) {
53 throw new IllegalArgumentException("Data provided to an operation does not meet requirements");
54 }
55 modulusLength_ = modulusLength;
56
57 if (publicExponent == null || publicExponent.length == 0) {
58 throw new IllegalArgumentException("Data provided to an operation does not meet requirements");
59 }
60 publicExponent_ = publicExponent.clone();
61
62 if (!SUPPORTED_HASH_ALGORITHMS.contains(hash)) {
63 throw new UnsupportedOperationException("RSA hash " + hash);
64 }
65 hash_ = hash;
66 }
67
68
69
70
71
72
73
74 static RsaHashedKeyAlgorithm from(final Scriptable keyGenParams) {
75 final Object nameProp = ScriptableObject.getProperty(keyGenParams, "name");
76 if (!(nameProp instanceof String name)) {
77 throw new IllegalArgumentException("An invalid or illegal string was specified");
78 }
79
80 final Object modulusLengthProp = ScriptableObject.getProperty(keyGenParams, "modulusLength");
81 if (!(modulusLengthProp instanceof Number numModulusLength)) {
82 throw new IllegalArgumentException("An invalid or illegal string was specified");
83 }
84
85 final Object publicExponentProp = ScriptableObject.getProperty(keyGenParams, "publicExponent");
86 final byte[] publicExponent = extractBytes(publicExponentProp);
87 if (publicExponent == null) {
88 throw new IllegalArgumentException("An invalid or illegal string was specified");
89 }
90
91 final Object hashProp = ScriptableObject.getProperty(keyGenParams, "hash");
92 final String hash = SubtleCrypto.resolveAlgorithmName(hashProp);
93
94 return new RsaHashedKeyAlgorithm(name, numModulusLength.intValue(), publicExponent, hash);
95 }
96
97 private static byte[] extractBytes(final Object value) {
98 if (value instanceof NativeArrayBufferView view) {
99 final NativeArrayBuffer buf = view.getBuffer();
100 final byte[] result = new byte[view.getByteLength()];
101 System.arraycopy(buf.getBuffer(), view.getByteOffset(), result, 0, result.length);
102 return result;
103 }
104 if (value instanceof NativeArrayBuffer buf) {
105 return buf.getBuffer().clone();
106 }
107 return null;
108 }
109
110 static boolean isSupported(final String name) {
111 return SUPPORTED_NAMES.contains(name);
112 }
113
114 String getName() {
115 return name_;
116 }
117
118 int getModulusLength() {
119 return modulusLength_;
120 }
121
122 byte[] getPublicExponent() {
123 return publicExponent_.clone();
124 }
125
126
127
128
129 BigInteger getPublicExponentAsBigInteger() {
130 return new BigInteger(1, publicExponent_);
131 }
132
133 String getHash() {
134 return hash_;
135 }
136
137
138
139
140 String getJavaHash() {
141 return hash_.replace("-", "");
142 }
143
144
145
146
147
148
149
150
151 Scriptable toScriptableObject(final VarScope scope) {
152 final Scriptable hashObj = JavaScriptEngine.newObject(scope);
153 ScriptableObject.putProperty(hashObj, "name", getHash());
154
155 final NativeUint8Array uint8Array = JavaScriptEngine.newUint8Array(scope, publicExponent_);
156
157 final Scriptable algorithm = JavaScriptEngine.newObject(scope);
158 ScriptableObject.putProperty(algorithm, "name", getName());
159 ScriptableObject.putProperty(algorithm, "hash", hashObj);
160 ScriptableObject.putProperty(algorithm, "modulusLength", getModulusLength());
161 ScriptableObject.putProperty(algorithm, "publicExponent", uint8Array);
162 return algorithm;
163 }
164 }