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

pgpainless / sop-java / #65

12 Jan 2026 11:37AM UTC coverage: 57.95% (-0.3%) from 58.291%
#65

push

other

vanitasvitae
Add comments

2103 of 3629 relevant lines covered (57.95%)

0.58 hits per line

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

25.91
/sop-java-testfixtures/src/main/java/sop/testsuite/operation/EncryptDecryptTest.java
1
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
2
//
3
// SPDX-License-Identifier: Apache-2.0
4

5
package sop.testsuite.operation;
6

7
import org.junit.jupiter.api.condition.EnabledIf;
8
import org.junit.jupiter.params.ParameterizedTest;
9
import org.junit.jupiter.params.provider.Arguments;
10
import org.junit.jupiter.params.provider.MethodSource;
11
import sop.ByteArrayAndResult;
12
import sop.DecryptionResult;
13
import sop.EncryptionResult;
14
import sop.Profile;
15
import sop.SOP;
16
import sop.SessionKey;
17
import sop.Verification;
18
import sop.enums.EncryptAs;
19
import sop.enums.EncryptFor;
20
import sop.enums.SignatureMode;
21
import sop.exception.SOPGPException;
22
import sop.operation.Decrypt;
23
import sop.operation.Encrypt;
24
import sop.testsuite.TestData;
25
import sop.testsuite.assertions.VerificationListAssert;
26
import sop.util.Optional;
27
import sop.util.UTCUtil;
28

29
import java.io.IOException;
30
import java.nio.charset.StandardCharsets;
31
import java.text.ParseException;
32
import java.util.ArrayList;
33
import java.util.Date;
34
import java.util.List;
35
import java.util.stream.Stream;
36

37
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
38
import static org.junit.jupiter.api.Assertions.assertEquals;
39
import static org.junit.jupiter.api.Assertions.assertNotNull;
40
import static org.junit.jupiter.api.Assertions.assertThrows;
41

42
@EnabledIf("sop.testsuite.operation.AbstractSOPTest#hasBackends")
43
public class EncryptDecryptTest extends AbstractSOPTest {
1✔
44

45
    static Stream<Arguments> provideInstances() {
46
        return provideBackends();
1✔
47
    }
48

49
    @ParameterizedTest
50
    @MethodSource("provideInstances")
51
    public void encryptDecryptRoundTripPasswordTest(SOP sop) throws IOException {
52
        byte[] message = TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8);
1✔
53
        ByteArrayAndResult<EncryptionResult> encResult = assumeSupported(sop::encrypt)
1✔
54
                .withPassword("sw0rdf1sh")
1✔
55
                .plaintext(message)
1✔
56
                .toByteArrayAndResult();
×
57

58
        byte[] ciphertext = encResult.getBytes();
×
59
        Optional<SessionKey> encSessionKey = encResult.getResult().getSessionKey();
×
60

61
        ByteArrayAndResult<DecryptionResult> decResult = assumeSupported(sop::decrypt)
×
62
                .withPassword("sw0rdf1sh")
×
63
                .ciphertext(ciphertext)
×
64
                .toByteArrayAndResult();
×
65

66
        byte[] plaintext = decResult.getBytes();
×
67
        Optional<SessionKey> decSessionKey = decResult.getResult().getSessionKey();
×
68

69
        assertArrayEquals(message, plaintext, "Decrypted plaintext does not match original plaintext.");
×
70
        if (encSessionKey.isPresent() && decSessionKey.isPresent()) {
×
71
            assertEquals(encSessionKey.get(), decSessionKey.get(),
×
72
                    "Extracted Session Key mismatch.");
73
        }
74
    }
×
75

76
    @ParameterizedTest
77
    @MethodSource("provideInstances")
78
    public void encryptDecryptRoundTripAliceTest(SOP sop) throws IOException {
79
        byte[] message = TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8);
1✔
80
        byte[] ciphertext = assumeSupported(sop::encrypt)
1✔
81
                .withCert(TestData.ALICE_CERT.getBytes(StandardCharsets.UTF_8))
1✔
82
                .plaintext(message)
1✔
83
                .toByteArrayAndResult()
×
84
                .getBytes();
×
85

86
        ByteArrayAndResult<DecryptionResult> bytesAndResult = assumeSupported(sop::decrypt)
×
87
                .withKey(TestData.ALICE_KEY.getBytes(StandardCharsets.UTF_8))
×
88
                .ciphertext(ciphertext)
×
89
                .toByteArrayAndResult();
×
90

91
        byte[] plaintext = bytesAndResult.getBytes();
×
92
        assertArrayEquals(message, plaintext, "Decrypted plaintext does not match original plaintext.");
×
93

94
        DecryptionResult result = bytesAndResult.getResult();
×
95
        if (result.getSessionKey().isPresent()) {
×
96
            assertNotNull(result.getSessionKey().get(), "Session key MUST NOT be null.");
×
97
        }
98
    }
×
99

100
    @ParameterizedTest
101
    @MethodSource("provideInstances")
102
    public void encryptDecryptRoundTripBobTest(SOP sop) throws IOException {
103
        byte[] message = TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8);
1✔
104
        byte[] ciphertext = assumeSupported(sop::encrypt)
1✔
105
                .withCert(TestData.BOB_CERT.getBytes(StandardCharsets.UTF_8))
1✔
106
                .plaintext(message)
1✔
107
                .toByteArrayAndResult()
×
108
                .getBytes();
×
109

110
        byte[] plaintext = assumeSupported(sop::decrypt)
×
111
                .withKey(TestData.BOB_KEY.getBytes(StandardCharsets.UTF_8))
×
112
                .ciphertext(ciphertext)
×
113
                .toByteArrayAndResult()
×
114
                .getBytes();
×
115

116
        assertArrayEquals(message, plaintext, "Decrypted plaintext does not match original plaintext.");
×
117
    }
×
118

119
    @ParameterizedTest
120
    @MethodSource("provideInstances")
121
    public void encryptDecryptRoundTripCarolTest(SOP sop) throws IOException {
122
        byte[] message = TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8);
1✔
123
        byte[] ciphertext = assumeSupported(sop::encrypt)
1✔
124
                .withCert(TestData.CAROL_CERT.getBytes(StandardCharsets.UTF_8))
1✔
125
                .plaintext(message)
1✔
126
                .toByteArrayAndResult()
×
127
                .getBytes();
×
128

129
        byte[] plaintext = assumeSupported(sop::decrypt)
×
130
                .withKey(TestData.CAROL_KEY.getBytes(StandardCharsets.UTF_8))
×
131
                .ciphertext(ciphertext)
×
132
                .toByteArrayAndResult()
×
133
                .getBytes();
×
134

135
        assertArrayEquals(message, plaintext, "Decrypted plaintext does not match original plaintext.");
×
136
    }
×
137

138
    @ParameterizedTest
139
    @MethodSource("provideInstances")
140
    public void encryptNoArmorThenArmorThenDecryptRoundTrip(SOP sop) throws IOException {
141
        byte[] message = TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8);
1✔
142
        byte[] ciphertext = assumeSupported(sop::encrypt)
1✔
143
                .withCert(TestData.ALICE_CERT.getBytes(StandardCharsets.UTF_8))
1✔
144
                .noArmor()
1✔
145
                .plaintext(message)
1✔
146
                .toByteArrayAndResult()
×
147
                .getBytes();
×
148

149
        byte[] armored = assumeSupported(sop::armor)
×
150
                .data(ciphertext)
×
151
                .getBytes();
×
152

153
        ByteArrayAndResult<DecryptionResult> bytesAndResult = assumeSupported(sop::decrypt)
×
154
                .withKey(TestData.ALICE_KEY.getBytes(StandardCharsets.UTF_8))
×
155
                .ciphertext(armored)
×
156
                .toByteArrayAndResult();
×
157

158
        byte[] plaintext = bytesAndResult.getBytes();
×
159
        assertArrayEquals(message, plaintext, "Decrypted plaintext does not match original plaintext.");
×
160
    }
×
161

162
    @ParameterizedTest
163
    @MethodSource("provideInstances")
164
    public void encryptSignDecryptVerifyRoundTripAliceTest(SOP sop) throws IOException {
165
        byte[] message = TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8);
1✔
166
        byte[] ciphertext = assumeSupported(sop::encrypt)
1✔
167
                .withCert(TestData.ALICE_CERT.getBytes(StandardCharsets.UTF_8))
1✔
168
                .signWith(TestData.ALICE_KEY.getBytes(StandardCharsets.UTF_8))
1✔
169
                .mode(EncryptAs.binary)
1✔
170
                .plaintext(message)
1✔
171
                .toByteArrayAndResult()
×
172
                .getBytes();
×
173

174
        ByteArrayAndResult<DecryptionResult> bytesAndResult = assumeSupported(sop::decrypt)
×
175
                .withKey(TestData.ALICE_KEY.getBytes(StandardCharsets.UTF_8))
×
176
                .verifyWithCert(TestData.ALICE_CERT.getBytes(StandardCharsets.UTF_8))
×
177
                .ciphertext(ciphertext)
×
178
                .toByteArrayAndResult();
×
179

180
        byte[] plaintext = bytesAndResult.getBytes();
×
181
        assertArrayEquals(message, plaintext, "Decrypted plaintext does not match original plaintext.");
×
182

183
        DecryptionResult result = bytesAndResult.getResult();
×
184
        if (result.getSessionKey().isPresent()) {
×
185
            assertNotNull(result.getSessionKey().get(), "Session key MUST NOT be null.");
×
186
        }
187

188
        List<Verification> verificationList = result.getVerifications();
×
189
        VerificationListAssert.assertThatVerificationList(verificationList)
×
190
                .isNotEmpty()
×
191
                .hasSingleItem()
×
192
                .issuedBy(TestData.ALICE_SIGNING_FINGERPRINT, TestData.ALICE_PRIMARY_FINGERPRINT)
×
193
                .hasModeOrNull(SignatureMode.binary);
×
194
    }
×
195

196
    @ParameterizedTest
197
    @MethodSource("provideInstances")
198
    public void encryptSignAsTextDecryptVerifyRoundTripAliceTest(SOP sop) throws IOException {
199
        byte[] message = TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8);
1✔
200
        byte[] ciphertext = assumeSupported(sop::encrypt)
1✔
201
                .withCert(TestData.ALICE_CERT.getBytes(StandardCharsets.UTF_8))
1✔
202
                .signWith(TestData.ALICE_KEY.getBytes(StandardCharsets.UTF_8))
1✔
203
                .mode(EncryptAs.text)
1✔
204
                .plaintext(message)
1✔
205
                .toByteArrayAndResult()
×
206
                .getBytes();
×
207

208
        ByteArrayAndResult<DecryptionResult> bytesAndResult = assumeSupported(sop::decrypt)
×
209
                .withKey(TestData.ALICE_KEY.getBytes(StandardCharsets.UTF_8))
×
210
                .verifyWithCert(TestData.ALICE_CERT.getBytes(StandardCharsets.UTF_8))
×
211
                .ciphertext(ciphertext)
×
212
                .toByteArrayAndResult();
×
213

214
        byte[] plaintext = bytesAndResult.getBytes();
×
215
        assertArrayEquals(message, plaintext, "Decrypted plaintext does not match original plaintext.");
×
216

217
        DecryptionResult result = bytesAndResult.getResult();
×
218
        assertNotNull(result.getSessionKey().get());
×
219

220
        List<Verification> verificationList = result.getVerifications();
×
221
        VerificationListAssert.assertThatVerificationList(verificationList)
×
222
                .hasSingleItem()
×
223
                .issuedBy(TestData.ALICE_SIGNING_FINGERPRINT, TestData.ALICE_PRIMARY_FINGERPRINT)
×
224
                .hasModeOrNull(SignatureMode.text);
×
225
    }
×
226

227
    @ParameterizedTest
228
    @MethodSource("provideInstances")
229
    public void encryptSignDecryptVerifyRoundTripWithFreshEncryptedKeyTest(SOP sop) throws IOException {
230
        byte[] keyPassword = "sw0rdf1sh".getBytes(StandardCharsets.UTF_8);
1✔
231
        byte[] key = assumeSupported(sop::generateKey)
1✔
232
                .withKeyPassword(keyPassword)
1✔
233
                .userId("Alice <alice@openpgp.org>")
1✔
234
                .generate()
1✔
235
                .getBytes();
×
236
        byte[] cert = assumeSupported(sop::extractCert)
×
237
                .key(key)
×
238
                .getBytes();
×
239

240
        byte[] message = "Hello, World!\n".getBytes(StandardCharsets.UTF_8);
×
241
        byte[] ciphertext = assumeSupported(sop::encrypt)
×
242
                .withCert(cert)
×
243
                .signWith(key)
×
244
                .withKeyPassword(keyPassword)
×
245
                .plaintext(message)
×
246
                .toByteArrayAndResult()
×
247
                .getBytes();
×
248

249
        ByteArrayAndResult<DecryptionResult> bytesAndResult = assumeSupported(sop::decrypt)
×
250
                .withKey(key)
×
251
                .withKeyPassword(keyPassword)
×
252
                .verifyWithCert(cert)
×
253
                .ciphertext(ciphertext)
×
254
                .toByteArrayAndResult();
×
255

256
        List<Verification> verifications = bytesAndResult.getResult().getVerifications();
×
257
        VerificationListAssert.assertThatVerificationList(verifications)
×
258
                .isNotEmpty()
×
259
                .hasSingleItem();
×
260
    }
×
261

262
    @ParameterizedTest
263
    @MethodSource("provideInstances")
264
    public void decryptVerifyNotAfterTest(SOP sop) throws ParseException {
265
        byte[] message = ("-----BEGIN PGP MESSAGE-----\n" +
1✔
266
                "\n" +
267
                "wV4DR2b2udXyHrYSAQdAwlOwwyxFDJta5+H9abgSj8jum9v7etUc9usdrElESmow\n" +
268
                "2Hka48AFVfOezYh0OFn9R8+DMcpuE+e4nw3XnnX5nKs/j3AC2IW6zRHUkRcF3ZCq\n" +
269
                "0sBNAfjnTYCMjuBmqdcCLzaZT4Hadnpg6neP1UecT/jP14maGfv8nwt0IDGR0Bik\n" +
270
                "0WC/UJLpWyJ/6TgRrA5hNfANVnfiFBzIiThiVBRWPT2StHr2cOAvFxQK4Uk07rK9\n" +
271
                "9aTUak8FpML+QA83U8I3qOk4QbzGVBP+IDJ+AKmvDz+0V+9kUhKp+8vyXsBmo9c3\n" +
272
                "SAXjhFSiPQkU7ORsc6gQHL9+KPOU+W2poPK87H3cmaGiusnXMeLXLIUbkBUJTswd\n" +
273
                "JNrA2yAkTTFP9QabsdcdTGoeYamq1c29kHF3GOTTcEqXw4WWXngcF7Kbcf435kkL\n" +
274
                "4iSJnCaxTPftKUxmiGqMqLef7ICVnq/lz3HrH1VD54s=\n" +
275
                "=Ebi3\n" +
276
                "-----END PGP MESSAGE-----").getBytes(StandardCharsets.UTF_8);
1✔
277
        Date signatureDate = UTCUtil.parseUTCDate("2023-01-13T16:09:32Z");
1✔
278

279
        Date beforeSignature = new Date(signatureDate.getTime() - 1000); // 1 sec before signing date
1✔
280

281
        assertThrows(SOPGPException.NoSignature.class, () -> {
×
282
            ByteArrayAndResult<DecryptionResult> bytesAndResult = assumeSupported(sop::decrypt)
1✔
283
                    .withKey(TestData.ALICE_KEY.getBytes(StandardCharsets.UTF_8))
1✔
284
                    .verifyWithCert(TestData.ALICE_CERT.getBytes(StandardCharsets.UTF_8))
1✔
285
                    .verifyNotAfter(beforeSignature)
1✔
286
                    .ciphertext(message)
1✔
287
                    .toByteArrayAndResult();
×
288

289
            // Some implementations do not throw NoSignature and instead return an empty list.
290
            if (bytesAndResult.getResult().getVerifications().isEmpty()) {
×
291
                throw new SOPGPException.NoSignature("No verifiable signature found.");
×
292
            }
293
        });
×
294
    }
×
295

296
    @ParameterizedTest
297
    @MethodSource("provideInstances")
298
    public void decryptVerifyNotBeforeTest(SOP sop) throws ParseException {
299
        byte[] message = ("-----BEGIN PGP MESSAGE-----\n" +
1✔
300
                "\n" +
301
                "wV4DR2b2udXyHrYSAQdAwlOwwyxFDJta5+H9abgSj8jum9v7etUc9usdrElESmow\n" +
302
                "2Hka48AFVfOezYh0OFn9R8+DMcpuE+e4nw3XnnX5nKs/j3AC2IW6zRHUkRcF3ZCq\n" +
303
                "0sBNAfjnTYCMjuBmqdcCLzaZT4Hadnpg6neP1UecT/jP14maGfv8nwt0IDGR0Bik\n" +
304
                "0WC/UJLpWyJ/6TgRrA5hNfANVnfiFBzIiThiVBRWPT2StHr2cOAvFxQK4Uk07rK9\n" +
305
                "9aTUak8FpML+QA83U8I3qOk4QbzGVBP+IDJ+AKmvDz+0V+9kUhKp+8vyXsBmo9c3\n" +
306
                "SAXjhFSiPQkU7ORsc6gQHL9+KPOU+W2poPK87H3cmaGiusnXMeLXLIUbkBUJTswd\n" +
307
                "JNrA2yAkTTFP9QabsdcdTGoeYamq1c29kHF3GOTTcEqXw4WWXngcF7Kbcf435kkL\n" +
308
                "4iSJnCaxTPftKUxmiGqMqLef7ICVnq/lz3HrH1VD54s=\n" +
309
                "=Ebi3\n" +
310
                "-----END PGP MESSAGE-----").getBytes(StandardCharsets.UTF_8);
1✔
311
        Date signatureDate = UTCUtil.parseUTCDate("2023-01-13T16:09:32Z");
1✔
312

313
        Date afterSignature = new Date(signatureDate.getTime() + 1000); // 1 sec after signing date
1✔
314

315
        assertThrows(SOPGPException.NoSignature.class, () -> {
×
316
            ByteArrayAndResult<DecryptionResult> bytesAndResult = assumeSupported(sop::decrypt)
1✔
317
                    .withKey(TestData.ALICE_KEY.getBytes(StandardCharsets.UTF_8))
1✔
318
                    .verifyWithCert(TestData.ALICE_CERT.getBytes(StandardCharsets.UTF_8))
1✔
319
                    .verifyNotBefore(afterSignature)
1✔
320
                    .ciphertext(message)
1✔
321
                    .toByteArrayAndResult();
×
322

323
            // Some implementations do not throw NoSignature and instead return an empty list.
324
            if (bytesAndResult.getResult().getVerifications().isEmpty()) {
×
325
                throw new SOPGPException.NoSignature("No verifiable signature found.");
×
326
            }
327
        });
×
328
    }
×
329

330
    @ParameterizedTest
331
    @MethodSource("provideInstances")
332
    public void missingArgsTest(SOP sop) {
333
        byte[] message = TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8);
1✔
334

335
        assertThrows(SOPGPException.MissingArg.class, () -> assumeSupported(sop::encrypt)
1✔
336
                .plaintext(message)
1✔
337
                .toByteArrayAndResult()
×
338
                .getBytes());
×
339
    }
×
340

341
    @ParameterizedTest
342
    @MethodSource("provideInstances")
343
    public void passingSecretKeysForPublicKeysFails(SOP sop) {
344
        assertThrows(SOPGPException.BadData.class, () ->
×
345
                assumeSupported(sop::encrypt)
1✔
346
                        .withCert(TestData.ALICE_KEY.getBytes(StandardCharsets.UTF_8))
1✔
347
                        .plaintext(TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8))
1✔
348
                        .toByteArrayAndResult()
×
349
                        .getBytes());
×
350
    }
×
351

352
    @ParameterizedTest
353
    @MethodSource("provideInstances")
354
    public void encryptDecryptWithAllSupportedKeyGenerationProfiles(SOP sop) throws IOException {
355
        List<Profile> profiles = assumeSupported(sop::listProfiles).generateKey();
×
356

357
        List<byte[]> keys = new ArrayList<>();
×
358
        List<byte[]> certs = new ArrayList<>();
×
359
        for (Profile p : profiles) {
×
360
            byte[] k = assumeSupported(sop::generateKey)
×
361
                    .profile(p)
×
362
                    .userId(p.getName())
×
363
                    .generate()
×
364
                    .getBytes();
×
365
            keys.add(k);
×
366

367
            byte[] c = assumeSupported(sop::extractCert)
×
368
                    .key(k)
×
369
                    .getBytes();
×
370
            certs.add(c);
×
371
        }
×
372

373
        byte[] plaintext = "Hello, World!\n".getBytes();
×
374

375
        Encrypt encrypt = assumeSupported(sop::encrypt);
×
376
        for (byte[] c : certs) {
×
377
            encrypt.withCert(c);
×
378
        }
×
379
        for (byte[] k : keys) {
×
380
            encrypt.signWith(k);
×
381
        }
×
382

383
        ByteArrayAndResult<EncryptionResult> encRes = encrypt.plaintext(plaintext)
×
384
                .toByteArrayAndResult();
×
385
        EncryptionResult eResult = encRes.getResult();
×
386
        byte[] ciphertext = encRes.getBytes();
×
387

388
        for (byte[] k : keys) {
×
389
            Decrypt decrypt = assumeSupported(sop::decrypt)
×
390
                    .withKey(k);
×
391
            for (byte[] c : certs) {
×
392
                decrypt.verifyWithCert(c);
×
393
            }
×
394
            ByteArrayAndResult<DecryptionResult> decRes = decrypt.ciphertext(ciphertext)
×
395
                    .toByteArrayAndResult();
×
396
            DecryptionResult dResult = decRes.getResult();
×
397
            byte[] decPlaintext = decRes.getBytes();
×
398
            assertArrayEquals(plaintext, decPlaintext, "Decrypted plaintext does not match original plaintext.");
×
399
            assertEquals(certs.size(), dResult.getVerifications().size());
×
400
        }
×
401
    }
×
402

403
    @ParameterizedTest
404
    @MethodSource("provideInstances")
405
    public void encryptForPurpose(SOP sop) throws IOException {
406
        String CERT = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
1✔
407
                "Comment: 88BF 5516 C226 5B7D 1817  03E6 1FF0 DE1E AF8B 379F\n" +
408
                "\n" +
409
                "mCYEaVxG2BvmBuO3v5cDQQCuGnAIuaeP0frpw7mutcMQwPkGuuAKUMKSBB8bCgA+\n" +
410
                "FqEEiL9VFsImW30YFwPmH/DeHq+LN58FgmlcRtgCngkFlQoJCAsFlgIDAQAEiwkI\n" +
411
                "BwknCQEJAgkDCAECmwEACgkQH/DeHq+LN5/NVHbqH098dr34p9KVQQNLXr8CITqP\n" +
412
                "vLTkijVXyfZg6Lz1krs3EgEvc8nz3evyYj5xJI+Hg1kHb+ctB5myyTyEtge4JgRp\n" +
413
                "XEbYG52SLEi5Biq9vn1pFgrozM2QuCqkwXtOr/0ASs0b3t20wsAnBBgbCgCTFqEE\n" +
414
                "iL9VFsImW30YFwPmH/DeHq+LN58FgmlcRtgCmwJyoAQZGwoAHRahBGp6EAtdr26T\n" +
415
                "x4sGLa+TQ+g71BlpBYJpXEbYAAoJEK+TQ+g71BlpkJ4VPAQeTXN88wXzLloW2WYP\n" +
416
                "5w3w7Js4csGE5OynUupCNwUBcIfC+FBMuUdgqjczw4xKRLbZMgp5YLr8Ve3pG48L\n" +
417
                "AAoJEB/w3h6vizefXrbECKbGBPh+c3+fFG3Au0gzkRMCsZsMaQaRWlQ1E2P/VWlo\n" +
418
                "xy4JF5nCA6bSC+sFl+DTbwpgvdQlIILR9O386EcHuCYEaVxG2Blrm96fHzaN1JmO\n" +
419
                "uhU0OMbiDMBYKOL3Iup+TQWzx897CMJ0BBgbCgAgFqEEiL9VFsImW30YFwPmH/De\n" +
420
                "Hq+LN58FgmlcRtgCmwQACgkQH/DeHq+LN5/wOkjl+MJktOsh+COv4tAhSu2kR0iw\n" +
421
                "rdY4IAEp7jlnZfx0BVMnVURSrZSge3Zw2vbQQe864GA3Y4le4CWFKm2QAwG4JgRp\n" +
422
                "XEbYGUzlbIju0H0KDcLmLXsXp7CCLmkcnSjNAj9WTRW7GCJownQEGBsKACAWoQSI\n" +
423
                "v1UWwiZbfRgXA+Yf8N4er4s3nwWCaVxG2AKbCAAKCRAf8N4er4s3n4+EpHlXYNzD\n" +
424
                "I2OT9NpobaalDbmDMuvIu/81Uoxv+pJLkrMV+WW5be27HrH6w7YTH1TngILr4V2e\n" +
425
                "jSB2HhjClk4YBw==\n" +
426
                "=3S3M\n" +
427
                "-----END PGP PUBLIC KEY BLOCK-----";
428
        String KEY_ONLY_STORAGE = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
1✔
429
                "Comment: 88BF 5516 C226 5B7D 1817  03E6 1FF0 DE1E AF8B 379F\n" +
430
                "\n" +
431
                "lEkEaVxG2BvmBuO3v5cDQQCuGnAIuaeP0frpw7mutcMQwPkGuuAKUAA0eJO2aUrG\n" +
432
                "NwfD+W5mn/EHzossHrQPa0jPzERJ1m7kkA/MwpIEHxsKAD4WoQSIv1UWwiZbfRgX\n" +
433
                "A+Yf8N4er4s3nwWCaVxG2AKeCQWVCgkICwWWAgMBAASLCQgHCScJAQkCCQMIAQKb\n" +
434
                "AQAKCRAf8N4er4s3n81UduofT3x2vfin0pVBA0tevwIhOo+8tOSKNVfJ9mDovPWS\n" +
435
                "uzcSAS9zyfPd6/JiPnEkj4eDWQdv5y0HmbLJPIS2B5xJBGlcRtgbnZIsSLkGKr2+\n" +
436
                "fWkWCujMzZC4KqTBe06v/QBKzRve3bQA80/esWlwPguauRAay+kli/gw/SRhTAK7\n" +
437
                "n1k63W1vkxkPHsLAJwQYGwoAkxahBIi/VRbCJlt9GBcD5h/w3h6vizefBYJpXEbY\n" +
438
                "ApsCcqAEGRsKAB0WoQRqehALXa9uk8eLBi2vk0PoO9QZaQWCaVxG2AAKCRCvk0Po\n" +
439
                "O9QZaZCeFTwEHk1zfPMF8y5aFtlmD+cN8OybOHLBhOTsp1LqQjcFAXCHwvhQTLlH\n" +
440
                "YKo3M8OMSkS22TIKeWC6/FXt6RuPCwAKCRAf8N4er4s3n162xAimxgT4fnN/nxRt\n" +
441
                "wLtIM5ETArGbDGkGkVpUNRNj/1VpaMcuCReZwgOm0gvrBZfg028KYL3UJSCC0fTt\n" +
442
                "/OhHB5xJBGlcRtgZTOVsiO7QfQoNwuYtexensIIuaRydKM0CP1ZNFbsYImgAUDFh\n" +
443
                "CkwFwBaiasW9oHJQd1TALWOtj0TjQo9tEp1zxmQOC8J0BBgbCgAgFqEEiL9VFsIm\n" +
444
                "W30YFwPmH/DeHq+LN58FgmlcRtgCmwgACgkQH/DeHq+LN5+PhKR5V2DcwyNjk/Ta\n" +
445
                "aG2mpQ25gzLryLv/NVKMb/qSS5KzFflluW3tux6x+sO2Ex9U54CC6+Fdno0gdh4Y\n" +
446
                "wpZOGAc=\n" +
447
                "=oK8k\n" +
448
                "-----END PGP PRIVATE KEY BLOCK-----";
449
        String KEY_ONLY_COMMS = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
1✔
450
                "Comment: 88BF 5516 C226 5B7D 1817  03E6 1FF0 DE1E AF8B 379F\n" +
451
                "\n" +
452
                "lEkEaVxG2BvmBuO3v5cDQQCuGnAIuaeP0frpw7mutcMQwPkGuuAKUAA0eJO2aUrG\n" +
453
                "NwfD+W5mn/EHzossHrQPa0jPzERJ1m7kkA/MwpIEHxsKAD4WoQSIv1UWwiZbfRgX\n" +
454
                "A+Yf8N4er4s3nwWCaVxG2AKeCQWVCgkICwWWAgMBAASLCQgHCScJAQkCCQMIAQKb\n" +
455
                "AQAKCRAf8N4er4s3n81UduofT3x2vfin0pVBA0tevwIhOo+8tOSKNVfJ9mDovPWS\n" +
456
                "uzcSAS9zyfPd6/JiPnEkj4eDWQdv5y0HmbLJPIS2B5xJBGlcRtgbnZIsSLkGKr2+\n" +
457
                "fWkWCujMzZC4KqTBe06v/QBKzRve3bQA80/esWlwPguauRAay+kli/gw/SRhTAK7\n" +
458
                "n1k63W1vkxkPHsLAJwQYGwoAkxahBIi/VRbCJlt9GBcD5h/w3h6vizefBYJpXEbY\n" +
459
                "ApsCcqAEGRsKAB0WoQRqehALXa9uk8eLBi2vk0PoO9QZaQWCaVxG2AAKCRCvk0Po\n" +
460
                "O9QZaZCeFTwEHk1zfPMF8y5aFtlmD+cN8OybOHLBhOTsp1LqQjcFAXCHwvhQTLlH\n" +
461
                "YKo3M8OMSkS22TIKeWC6/FXt6RuPCwAKCRAf8N4er4s3n162xAimxgT4fnN/nxRt\n" +
462
                "wLtIM5ETArGbDGkGkVpUNRNj/1VpaMcuCReZwgOm0gvrBZfg028KYL3UJSCC0fTt\n" +
463
                "/OhHB5xJBGlcRtgZa5venx82jdSZjroVNDjG4gzAWCji9yLqfk0Fs8fPewgAILUp\n" +
464
                "R5Ihy+ooSi/6ZnicHsld84qTXLpmeKZMVYhqDm4Ov8J0BBgbCgAgFqEEiL9VFsIm\n" +
465
                "W30YFwPmH/DeHq+LN58FgmlcRtgCmwQACgkQH/DeHq+LN5/wOkjl+MJktOsh+COv\n" +
466
                "4tAhSu2kR0iwrdY4IAEp7jlnZfx0BVMnVURSrZSge3Zw2vbQQe864GA3Y4le4CWF\n" +
467
                "Km2QAwE=\n" +
468
                "=6hyI\n" +
469
                "-----END PGP PRIVATE KEY BLOCK-----";
470

471
        // Encrypt message only for the storage encryption subkey
472
        byte[] forStorage = sop.encrypt()
1✔
473
                .encryptFor(EncryptFor.storage)
1✔
474
                .withCert(CERT.getBytes(StandardCharsets.UTF_8))
1✔
475
                .plaintext(TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8))
1✔
476
                .toByteArrayAndResult()
×
477
                .getBytes();
×
478

479
        // Storage enc key can decrypt
480
        assertArrayEquals(
×
481
                TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8),
×
482
                sop.decrypt()
×
483
                        .withKey(KEY_ONLY_STORAGE.getBytes(StandardCharsets.UTF_8))
×
484
                        .ciphertext(forStorage)
×
485
                        .toByteArrayAndResult()
×
486
                        .getBytes());
×
487
        // Comms only subkey cannot decrypt
488
        assertThrows(SOPGPException.CannotDecrypt.class, () -> sop.decrypt()
×
489
                .withKey(KEY_ONLY_COMMS.getBytes(StandardCharsets.UTF_8))
×
490
                .ciphertext(forStorage));
×
491

492
        // Encrypt message only for the comms encryption subkey
493
        byte[] forComms = sop.encrypt()
×
494
                .encryptFor(EncryptFor.communications)
×
495
                .withCert(CERT.getBytes(StandardCharsets.UTF_8))
×
496
                .plaintext(TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8))
×
497
                .toByteArrayAndResult()
×
498
                .getBytes();
×
499

500
        // Comms enc key can decrypt
501
        assertArrayEquals(
×
502
                TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8),
×
503
                sop.decrypt()
×
504
                        .withKey(KEY_ONLY_COMMS.getBytes(StandardCharsets.UTF_8))
×
505
                        .ciphertext(forComms)
×
506
                        .toByteArrayAndResult()
×
507
                        .getBytes());
×
508
        // Storage only subkey cannot decrypt
509
        assertThrows(SOPGPException.CannotDecrypt.class, () -> sop.decrypt()
×
510
                .withKey(KEY_ONLY_STORAGE.getBytes(StandardCharsets.UTF_8))
×
511
                .ciphertext(forComms));
×
512
    }
×
513
}
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

© 2026 Coveralls, Inc