diff --git a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java index 9997bfba..cafde397 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java +++ b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java @@ -15,8 +15,8 @@ */ package org.pgpainless; -import javax.annotation.Nonnull; import java.io.IOException; +import javax.annotation.Nonnull; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPSecretKeyRing; @@ -30,8 +30,6 @@ import org.pgpainless.key.generation.KeyRingBuilder; import org.pgpainless.key.modification.KeyRingEditor; import org.pgpainless.key.modification.KeyRingEditorInterface; import org.pgpainless.key.parsing.KeyRingReader; -import org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider; -import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvider; import org.pgpainless.symmetric_encryption.SymmetricEncryptorDecryptor; import org.pgpainless.util.Passphrase; diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/modification/KeyRingEditor.java b/pgpainless-core/src/main/java/org/pgpainless/key/modification/KeyRingEditor.java index a8c2f503..628363df 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/modification/KeyRingEditor.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/modification/KeyRingEditor.java @@ -1,30 +1,44 @@ +/* + * Copyright 2020 Paul Schaub. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.pgpainless.key.modification; import java.util.ArrayList; import java.util.Iterator; import java.util.List; - import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPrivateKey; import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureGenerator; import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.bouncycastle.openpgp.operator.PGPDigestCalculator; +import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; +import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.pgpainless.algorithm.HashAlgorithm; import org.pgpainless.algorithm.SignatureType; import org.pgpainless.key.OpenPgpV4Fingerprint; -import org.pgpainless.key.collection.PGPKeyRing; import org.pgpainless.key.generation.KeySpec; import org.pgpainless.key.protection.KeyRingProtectionSettings; -import org.pgpainless.key.protection.PasswordBasedSecretKeyRingProtector; -import org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider; import org.pgpainless.key.protection.SecretKeyRingProtector; -import org.pgpainless.key.protection.UnprotectedKeysProtector; -import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvider; +import org.pgpainless.key.util.OpenPgpKeyAttributeUtil; import org.pgpainless.util.Passphrase; public class KeyRingEditor implements KeyRingEditorInterface { @@ -42,18 +56,27 @@ public class KeyRingEditor implements KeyRingEditorInterface { public KeyRingEditorInterface addUserId(String userId, SecretKeyRingProtector secretKeyRingProtector) throws PGPException { userId = sanitizeUserId(userId); + Iterator secretKeys = secretKeyRing.getSecretKeys(); + PGPSecretKey primarySecKey = secretKeys.next(); PGPPublicKey primaryPubKey = secretKeyRing.getPublicKey(); - PGPPrivateKey privateKey = unlockSecretKey(primaryPubKey.getKeyID(), secretKeyRingProtector); + PGPPrivateKey privateKey = unlockSecretKey(primarySecKey, secretKeyRingProtector); + + PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator( + getPgpContentSignerBuilderForKey(primarySecKey)); signatureGenerator.init(SignatureType.POSITIVE_CERTIFICATION.getCode(), privateKey); PGPSignature userIdSignature = signatureGenerator.generateCertification(userId, primaryPubKey); primaryPubKey = PGPPublicKey.addCertification(primaryPubKey, userId, userIdSignature); + PGPDigestCalculator digestCalculator = new BcPGPDigestCalculatorProvider().get( + // TODO: Is SHA1 still a good choice? + // If not, what to use/how to make a proper choice? + HashAlgorithm.SHA1.getAlgorithmId()); + // "reassemble" secret key ring with modified primary key - PGPSecretKey primarySecKey = new PGPSecretKey( - privateKey, - primaryPubKey, digestCalculator, true, secretKeyRingProtector); + primarySecKey = new PGPSecretKey(privateKey, primaryPubKey, digestCalculator, true, + secretKeyRingProtector.getEncryptor(primaryPubKey.getKeyID())); List secretKeyList = new ArrayList<>(); secretKeyList.add(primarySecKey); while (secretKeys.hasNext()) { @@ -61,20 +84,24 @@ public class KeyRingEditor implements KeyRingEditorInterface { } secretKeyRing = new PGPSecretKeyRing(secretKeyList); - // extract public key ring from secret keys - List publicKeyList = new ArrayList<>(); - Iterator publicKeys = secretKeyRing.getPublicKeys(); - while (publicKeys.hasNext()) { - publicKeyList.add(publicKeys.next()); - } - PGPPublicKeyRing publicKeyRing = new PGPPublicKeyRing(publicKeyList); - return this; } - private PGPPrivateKey unlockSecretKey(long keyId, SecretKeyRingProtector protector) throws PGPException { - PGPSecretKey secretKey = secretKeyRing.getSecretKey(keyId); - PBESecretKeyDecryptor secretKeyDecryptor = protector.getDecryptor(keyId); + private static BcPGPContentSignerBuilder getPgpContentSignerBuilderForKey(PGPSecretKey secretKey) { + List preferredHashAlgorithms = OpenPgpKeyAttributeUtil.getPreferredHashAlgorithms(secretKey.getPublicKey()); + HashAlgorithm hashAlgorithm = negotiateHashAlgorithm(preferredHashAlgorithms); + + return new BcPGPContentSignerBuilder(secretKey.getPublicKey().getAlgorithm(), hashAlgorithm.getAlgorithmId()); + } + + private static HashAlgorithm negotiateHashAlgorithm(List preferredHashAlgorithms) { + // TODO: Match our list of supported hash algorithms against the list, to determine the best suitable algo. + // For now we just take the first algorithm in the list and hope that BC has support for it. + return preferredHashAlgorithms.get(0); + } + + private PGPPrivateKey unlockSecretKey(PGPSecretKey secretKey, SecretKeyRingProtector protector) throws PGPException { + PBESecretKeyDecryptor secretKeyDecryptor = protector.getDecryptor(secretKey.getKeyID()); PGPPrivateKey privateKey = secretKey.extractPrivateKey(secretKeyDecryptor); return privateKey; } @@ -87,32 +114,32 @@ public class KeyRingEditor implements KeyRingEditorInterface { } @Override - public KeyRingEditorInterface deleteUserId(String userId) { + public KeyRingEditorInterface deleteUserId(String userId, SecretKeyRingProtector protector) { return this; } @Override - public KeyRingEditorInterface addSubKey(KeySpec keySpec) { + public KeyRingEditorInterface addSubKey(KeySpec keySpec, SecretKeyRingProtector protector) { return this; } @Override - public KeyRingEditorInterface deleteSubKey(OpenPgpV4Fingerprint fingerprint) { + public KeyRingEditorInterface deleteSubKey(OpenPgpV4Fingerprint fingerprint, SecretKeyRingProtector protector) { return this; } @Override - public KeyRingEditorInterface deleteSubKey(long subKeyId) { + public KeyRingEditorInterface deleteSubKey(long subKeyId, SecretKeyRingProtector protector) { return this; } @Override - public KeyRingEditorInterface revokeSubKey(OpenPgpV4Fingerprint fingerprint) { + public KeyRingEditorInterface revokeSubKey(OpenPgpV4Fingerprint fingerprint, SecretKeyRingProtector protector) { return this; } @Override - public KeyRingEditorInterface revokeSubKey(long subKeyId) { + public KeyRingEditorInterface revokeSubKey(long subKeyId, SecretKeyRingProtector protector) { return this; } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/modification/KeyRingEditorInterface.java b/pgpainless-core/src/main/java/org/pgpainless/key/modification/KeyRingEditorInterface.java index df9d45b8..332c54ec 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/modification/KeyRingEditorInterface.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/modification/KeyRingEditorInterface.java @@ -164,7 +164,7 @@ public interface KeyRingEditorInterface { } /** - * Return the {@link PGPSecretKeyRing} + * Return the {@link PGPSecretKeyRing}. * @return the key */ PGPSecretKeyRing done(); diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/modification/package-info.java b/pgpainless-core/src/main/java/org/pgpainless/key/modification/package-info.java new file mode 100644 index 00000000..0b09adf5 --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/key/modification/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2020 Paul Schaub. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Classes that deal with modifications made to OpenPGP keys. + */ +package org.pgpainless.key.modification; diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/protection/passphrase_provider/MapBasedPassphraseProvider.java b/pgpainless-core/src/main/java/org/pgpainless/key/protection/passphrase_provider/MapBasedPassphraseProvider.java index a29ea423..24ea569d 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/protection/passphrase_provider/MapBasedPassphraseProvider.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/protection/passphrase_provider/MapBasedPassphraseProvider.java @@ -1,3 +1,18 @@ +/* + * Copyright 2020 Paul Schaub. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.pgpainless.key.protection.passphrase_provider; import java.util.Map; @@ -5,6 +20,13 @@ import javax.annotation.Nullable; import org.pgpainless.util.Passphrase; +/** + * Implementation of the {@link SecretKeyPassphraseProvider} that holds a map of different {@link Passphrase passphrases}. + * It will return the right passphrase depending on the key-id. + * + * Note: This provider might return null! + * TODO: Make this null-safe and throw an exception instead? + */ public class MapBasedPassphraseProvider implements SecretKeyPassphraseProvider { private final Map map; diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/protection/passphrase_provider/SecretKeyPassphraseProvider.java b/pgpainless-core/src/main/java/org/pgpainless/key/protection/passphrase_provider/SecretKeyPassphraseProvider.java index 3fb1e2e2..aa5d43ef 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/protection/passphrase_provider/SecretKeyPassphraseProvider.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/protection/passphrase_provider/SecretKeyPassphraseProvider.java @@ -20,7 +20,7 @@ import javax.annotation.Nullable; import org.pgpainless.util.Passphrase; /** - * Interface to allow the user to provide a passphrase for an encrypted OpenPGP secret key. + * Interface to allow the user to provide a {@link Passphrase} for an encrypted OpenPGP secret key. */ public interface SecretKeyPassphraseProvider { diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/protection/passphrase_provider/SolitaryPassphraseProvider.java b/pgpainless-core/src/main/java/org/pgpainless/key/protection/passphrase_provider/SolitaryPassphraseProvider.java index b3acfbf4..e251111f 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/protection/passphrase_provider/SolitaryPassphraseProvider.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/protection/passphrase_provider/SolitaryPassphraseProvider.java @@ -1,9 +1,27 @@ +/* + * Copyright 2020 Paul Schaub. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.pgpainless.key.protection.passphrase_provider; import javax.annotation.Nullable; import org.pgpainless.util.Passphrase; +/** + * Implementation of the {@link SecretKeyPassphraseProvider} that holds a single {@link Passphrase}. + */ public class SolitaryPassphraseProvider implements SecretKeyPassphraseProvider { private final Passphrase passphrase; @@ -15,6 +33,7 @@ public class SolitaryPassphraseProvider implements SecretKeyPassphraseProvider { @Nullable @Override public Passphrase getPassphraseFor(Long keyId) { + // always return the same passphrase. return passphrase; } } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/protection/passphrase_provider/package-info.java b/pgpainless-core/src/main/java/org/pgpainless/key/protection/passphrase_provider/package-info.java new file mode 100644 index 00000000..bd356681 --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/key/protection/passphrase_provider/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2020 Paul Schaub. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Passphrase Provider classes. + */ +package org.pgpainless.key.protection.passphrase_provider; diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/util/OpenPgpKeyAttributeUtil.java b/pgpainless-core/src/main/java/org/pgpainless/key/util/OpenPgpKeyAttributeUtil.java new file mode 100644 index 00000000..bffa703d --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/key/util/OpenPgpKeyAttributeUtil.java @@ -0,0 +1,54 @@ +/* + * Copyright 2020 Paul Schaub. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pgpainless.key.util; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSignature; +import org.pgpainless.algorithm.HashAlgorithm; +import org.pgpainless.algorithm.SignatureType; + +public class OpenPgpKeyAttributeUtil { + + public static List getPreferredHashAlgorithms(PGPPublicKey publicKey) { + List hashAlgorithms = new ArrayList<>(); + // TODO: I'd assume that we have to use publicKey.getKeySignatures() here, but that is empty... + Iterator keySignatures = publicKey.getSignatures(); + while (keySignatures.hasNext()) { + PGPSignature signature = (PGPSignature) keySignatures.next(); + + if (signature.getKeyID() != publicKey.getKeyID()) { + // Signature from a foreign key. Skip. + continue; + } + + if (signature.getSignatureType() == SignatureType.POSITIVE_CERTIFICATION.getCode()) { + int[] hashAlgos = signature.getHashedSubPackets().getPreferredHashAlgorithms(); + for (int h : hashAlgos) { + hashAlgorithms.add(HashAlgorithm.fromId(h)); + } + // Exit the loop after the first key signature with hash algorithms. + // TODO: Find out, if it is possible that there are multiple key signatures which specify preferred + // algorithms and how to deal with that. + break; + } + } + return hashAlgorithms; + } +} diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/util/package-info.java b/pgpainless-core/src/main/java/org/pgpainless/key/util/package-info.java new file mode 100644 index 00000000..27bc9acd --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/key/util/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2020 Paul Schaub. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Utility functions to deal with OpenPGP keys. + */ +package org.pgpainless.key.util; diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/KeyPrinter.java b/pgpainless-core/src/main/java/org/pgpainless/util/KeyPrinter.java new file mode 100644 index 00000000..14f6510a --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/util/KeyPrinter.java @@ -0,0 +1,48 @@ +/* + * Copyright 2020 Paul Schaub. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pgpainless.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.bouncycastle.bcpg.ArmoredOutputStream; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.util.io.Streams; + +public class KeyPrinter { + + public static String toAsciiArmoredString(PGPSecretKeyRing secretKeys) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ArmoredOutputStream armor = new ArmoredOutputStream(out); + + Streams.pipeAll(new ByteArrayInputStream(secretKeys.getEncoded()), armor); + armor.close(); + + return out.toString(); + } + + public static String toAsciiArmoredString(PGPPublicKeyRing publicKeys) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ArmoredOutputStream armor = new ArmoredOutputStream(out); + + Streams.pipeAll(new ByteArrayInputStream(publicKeys.getEncoded()), armor); + armor.close(); + + return out.toString(); + } +} diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/modification/AddUserIdTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/modification/AddUserIdTest.java new file mode 100644 index 00000000..af793c65 --- /dev/null +++ b/pgpainless-core/src/test/java/org/pgpainless/key/modification/AddUserIdTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 2020 Paul Schaub. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pgpainless.key.modification; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.util.Iterator; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.junit.Test; +import org.pgpainless.PGPainless; +import org.pgpainless.key.collection.PGPKeyRing; +import org.pgpainless.key.protection.PasswordBasedSecretKeyRingProtector; +import org.pgpainless.key.protection.SecretKeyRingProtector; +import org.pgpainless.util.Passphrase; + +public class AddUserIdTest { + + @Test + public void addUserIdToExistingKeyRing() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException { + PGPKeyRing keyRing = PGPainless.generateKeyRing().simpleEcKeyRing("alice@wonderland.lit", "rabb1th0le"); + PGPSecretKeyRing secretKeys = keyRing.getSecretKeys(); + + Iterator userIds = secretKeys.getSecretKey().getPublicKey().getUserIDs(); + assertEquals("alice@wonderland.lit", userIds.next()); + assertFalse(userIds.hasNext()); + + SecretKeyRingProtector protector = PasswordBasedSecretKeyRingProtector.forKey(secretKeys, Passphrase.fromPassword("rabb1th0le")); + secretKeys = PGPainless.modifyKeyRing(secretKeys) + .addUserId("cheshirecat@wonderland.lit", protector) + .done(); + + userIds = secretKeys.getPublicKey().getUserIDs(); + assertEquals("alice@wonderland.lit", userIds.next()); + assertEquals("cheshirecat@wonderland.lit", userIds.next()); + assertFalse(userIds.hasNext()); + + // CHECKSTYLE:OFF + // System.out.println(KeyPrinter.toAsciiArmoredString(secretKeys)); + // CHECKSTYLE:ON + } +} diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/modification/ChangeSecretKeyRingPassphraseTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/modification/ChangeSecretKeyRingPassphraseTest.java index a00612a7..6d09ff04 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/modification/ChangeSecretKeyRingPassphraseTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/modification/ChangeSecretKeyRingPassphraseTest.java @@ -1,3 +1,18 @@ +/* + * Copyright 2020 Paul Schaub. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.pgpainless.key.modification; import static org.junit.Assert.fail;