diff --git a/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/DetachedVerifyTest.java b/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/DetachedVerifyTest.java deleted file mode 100644 index 8ca11c0..0000000 --- a/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/DetachedVerifyTest.java +++ /dev/null @@ -1,97 +0,0 @@ -package org.pgpainless.bouncycastle.sop; - -import sop.Verification; -import sop.operation.DetachedVerify; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.SequenceInputStream; -import java.nio.charset.StandardCharsets; -import java.util.List; - -public class DetachedVerifyTest { - - static String SIG = "-----BEGIN PGP SIGNATURE-----\n" + - "\n" + - "wnUEABYKAB0WIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCZ1mi4QAKCRDyMVUMT0fj\n" + - "jun3AP9ZiRPXlow7ywR3D7nC4/GGUC+PueZ1GkcXDtE8zbAiTgEArkOQO8qhC0QG\n" + - "K6TKEPJM07Zie/kgcGyh8HfSpbunwAXCwPMEAAEKAB0WIQTRpm4aI7GCyZgPeIz7\n" + - "/MgqAV5zMAUCZ1mi4QAKCRD7/MgqAV5zMI51C/sFv6vjBbxdgNMp1qwJ3AqMhbtV\n" + - "WJ7z8HJraT+dW0VKYmib7H7lL3x3gTC85VaOWGU3mqhxZLQVi6j99pbbZjGXJADF\n" + - "e5E1S+tp6js56OI4fY/kchLMcz1JxgjSJPJnlyEXy5C2/2RlI1JmpUn6Y8YOlNhZ\n" + - "Vbov36QB2ILt+k62ElQeHgWQYqWWMmxmdk9wipNKYfBoQNVwJNICDjI+XlwL4cGw\n" + - "nyg3cdgkKXikPFEBhlApI5HmX4jOv68gReEMMTaZoQoRD8kzg9m7KC6BqHpG0TAZ\n" + - "Bz5TzZrxW1LdXj/tKqNONcldHHzO4mZIUkdOowGMBmEf0kZ2nVHZGaWE1T7IqJPo\n" + - "cIPtZB+EpDCcPQN35ekC0B1ilDd07IulNHOdbhI7xu0/eQb6dGpkzkak3OpWbGtX\n" + - "rKfxljqLFTnnJfM95UlrlCWwEplRZ2q4QhXi8/mRM/Szz5D3KGd7hnLRnr42J4Dr\n" + - "j/XWKoSXmFZ5t6CBq7BgubE1uO2ToRWOlas+jNE=\n" + - "=bJ8+\n" + - "-----END PGP SIGNATURE-----"; - static String ALICE = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + - "Comment: Alice's OpenPGP certificate\n" + - "\n" + - "mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U\n" + - "b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE\n" + - "ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy\n" + - "MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO\n" + - "dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4\n" + - "OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s\n" + - "E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb\n" + - "DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn\n" + - "0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE=\n" + - "=iIGO\n" + - "-----END PGP PUBLIC KEY BLOCK-----"; - static String BOB = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + - "Comment: Bob's OpenPGP certificate\n" + - "\n" + - "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\n" + - "NEJd3XZRzaXZE2aAMQ==\n" + - "=NXei\n" + - "-----END PGP PUBLIC KEY BLOCK-----"; - static String MSG = "Hello World :)"; - - public static void main(String[] args) throws IOException { - BouncyCastleSOP sop = new BouncyCastleSOP(); - ByteArrayInputStream sigIn = new ByteArrayInputStream(SIG.getBytes(StandardCharsets.UTF_8)); - DetachedVerify verify = sop.detachedVerify(); - verify.signatures(sigIn); - verify.cert(new ByteArrayInputStream(ALICE.getBytes(StandardCharsets.UTF_8))) - .cert(new ByteArrayInputStream(BOB.getBytes(StandardCharsets.UTF_8))); - List verif = verify.data(new ByteArrayInputStream(MSG.getBytes(StandardCharsets.UTF_8))); - } -} diff --git a/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDecrypt.java b/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDecrypt.java index 238dd83..df1bf67 100644 --- a/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDecrypt.java +++ b/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDecrypt.java @@ -3,6 +3,7 @@ package org.pgpainless.bouncycastle.sop.operation; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPSessionKey; import org.bouncycastle.openpgp.api.OpenPGPApi; +import org.bouncycastle.openpgp.api.OpenPGPKey; import org.bouncycastle.openpgp.api.OpenPGPMessageInputStream; import org.bouncycastle.openpgp.api.OpenPGPMessageProcessor; import org.bouncycastle.util.io.Streams; @@ -16,6 +17,7 @@ import sop.operation.Decrypt; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.charset.StandardCharsets; import java.util.Date; public class BCDecrypt @@ -24,8 +26,6 @@ public class BCDecrypt private final OpenPGPMessageProcessor processor; - private char[] keyPassword; - public BCDecrypt(OpenPGPApi api) { super(api); this.processor = api.decryptAndOrVerifyMessage(); @@ -33,7 +33,9 @@ public class BCDecrypt @NotNull @Override - public ReadyWithResult ciphertext(@NotNull InputStream inputStream) throws SOPGPException.BadData, SOPGPException.MissingArg, SOPGPException.CannotDecrypt, SOPGPException.KeyIsProtected, IOException { + public ReadyWithResult ciphertext(@NotNull InputStream inputStream) + throws SOPGPException.BadData, SOPGPException.MissingArg, SOPGPException.CannotDecrypt, + SOPGPException.KeyIsProtected { return new ReadyWithResult<>() { @Override public DecryptionResult writeTo(@NotNull OutputStream outputStream) throws IOException, SOPGPException { @@ -89,16 +91,23 @@ public class BCDecrypt @NotNull @Override - public Decrypt withKey(@NotNull InputStream inputStream) throws SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo, IOException { - processor.addDecryptionKey(parseKey(inputStream), keyPassword); + public Decrypt withKey(@NotNull InputStream inputStream) + throws SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo, IOException { + OpenPGPKey key = api.readKeyOrCertificate().parseKey(inputStream); + processor.addDecryptionKey(key); + + if (key.getEncryptionKeys().isEmpty()) { + throw new SOPGPException.CertCannotEncrypt("No decryption key found."); + } return this; } @NotNull @Override - public Decrypt withKeyPassword(@NotNull byte[] bytes) throws SOPGPException.UnsupportedOption, SOPGPException.PasswordNotHumanReadable { - String passphrase = new String(bytes); - this.keyPassword = passphrase.toCharArray(); + public Decrypt withKeyPassword(@NotNull byte[] bytes) + throws SOPGPException.UnsupportedOption, SOPGPException.PasswordNotHumanReadable { + char[] pw = new String(bytes, StandardCharsets.UTF_8).toCharArray(); + processor.addDecryptionKeyPassphrase(pw); return this; } } diff --git a/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDetachedSign.java b/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDetachedSign.java index b352055..0ea4f1c 100644 --- a/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDetachedSign.java +++ b/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDetachedSign.java @@ -21,6 +21,8 @@ import sop.operation.DetachedSign; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.List; public class BCDetachedSign @@ -29,8 +31,7 @@ public class BCDetachedSign private final OpenPGPDetachedSignatureGenerator sigGen; private boolean armored = true; - private char[] keyPassword = null; - + private List signingKeys = new ArrayList<>(); private int signatureMode = PGPSignature.BINARY_DOCUMENT; public BCDetachedSign(OpenPGPApi api) { @@ -40,12 +41,21 @@ public class BCDetachedSign @NotNull @Override - public ReadyWithResult data(@NotNull InputStream inputStream) throws IOException, SOPGPException.KeyIsProtected, SOPGPException.ExpectedText { + public ReadyWithResult data(@NotNull InputStream inputStream) + throws SOPGPException.KeyIsProtected, SOPGPException.ExpectedText { return new ReadyWithResult() { @Override public SigningResult writeTo(@NotNull OutputStream outputStream) throws IOException, SOPGPException { try { + for (OpenPGPKey signingKey : signingKeys) { + sigGen.addSigningKey(signingKey, new SignatureParameters.Callback() { + @Override + public SignatureParameters apply(SignatureParameters parameters) { + return parameters.setSignatureType(signatureMode); + } + }); + } List signatures = sigGen.sign(inputStream); OutputStream aOut = null; OutputStream pOut; @@ -72,7 +82,11 @@ public class BCDetachedSign aOut.close(); } - return new SigningResult(MicAlg.fromHashAlgorithmId(signatures.get(0).getSignature().getHashAlgorithm())); + return new SigningResult(MicAlg.fromHashAlgorithmId( + signatures.get(0).getSignature().getHashAlgorithm())); + } + catch (InvalidSigningKeyException e) { + throw new SOPGPException.KeyCannotSign("Key cannot sign.", e); } catch (PGPException e) { @@ -104,34 +118,17 @@ public class BCDetachedSign } @Override - public DetachedSign key(@NotNull InputStream inputStream) throws SOPGPException.KeyCannotSign, SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo, IOException { - try - { - OpenPGPKey key = parseKey(inputStream); - OpenPGPKey.OpenPGPSecretKey signingKey = key.getSecretKey(key.getSigningKeys().get(0)); - sigGen.addSigningKey(signingKey, - k -> keyPassword, - new SignatureParameters.Callback() { - @Override - public SignatureParameters apply(SignatureParameters parameters) { - return parameters.setSignatureType(signatureMode); - } - }); - } - catch (InvalidSigningKeyException e) - { - throw new SOPGPException.KeyCannotSign("Key cannot sign", e); - } - catch (PGPException e) - { - throw new RuntimeException(e); - } + public DetachedSign key(@NotNull InputStream inputStream) + throws SOPGPException.KeyCannotSign, SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo { + OpenPGPKey key = parseKey(inputStream); + signingKeys.add(key); return this; } @Override - public DetachedSign withKeyPassword(@NotNull byte[] bytes) throws SOPGPException.UnsupportedOption, SOPGPException.PasswordNotHumanReadable { - keyPassword = new String(bytes).toCharArray(); + public DetachedSign withKeyPassword(@NotNull byte[] bytes) + throws SOPGPException.UnsupportedOption, SOPGPException.PasswordNotHumanReadable { + sigGen.addKeyPassphrase(new String(bytes, StandardCharsets.UTF_8).toCharArray()); return this; } } diff --git a/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCEncrypt.java b/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCEncrypt.java index 59bb3e8..29d7a22 100644 --- a/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCEncrypt.java +++ b/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCEncrypt.java @@ -1,9 +1,12 @@ package org.pgpainless.bouncycastle.sop.operation; import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.api.OpenPGPApi; +import org.bouncycastle.openpgp.api.OpenPGPKey; import org.bouncycastle.openpgp.api.OpenPGPMessageGenerator; import org.bouncycastle.openpgp.api.OpenPGPMessageOutputStream; +import org.bouncycastle.openpgp.api.SignatureParameters; import org.bouncycastle.openpgp.api.exception.InvalidEncryptionKeyException; import org.bouncycastle.openpgp.api.exception.InvalidSigningKeyException; import org.bouncycastle.util.io.Streams; @@ -18,12 +21,16 @@ import sop.operation.Encrypt; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; public class BCEncrypt extends AbstractBCOperation implements Encrypt { private final OpenPGPMessageGenerator mGen; - private char[] keyPassword; + private final List signingKeys = new ArrayList<>(); + private int signatureMode = PGPSignature.BINARY_DOCUMENT; public BCEncrypt(OpenPGPApi api) { super(api); @@ -33,7 +40,15 @@ public class BCEncrypt @NotNull @Override public Encrypt mode(@NotNull EncryptAs encryptAs) throws SOPGPException.UnsupportedOption { - // TODO: Implement + switch (encryptAs) + { + case text: + signatureMode = PGPSignature.CANONICAL_TEXT_DOCUMENT; + break; + case binary: + signatureMode = PGPSignature.BINARY_DOCUMENT; + break; + } return this; } @@ -46,32 +61,35 @@ public class BCEncrypt @NotNull @Override - public Encrypt signWith(@NotNull InputStream inputStream) throws SOPGPException.KeyCannotSign, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData, IOException { - try { - mGen.addSigningKey(parseKey(inputStream), k -> keyPassword); - } catch (InvalidSigningKeyException e) { - throw new SOPGPException.KeyCannotSign("Key cannot sign", e); - } + public Encrypt signWith(@NotNull InputStream inputStream) + throws SOPGPException.KeyCannotSign, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData, + IOException { + OpenPGPKey key = api.readKeyOrCertificate().parseKey(inputStream); + signingKeys.add(key); + return this; } @NotNull @Override - public Encrypt withKeyPassword(@NotNull byte[] bytes) throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption { - this.keyPassword = new String(bytes).toCharArray(); + public Encrypt withKeyPassword(@NotNull byte[] bytes) + throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption { + mGen.addKeyPassphrase(new String(bytes, StandardCharsets.UTF_8).toCharArray()); return this; } @NotNull @Override - public Encrypt withPassword(@NotNull String s) throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption { + public Encrypt withPassword(@NotNull String s) + throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption { mGen.addEncryptionPassphrase(s.toCharArray()); return this; } @NotNull @Override - public Encrypt withCert(@NotNull InputStream inputStream) throws SOPGPException.CertCannotEncrypt, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData, IOException { + public Encrypt withCert(@NotNull InputStream inputStream) + throws SOPGPException.CertCannotEncrypt, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData { try { mGen.addEncryptionCertificate(parseCertificate(inputStream)); } catch (InvalidEncryptionKeyException e) { @@ -89,7 +107,21 @@ public class BCEncrypt @NotNull @Override - public ReadyWithResult plaintext(@NotNull InputStream inputStream) throws IOException, SOPGPException.KeyIsProtected { + public ReadyWithResult plaintext(@NotNull InputStream inputStream) + throws SOPGPException.KeyIsProtected { + for (OpenPGPKey key : signingKeys) { + try { + mGen.addSigningKey(key, new SignatureParameters.Callback() { + @Override + public SignatureParameters apply(SignatureParameters parameters) { + return parameters.setSignatureType(signatureMode); + } + }); + } catch (InvalidSigningKeyException e) { + throw new SOPGPException.KeyCannotSign("Key cannot sign", e); + } + } + return new ReadyWithResult() { SessionKey sessionKey = null; @Override diff --git a/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCGenerateKey.java b/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCGenerateKey.java index 444a27a..adbad8b 100644 --- a/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCGenerateKey.java +++ b/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCGenerateKey.java @@ -12,7 +12,9 @@ import sop.operation.GenerateKey; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Date; +import java.util.List; public class BCGenerateKey extends AbstractBCOperation @@ -20,7 +22,7 @@ public class BCGenerateKey private boolean armor = true; private boolean signOnly = false; - private String userId; + private final List userIds = new ArrayList<>(); private char[] passphrase; public BCGenerateKey(OpenPGPApi api) { @@ -29,13 +31,13 @@ public class BCGenerateKey @NotNull @Override - public Ready generate() throws SOPGPException.MissingArg, SOPGPException.UnsupportedAsymmetricAlgo, IOException { + public Ready generate() throws SOPGPException.MissingArg, SOPGPException.UnsupportedAsymmetricAlgo { return new Ready() { @Override public void writeTo(@NotNull OutputStream outputStream) throws IOException { - OpenPGPV6KeyGenerator generator = null; + OpenPGPV6KeyGenerator generator; try { generator = api.generateKey(new Date()); } catch (PGPException e) { @@ -44,14 +46,20 @@ public class BCGenerateKey OpenPGPKey key; try { + OpenPGPV6KeyGenerator.WithPrimaryKey builder; if (signOnly) { - key = generator.signOnlyKey().build(passphrase); + builder = generator.signOnlyKey(); } else { - key = generator.ed25519x25519Key(userId).build(passphrase); + builder = generator.ed25519x25519Key(null); } + for (String userId : userIds) + { + builder.addUserId(userId); + } + key = builder.build(passphrase); } catch (PGPException e) { @@ -80,7 +88,7 @@ public class BCGenerateKey @NotNull @Override public GenerateKey userId(@NotNull String s) { - this.userId = s; + this.userIds.add(s); return this; } diff --git a/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCInlineSign.java b/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCInlineSign.java index ed205d9..630bad6 100644 --- a/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCInlineSign.java +++ b/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCInlineSign.java @@ -1,9 +1,12 @@ package org.pgpainless.bouncycastle.sop.operation; import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.api.OpenPGPApi; +import org.bouncycastle.openpgp.api.OpenPGPKey; import org.bouncycastle.openpgp.api.OpenPGPMessageGenerator; import org.bouncycastle.openpgp.api.OpenPGPMessageOutputStream; +import org.bouncycastle.openpgp.api.SignatureParameters; import org.bouncycastle.openpgp.api.exception.InvalidSigningKeyException; import org.bouncycastle.util.io.Streams; import org.jetbrains.annotations.NotNull; @@ -15,6 +18,9 @@ import sop.operation.InlineSign; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; public class BCInlineSign extends AbstractBCOperation @@ -22,7 +28,9 @@ public class BCInlineSign { private final OpenPGPMessageGenerator mGen; - private char[] keyPassword; + private final List signingKeys = new ArrayList<>(); + + private int signingMode = PGPSignature.BINARY_DOCUMENT; public BCInlineSign(OpenPGPApi api) { super(api); @@ -31,7 +39,20 @@ public class BCInlineSign @NotNull @Override - public Ready data(@NotNull InputStream inputStream) throws IOException, SOPGPException.KeyIsProtected, SOPGPException.ExpectedText { + public Ready data(@NotNull InputStream inputStream) + throws SOPGPException.KeyIsProtected, SOPGPException.ExpectedText { + for (OpenPGPKey key : signingKeys) { + try { + mGen.addSigningKey(key, new SignatureParameters.Callback() { + @Override + public SignatureParameters apply(SignatureParameters parameters) { + return parameters.setSignatureType(signingMode); + } + }); + } catch (InvalidSigningKeyException e) { + throw new SOPGPException.KeyCannotSign("Key cannot sign.", e); + } + } return new Ready() { @Override public void writeTo(@NotNull OutputStream outputStream) throws IOException { @@ -48,7 +69,18 @@ public class BCInlineSign @NotNull @Override - public InlineSign mode(@NotNull InlineSignAs inlineSignAs) throws SOPGPException.UnsupportedOption { + public InlineSign mode(@NotNull InlineSignAs inlineSignAs) + throws SOPGPException.UnsupportedOption { + switch (inlineSignAs) + { + case text: + signingMode = PGPSignature.CANONICAL_TEXT_DOCUMENT; + break; + case binary: + signingMode = PGPSignature.BINARY_DOCUMENT; + break; + // TODO: Cleartext Signature Framework + } return this; } @@ -59,18 +91,18 @@ public class BCInlineSign } @Override - public InlineSign key(@NotNull InputStream inputStream) throws SOPGPException.KeyCannotSign, SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo, IOException { - try { - mGen.addSigningKey(api.readKeyOrCertificate().parseKey(inputStream), k -> keyPassword); - } catch (InvalidSigningKeyException e) { - throw new SOPGPException.KeyCannotSign("Key cannot sign.", e); - } + public InlineSign key(@NotNull InputStream inputStream) + throws SOPGPException.KeyCannotSign, SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo, + IOException { + OpenPGPKey key = api.readKeyOrCertificate().parseKey(inputStream); + signingKeys.add(key); return this; } @Override - public InlineSign withKeyPassword(@NotNull byte[] bytes) throws SOPGPException.UnsupportedOption, SOPGPException.PasswordNotHumanReadable { - this.keyPassword = new String(bytes).toCharArray(); + public InlineSign withKeyPassword(@NotNull byte[] bytes) + throws SOPGPException.UnsupportedOption, SOPGPException.PasswordNotHumanReadable { + mGen.addKeyPassphrase(new String(bytes, StandardCharsets.UTF_8).toCharArray()); return this; } }