• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hyperwallet / java-sdk / #692

15 May 2025 07:47PM UTC coverage: 96.097% (-2.1%) from 98.161%
#692

push

web-flow
Add support to github actions for CI/CD (#219)

* Add support to github actions for CI/CD

* Add support to github actions for CI/CD

5048 of 5253 relevant lines covered (96.1%)

65.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.54
/src/main/java/com/hyperwallet/clientsdk/util/HyperwalletEncryption.java
1
package com.hyperwallet.clientsdk.util;
2

3
import com.hyperwallet.clientsdk.HyperwalletException;
4
import com.nimbusds.jose.Algorithm;
5
import com.nimbusds.jose.EncryptionMethod;
6
import com.nimbusds.jose.JOSEException;
7
import com.nimbusds.jose.JWEAlgorithm;
8
import com.nimbusds.jose.JWEDecrypter;
9
import com.nimbusds.jose.JWEEncrypter;
10
import com.nimbusds.jose.JWEHeader;
11
import com.nimbusds.jose.JWEObject;
12
import com.nimbusds.jose.JWSAlgorithm;
13
import com.nimbusds.jose.JWSHeader;
14
import com.nimbusds.jose.JWSObject;
15
import com.nimbusds.jose.JWSSigner;
16
import com.nimbusds.jose.JWSVerifier;
17
import com.nimbusds.jose.Payload;
18
import com.nimbusds.jose.crypto.ECDHDecrypter;
19
import com.nimbusds.jose.crypto.ECDHEncrypter;
20
import com.nimbusds.jose.crypto.ECDSASigner;
21
import com.nimbusds.jose.crypto.ECDSAVerifier;
22
import com.nimbusds.jose.crypto.RSADecrypter;
23
import com.nimbusds.jose.crypto.RSAEncrypter;
24
import com.nimbusds.jose.crypto.RSASSASigner;
25
import com.nimbusds.jose.crypto.RSASSAVerifier;
26
import com.nimbusds.jose.jwk.ECKey;
27
import com.nimbusds.jose.jwk.JWK;
28
import com.nimbusds.jose.jwk.JWKSet;
29
import com.nimbusds.jose.jwk.KeyType;
30
import com.nimbusds.jose.jwk.RSAKey;
31

32
import java.io.File;
33
import java.io.IOException;
34
import java.net.*;
35
import java.nio.file.Files;
36
import java.nio.file.Paths;
37
import java.text.ParseException;
38
import java.util.Arrays;
39
import java.util.Collections;
40
import java.util.Date;
41
import java.util.HashSet;
42
import java.util.List;
43

44
public class HyperwalletEncryption {
45

46
    private static final String EXPIRATION = "exp";
47
    private static final Integer MILLISECONDS_IN_ONE_MINUTE = 60000;
1✔
48
    private static final Long MILLISECONDS_IN_SECOND = 1000L;
1✔
49
    private static final Integer EXPIRATION_MINUTES = 5;
1✔
50
    private static final JWEAlgorithm ENCRYPTION_ALGORITHM = JWEAlgorithm.RSA_OAEP_256;
1✔
51
    private static final JWSAlgorithm SIGN_ALGORITHM = JWSAlgorithm.RS256;
1✔
52
    private static final EncryptionMethod ENCRYPTION_METHOD = EncryptionMethod.A256CBC_HS512;
1✔
53
    private static final String INVALID_KEY_TYPE_STRING = "'kty' not supported = %s";
54

55
    private static final List<JWEAlgorithm> SUPPORTED_JWE_ALGORITHMS = Arrays.asList(JWEAlgorithm.RSA_OAEP_256,
1✔
56
            JWEAlgorithm.ECDH_ES,
57
            JWEAlgorithm.ECDH_ES_A128KW,
58
            JWEAlgorithm.ECDH_ES_A192KW,
59
            JWEAlgorithm.ECDH_ES_A256KW);
60
    private static final List<JWSAlgorithm> SUPPORTED_JWS_ALGORITHMS = Arrays.asList(JWSAlgorithm.RS256,
1✔
61
            JWSAlgorithm.RS384,
62
            JWSAlgorithm.RS512,
63
            JWSAlgorithm.PS256,
64
            JWSAlgorithm.PS384,
65
            JWSAlgorithm.PS512,
66
            JWSAlgorithm.ES256,
67
            JWSAlgorithm.ES384,
68
            JWSAlgorithm.ES512);
69
    private static final List<EncryptionMethod> SUPPORTED_ENCRYPTION_METHODS = Arrays.asList(EncryptionMethod.A128CBC_HS256,
1✔
70
            EncryptionMethod.A192CBC_HS384,
71
            EncryptionMethod.A256CBC_HS512,
72
            EncryptionMethod.A128GCM,
73
            EncryptionMethod.A256GCM);
74

75
    private final JWEAlgorithm encryptionAlgorithm;
76
    private final JWSAlgorithm signAlgorithm;
77
    private final EncryptionMethod encryptionMethod;
78
    private final String clientPrivateKeySetLocation;
79
    private final String hyperwalletKeySetLocation;
80
    private final Integer jwsExpirationMinutes;
81

82
    private Proxy proxy;
83
    private String proxyUsername;
84
    private String proxyPassword;
85

86
    public HyperwalletEncryption(JWEAlgorithm encryptionAlgorithm, JWSAlgorithm signAlgorithm, EncryptionMethod encryptionMethod,
87
            String clientPrivateKeySetLocation, String hyperwalletKeySetLocation, Integer jwsExpirationMinutes) {
126✔
88
        this.encryptionAlgorithm = encryptionAlgorithm == null ? ENCRYPTION_ALGORITHM : encryptionAlgorithm;
126✔
89
        this.signAlgorithm = signAlgorithm == null ? SIGN_ALGORITHM : signAlgorithm;
126✔
90
        this.encryptionMethod = encryptionMethod == null ? ENCRYPTION_METHOD : encryptionMethod;
126✔
91
        this.clientPrivateKeySetLocation = clientPrivateKeySetLocation;
126✔
92
        this.hyperwalletKeySetLocation = hyperwalletKeySetLocation;
126✔
93
        this.jwsExpirationMinutes = jwsExpirationMinutes == null ? EXPIRATION_MINUTES : jwsExpirationMinutes;
126✔
94

95
        if (!SUPPORTED_JWS_ALGORITHMS.contains(this.signAlgorithm)) {
126✔
96
            throw new IllegalArgumentException("Unsupported signing algorithm " + this.signAlgorithm);
1✔
97
        }
98
        if (!SUPPORTED_JWE_ALGORITHMS.contains(this.encryptionAlgorithm)) {
125✔
99
            throw new IllegalArgumentException("Unsupported encryption algorithm " + this.encryptionAlgorithm);
1✔
100
        }
101
        if (!SUPPORTED_ENCRYPTION_METHODS.contains(this.encryptionMethod)) {
124✔
102
            throw new IllegalArgumentException("Unsupported encryption method " + this.encryptionMethod);
2✔
103
        }
104
    }
122✔
105

106
    public String encrypt(String body) throws JOSEException, IOException, ParseException {
107

108
        JWK clientPrivateKey = getKeyByAlgorithm(loadKeySet(clientPrivateKeySetLocation), signAlgorithm);
22✔
109
        JWK hyperwalletPublicKey = getKeyByAlgorithm(loadKeySet(hyperwalletKeySetLocation), encryptionAlgorithm);
21✔
110
        JWSSigner signer = getJWSSigner(clientPrivateKey);
19✔
111
        JWEEncrypter jwsEncrypter = getJWEEncrypter(hyperwalletPublicKey);
19✔
112

113
        JWSObject jwsObject = new JWSObject(
18✔
114
                new JWSHeader.Builder(signAlgorithm)
115
                        .keyID(clientPrivateKey.getKeyID())
18✔
116
                        .criticalParams(new HashSet<>(Collections.singletonList(EXPIRATION)))
18✔
117
                        .customParam(EXPIRATION, getJWSExpirationMillis()).build(),
18✔
118
                new Payload(body));
119

120
        jwsObject.sign(signer);
18✔
121

122
        JWEObject jweObject = new JWEObject(
18✔
123
                new JWEHeader.Builder(encryptionAlgorithm, encryptionMethod)
124
                        .keyID(hyperwalletPublicKey.getKeyID()).build(),
18✔
125
                new Payload(jwsObject));
126

127
        jweObject.encrypt(jwsEncrypter);
18✔
128

129
        return jweObject.serialize();
18✔
130
    }
131

132
    public String decrypt(String body) throws ParseException, IOException, JOSEException {
133

134
        JWK privateKeyToDecrypt = getKeyByAlgorithm(loadKeySet(clientPrivateKeySetLocation), encryptionAlgorithm);
11✔
135
        JWK publicKeyToSign = getKeyByAlgorithm(loadKeySet(hyperwalletKeySetLocation), signAlgorithm);
11✔
136
        JWEDecrypter jweDecrypter = getJWEDecrypter(privateKeyToDecrypt);
11✔
137
        JWSVerifier verifier = getJWSVerifier(publicKeyToSign);
11✔
138

139
        JWEObject jweObject = JWEObject.parse(body);
11✔
140
        jweObject.decrypt(jweDecrypter);
11✔
141
        JWSObject jwsObject = jweObject.getPayload().toJWSObject();
10✔
142
        verifySignatureExpirationDate(jwsObject.getHeader().getCustomParam(EXPIRATION));
10✔
143
        boolean verifyStatus = jwsObject.verify(verifier);
10✔
144
        if (!verifyStatus) {
10✔
145
            throw new HyperwalletException("JWS signature is incorrect");
×
146
        }
147
        return jwsObject.getPayload().toString();
10✔
148
    }
149

150
    public void verifySignatureExpirationDate(Object signatureExpirationDate) {
151
        if (signatureExpirationDate == null) {
13✔
152
            throw new HyperwalletException("exp JWS header param was null");
1✔
153
        }
154
        if (!(signatureExpirationDate instanceof Long)) {
12✔
155
            throw new HyperwalletException("exp JWS header must be of type Long");
1✔
156
        }
157
        long expirationTimeSeconds = (long) signatureExpirationDate;
11✔
158
        if (new Date().getTime() / MILLISECONDS_IN_SECOND > expirationTimeSeconds) {
11✔
159
            throw new HyperwalletException("Response message signature(JWS) has expired");
1✔
160
        }
161
    }
10✔
162

163
    public JWEAlgorithm getEncryptionAlgorithm() {
164
        return encryptionAlgorithm;
3✔
165
    }
166

167
    public JWSAlgorithm getSignAlgorithm() {
168
        return signAlgorithm;
3✔
169
    }
170

171
    public EncryptionMethod getEncryptionMethod() {
172
        return encryptionMethod;
3✔
173
    }
174

175
    public String getClientPrivateKeySetLocation() {
176
        return clientPrivateKeySetLocation;
3✔
177
    }
178

179
    public String getHyperwalletKeySetLocation() {
180
        return hyperwalletKeySetLocation;
3✔
181
    }
182

183
    public Integer getJwsExpirationMinutes() {
184
        return jwsExpirationMinutes;
3✔
185
    }
186

187
    private JWKSet loadKeySet(String keySetLocation) throws IOException, ParseException {
188
        URL url;
189
        try {
190
            url = new URL(keySetLocation);
65✔
191
        } catch (MalformedURLException e) {
63✔
192
            checkKeySetLocationIsFile(keySetLocation);
63✔
193
            return JWKSet.load(new File(keySetLocation));
62✔
194
        }
2✔
195

196
        if (usesProxy()) {
2✔
197
            if (proxyUsername != null && proxyPassword != null) {
×
198
                Authenticator authenticator = new Request.DefaultPasswordAuthenticator(
×
199
                        proxyUsername, proxyPassword);
200
                Authenticator.setDefault(authenticator);
×
201
            }
202
            return JWKSet.load(url, 0, 0, 0, getProxy());
×
203
        }
204
        return JWKSet.load(url);
2✔
205
    }
206

207
    private long getJWSExpirationMillis() {
208
        return new Date(new Date().getTime() + MILLISECONDS_IN_ONE_MINUTE * jwsExpirationMinutes).getTime() / MILLISECONDS_IN_SECOND;
18✔
209
    }
210

211
    private <T extends Algorithm> JWK getKeyByAlgorithm(JWKSet keySet, T algorithm) {
212
        for (JWK key : keySet.getKeys()) {
64✔
213
            if (key.getAlgorithm().equals(algorithm)) {
105✔
214
                return key;
62✔
215
            }
216
        }
43✔
217
        throw new IllegalStateException("Algorithm = " + algorithm + " is not found in client or Hyperwallet key set");
2✔
218
    }
219

220
    private void checkKeySetLocationIsFile(String keySetLocation) {
221
        if (Files.notExists(Paths.get(keySetLocation))) {
63✔
222
            throw new IllegalArgumentException("Wrong client JWK set location");
1✔
223
        }
224
    }
62✔
225

226
    private JWSSigner getJWSSigner(JWK jwk) {
227
        try {
228
            KeyType kty = jwk.getKeyType();
19✔
229
            if (kty.equals(KeyType.RSA)) {
19✔
230
                return new RSASSASigner((RSAKey) jwk);
17✔
231
            } else if (kty.equals(KeyType.EC)) {
2✔
232
                return new ECDSASigner((ECKey) jwk);
2✔
233
            } else {
234
                throw new IllegalArgumentException(String.format(INVALID_KEY_TYPE_STRING, kty));
×
235
            }
236
        } catch (JOSEException e) {
×
237
            throw new HyperwalletException("Unable to create signer");
×
238
        }
239
    }
240

241
    private JWEEncrypter getJWEEncrypter(JWK jwk) {
242
        try {
243
            KeyType kty = jwk.getKeyType();
19✔
244
            if (kty.equals(KeyType.RSA)) {
19✔
245
                return new RSAEncrypter((RSAKey) jwk);
17✔
246
            } else if (kty.equals(KeyType.EC)) {
2✔
247
                return new ECDHEncrypter((ECKey) jwk);
1✔
248
            } else {
249
                throw new IllegalArgumentException(String.format(INVALID_KEY_TYPE_STRING, kty));
1✔
250
            }
251
        } catch (JOSEException e) {
×
252
            throw new HyperwalletException("Unable to create encrypter");
×
253
        }
254
    }
255

256
    private JWSVerifier getJWSVerifier(JWK jwk) {
257
        try {
258
            KeyType kty = jwk.getKeyType();
11✔
259
            if (kty.equals(KeyType.RSA)) {
11✔
260
                return new RSASSAVerifier(((RSAKey) jwk).toRSAPublicKey(), new HashSet<>(Collections.singletonList(EXPIRATION)));
10✔
261
            } else if (kty.equals(KeyType.EC)) {
1✔
262
                return new ECDSAVerifier(((ECKey) jwk).toECPublicKey(), new HashSet<>(Collections.singletonList(EXPIRATION)));
1✔
263
            } else {
264
                throw new IllegalArgumentException(String.format(INVALID_KEY_TYPE_STRING, kty));
×
265
            }
266
        } catch (JOSEException e) {
×
267
            throw new HyperwalletException("Unable to create verifier");
×
268
        }
269
    }
270

271
    private JWEDecrypter getJWEDecrypter(JWK jwk) {
272
        try {
273
            KeyType kty = jwk.getKeyType();
11✔
274
            if (kty.equals(KeyType.RSA)) {
11✔
275
                return new RSADecrypter((RSAKey) jwk);
10✔
276
            } else if (kty.equals(KeyType.EC)) {
1✔
277
                return new ECDHDecrypter((ECKey) jwk);
1✔
278
            } else {
279
                throw new IllegalArgumentException(String.format(INVALID_KEY_TYPE_STRING, kty));
×
280
            }
281
        } catch (JOSEException e) {
×
282
            throw new HyperwalletException("Unable to create decrypter");
×
283
        }
284
    }
285

286
    public Boolean usesProxy() {
287
        return proxy != null;
2✔
288
    }
289

290
    public void setProxy(String url, Integer port) {
291
        this.proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(url, port));
9✔
292
    }
9✔
293

294
    public void setProxy(Proxy proxy) {
295
        this.proxy = proxy;
1✔
296
    }
1✔
297

298
    public Proxy getProxy() {
299
        return proxy;
3✔
300
    }
301

302
    public String getProxyUsername() {
303
        return proxyUsername;
3✔
304
    }
305

306
    public void setProxyUsername(String proxyUsername) {
307
        this.proxyUsername = proxyUsername;
10✔
308
    }
10✔
309

310
    public String getProxyPassword() {
311
        return proxyPassword;
3✔
312
    }
313

314
    public void setProxyPassword(String proxyPassword) {
315
        this.proxyPassword = proxyPassword;
10✔
316
    }
10✔
317

318
    public static class HyperwalletEncryptionBuilder {
117✔
319

320
        private JWEAlgorithm encryptionAlgorithm;
321
        private JWSAlgorithm signAlgorithm;
322
        private EncryptionMethod encryptionMethod;
323
        private String clientPrivateKeySetLocation;
324
        private String hyperwalletKeySetLocation;
325
        private Integer jwsExpirationMinutes;
326

327
        private Proxy proxy;
328
        private String proxyUsername;
329
        private String proxyPassword;
330

331
        public HyperwalletEncryptionBuilder encryptionAlgorithm(JWEAlgorithm encryptionAlgorithm) {
332
            this.encryptionAlgorithm = encryptionAlgorithm;
78✔
333
            return this;
78✔
334
        }
335

336
        public HyperwalletEncryptionBuilder signAlgorithm(JWSAlgorithm signAlgorithm) {
337
            this.signAlgorithm = signAlgorithm;
76✔
338
            return this;
76✔
339
        }
340

341
        public HyperwalletEncryptionBuilder encryptionMethod(EncryptionMethod encryptionMethod) {
342
            this.encryptionMethod = encryptionMethod;
75✔
343
            return this;
75✔
344
        }
345

346
        public HyperwalletEncryptionBuilder clientPrivateKeySetLocation(String clientPrivateKeySetLocation) {
347
            this.clientPrivateKeySetLocation = clientPrivateKeySetLocation;
110✔
348
            return this;
110✔
349
        }
350

351
        public HyperwalletEncryptionBuilder hyperwalletKeySetLocation(String hyperwalletKeySetLocation) {
352
            this.hyperwalletKeySetLocation = hyperwalletKeySetLocation;
110✔
353
            return this;
110✔
354
        }
355

356
        public HyperwalletEncryptionBuilder jwsExpirationMinutes(Integer jwsExpirationMinutes) {
357
            this.jwsExpirationMinutes = jwsExpirationMinutes;
2✔
358
            return this;
2✔
359
        }
360

361
        public HyperwalletEncryptionBuilder proxy(Proxy proxy) {
362
            this.proxy = proxy;
2✔
363
            return this;
2✔
364
        }
365

366
        public HyperwalletEncryptionBuilder proxyUsername(String proxyUsername) {
367
            this.proxyUsername = proxyUsername;
2✔
368
            return this;
2✔
369
        }
370

371
        public HyperwalletEncryptionBuilder proxyPassword(String proxyPassword) {
372
            this.proxyPassword = proxyPassword;
2✔
373
            return this;
2✔
374
        }
375

376
        public HyperwalletEncryption build() {
377
            HyperwalletEncryption hyperwalletEncryption = new HyperwalletEncryption(encryptionAlgorithm, signAlgorithm, encryptionMethod,
126✔
378
                    clientPrivateKeySetLocation, hyperwalletKeySetLocation, jwsExpirationMinutes);
379
            if (proxy != null) {
122✔
380
                hyperwalletEncryption.setProxy(proxy);
1✔
381
            }
382
            if (proxyUsername != null) {
122✔
383
                hyperwalletEncryption.setProxyUsername(proxyUsername);
1✔
384
            }
385
            if (proxyPassword != null) {
122✔
386
                hyperwalletEncryption.setProxyPassword(proxyPassword);
1✔
387
            }
388
            return hyperwalletEncryption;
122✔
389
        }
390
    }
391
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc