1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-09-10 18:59:39 +02:00

Rename module painless-core -> pgpainless-core

Fix build.gradle
This commit is contained in:
Paul Schaub 2018-07-19 00:15:06 +02:00
parent a80bb043eb
commit 39c5464c37
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
102 changed files with 1 additions and 14 deletions

View file

@ -0,0 +1,94 @@
/*
* Copyright 2018 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;
import org.bouncycastle.openpgp.PGPException;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.decryption_verification.DecryptionBuilder;
import org.pgpainless.decryption_verification.DecryptionStream;
import org.pgpainless.encryption_signing.EncryptionBuilder;
import org.pgpainless.encryption_signing.EncryptionStream;
import org.pgpainless.key.generation.KeyRingBuilder;
import org.pgpainless.key.parsing.KeyRingReader;
import org.pgpainless.symmetric_encryption.SymmetricEncryptorDecryptor;
import org.pgpainless.util.Passphrase;
import java.io.IOException;
public class PGPainless {
/**
* Generate a new OpenPGP key ring.
* @return builder
*/
public static KeyRingBuilder generateKeyRing() {
return new KeyRingBuilder();
}
/**
* Read an existing OpenPGP key ring.
* @return builder
*/
public static KeyRingReader readKeyRing() {
return new KeyRingReader();
}
/**
* Create an {@link EncryptionStream}, which can be used to encrypt and/or sign data using OpenPGP.
* @return builder
*/
public static EncryptionBuilder createEncryptor() {
return new EncryptionBuilder();
}
/**
* Create a {@link DecryptionStream}, which can be used to decrypt and/or verify data using OpenPGP.
* @return builder
*/
public static DecryptionBuilder createDecryptor() {
return new DecryptionBuilder();
}
/**
* Encrypt some data symmetrically using OpenPGP and a password.
* The resulting data will be uncompressed and integrity protected.
*
* @param data input data.
* @param password password.
* @return symmetrically OpenPGP encrypted data.
* @throws IOException IO is dangerous.
* @throws PGPException PGP is brittle.
*/
public static byte[] encryptWithPassword(byte[] data, Passphrase password, SymmetricKeyAlgorithm algorithm) throws IOException, PGPException {
return SymmetricEncryptorDecryptor.symmetricallyEncrypt(data, password,
algorithm, CompressionAlgorithm.UNCOMPRESSED);
}
/**
* Decrypt some symmetrically encrypted data using a password.
* The data is suspected to be integrity protected.
*
* @param data symmetrically OpenPGP encrypted data.
* @param password password.
* @return decrypted data.
* @throws IOException IO is dangerous.
* @throws PGPException PGP is brittle.
*/
public static byte[] decryptWithPassword(byte[] data, Passphrase password) throws IOException, PGPException {
return SymmetricEncryptorDecryptor.symmetricallyDecrypt(data, password);
}
}

View file

@ -0,0 +1,105 @@
/*
* Copyright 2018 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.algorithm;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class AlgorithmSuite {
private static AlgorithmSuite defaultAlgorithmSuite = new AlgorithmSuite(
Arrays.asList(
SymmetricKeyAlgorithm.AES_256,
SymmetricKeyAlgorithm.AES_192,
SymmetricKeyAlgorithm.AES_128),
Arrays.asList(
HashAlgorithm.SHA512,
HashAlgorithm.SHA384,
HashAlgorithm.SHA256,
HashAlgorithm.SHA224),
Arrays.asList(
CompressionAlgorithm.ZLIB,
CompressionAlgorithm.BZIP2,
CompressionAlgorithm.ZIP,
CompressionAlgorithm.UNCOMPRESSED)
);
private List<SymmetricKeyAlgorithm> symmetricKeyAlgorithms;
private List<HashAlgorithm> hashAlgorithms;
private List<CompressionAlgorithm> compressionAlgorithms;
public AlgorithmSuite(List<SymmetricKeyAlgorithm> symmetricKeyAlgorithms,
List<HashAlgorithm> hashAlgorithms,
List<CompressionAlgorithm> compressionAlgorithms) {
this.symmetricKeyAlgorithms = Collections.unmodifiableList(symmetricKeyAlgorithms);
this.hashAlgorithms = Collections.unmodifiableList(hashAlgorithms);
this.compressionAlgorithms = Collections.unmodifiableList(compressionAlgorithms);
}
public void setSymmetricKeyAlgorithms(List<SymmetricKeyAlgorithm> symmetricKeyAlgorithms) {
this.symmetricKeyAlgorithms = symmetricKeyAlgorithms;
}
public List<SymmetricKeyAlgorithm> getSymmetricKeyAlgorithms() {
return new ArrayList<>(symmetricKeyAlgorithms);
}
public int[] getSymmetricKeyAlgorithmIds() {
int[] array = new int[symmetricKeyAlgorithms.size()];
for (int i = 0; i < array.length; i++) {
array[i] = symmetricKeyAlgorithms.get(i).getAlgorithmId();
}
return array;
}
public void setHashAlgorithms(List<HashAlgorithm> hashAlgorithms) {
this.hashAlgorithms = hashAlgorithms;
}
public List<HashAlgorithm> getHashAlgorithms() {
return hashAlgorithms;
}
public int[] getHashAlgorithmIds() {
int[] array = new int[hashAlgorithms.size()];
for (int i = 0; i < array.length; i++) {
array[i] = hashAlgorithms.get(i).getAlgorithmId();
}
return array;
}
public void setCompressionAlgorithms(List<CompressionAlgorithm> compressionAlgorithms) {
this.compressionAlgorithms = compressionAlgorithms;
}
public List<CompressionAlgorithm> getCompressionAlgorithms() {
return compressionAlgorithms;
}
public int[] getCompressionAlgorithmIds() {
int[] array = new int[compressionAlgorithms.size()];
for (int i = 0; i < array.length; i++) {
array[i] = compressionAlgorithms.get(i).getAlgorithmId();
}
return array;
}
public static AlgorithmSuite getDefaultAlgorithmSuite() {
return defaultAlgorithmSuite;
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright 2018 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.algorithm;
import java.util.HashMap;
import java.util.Map;
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
public enum CompressionAlgorithm {
UNCOMPRESSED (CompressionAlgorithmTags.UNCOMPRESSED),
ZIP (CompressionAlgorithmTags.ZIP),
ZLIB (CompressionAlgorithmTags.ZLIB),
BZIP2 (CompressionAlgorithmTags.BZIP2),
;
private static final Map<Integer, CompressionAlgorithm> MAP = new HashMap<>();
static {
for (CompressionAlgorithm c : CompressionAlgorithm.values()) {
MAP.put(c.algorithmId, c);
}
}
public static CompressionAlgorithm fromId(int id) {
return MAP.get(id);
}
private final int algorithmId;
CompressionAlgorithm(int id) {
this.algorithmId = id;
}
public int getAlgorithmId() {
return algorithmId;
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright 2018 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.algorithm;
import java.util.HashMap;
import java.util.Map;
import org.bouncycastle.bcpg.sig.Features;
public enum Feature {
/**
* Add modification detection package.
*
* @see <a href="https://tools.ietf.org/html/rfc4880#section-5.14">
* RFC-4880 §5.14: Modification Detection Code Packet</a>
*/
MODIFICATION_DETECTION(Features.FEATURE_MODIFICATION_DETECTION),
;
private static final Map<Byte, Feature> MAP = new HashMap<>();
static {
for (Feature f : Feature.values()) {
MAP.put(f.featureId, f);
}
}
public static Feature fromId(byte id) {
return MAP.get(id);
}
private final byte featureId;
Feature(byte featureId) {
this.featureId = featureId;
}
public byte getFeatureId() {
return featureId;
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright 2018 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.algorithm;
import java.util.HashMap;
import java.util.Map;
import org.bouncycastle.bcpg.HashAlgorithmTags;
public enum HashAlgorithm {
MD5 (HashAlgorithmTags.MD5),
SHA1 (HashAlgorithmTags.SHA1),
RIPEMD160 (HashAlgorithmTags.RIPEMD160),
DOUBLE_SHA (HashAlgorithmTags.DOUBLE_SHA),
MD2 (HashAlgorithmTags.MD2),
TIGER_192 (HashAlgorithmTags.TIGER_192),
HAVAL_5_160(HashAlgorithmTags.HAVAL_5_160),
SHA256 (HashAlgorithmTags.SHA256),
SHA384 (HashAlgorithmTags.SHA384),
SHA512 (HashAlgorithmTags.SHA512),
SHA224 (HashAlgorithmTags.SHA224),
;
// Coincidence? I don't this so...
private static final Map<Integer, HashAlgorithm> MAP = new HashMap<>();
static {
for (HashAlgorithm h : HashAlgorithm.values()) {
MAP.put(h.algorithmId, h);
}
}
public static HashAlgorithm fromId(int id) {
return MAP.get(id);
}
private final int algorithmId;
HashAlgorithm(int id) {
this.algorithmId = id;
}
public int getAlgorithmId() {
return algorithmId;
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright 2018 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.algorithm;
import java.util.ArrayList;
import java.util.List;
import org.bouncycastle.bcpg.sig.KeyFlags;
public enum KeyFlag {
CERTIFY_OTHER (KeyFlags.CERTIFY_OTHER),
SIGN_DATA (KeyFlags.SIGN_DATA),
ENCRYPT_COMMS (KeyFlags.ENCRYPT_COMMS),
ENCRYPT_STORAGE(KeyFlags.ENCRYPT_STORAGE),
SPLIT (KeyFlags.SPLIT),
AUTHENTICATION (KeyFlags.AUTHENTICATION),
SHARED (KeyFlags.SHARED),
;
private final int flag;
KeyFlag(int flag) {
this.flag = flag;
}
public int getFlag() {
return flag;
}
public static List<KeyFlag> fromInteger(int bitmask) {
List<KeyFlag> flags = new ArrayList<>();
for (KeyFlag f : KeyFlag.values()) {
if ((bitmask & f.flag) != 0) {
flags.add(f);
}
}
return flags;
}
}

View file

@ -0,0 +1,62 @@
/*
* Copyright 2018 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.algorithm;
import java.util.HashMap;
import java.util.Map;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
public enum PublicKeyAlgorithm {
RSA_GENERAL (PublicKeyAlgorithmTags.RSA_GENERAL),
RSA_ENCRYPT (PublicKeyAlgorithmTags.RSA_ENCRYPT),
RSA_SIGN (PublicKeyAlgorithmTags.RSA_SIGN),
ELGAMAL_ENCRYPT (PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT),
DSA (PublicKeyAlgorithmTags.DSA),
/**
* EC is deprecated.
* @deprecated use {@link #ECDH} instead.
*/
EC (PublicKeyAlgorithmTags.EC),
ECDH (PublicKeyAlgorithmTags.ECDH),
ECDSA (PublicKeyAlgorithmTags.ECDSA),
ELGAMAL_GENERAL (PublicKeyAlgorithmTags.ELGAMAL_GENERAL),
DIFFIE_HELLMAN (PublicKeyAlgorithmTags.DIFFIE_HELLMAN),
;
private static final Map<Integer, PublicKeyAlgorithm> MAP = new HashMap<>();
static {
for (PublicKeyAlgorithm p : PublicKeyAlgorithm.values()) {
MAP.put(p.algorithmId, p);
}
}
public static PublicKeyAlgorithm fromId(int id) {
return MAP.get(id);
}
private final int algorithmId;
PublicKeyAlgorithm(int algorithmId) {
this.algorithmId = algorithmId;
}
public int getAlgorithmId() {
return algorithmId;
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright 2018 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.algorithm;
import java.util.HashMap;
import java.util.Map;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
public enum SymmetricKeyAlgorithm {
NULL (SymmetricKeyAlgorithmTags.NULL),
/**
* IDEA is deprecated.
* @deprecated use a different algorithm.
*/
IDEA (SymmetricKeyAlgorithmTags.IDEA),
TRIPLE_DES (SymmetricKeyAlgorithmTags.TRIPLE_DES),
CAST5 (SymmetricKeyAlgorithmTags.CAST5),
BLOWFISH (SymmetricKeyAlgorithmTags.BLOWFISH),
SAFER (SymmetricKeyAlgorithmTags.SAFER),
DES (SymmetricKeyAlgorithmTags.DES),
AES_128 (SymmetricKeyAlgorithmTags.AES_128),
AES_192 (SymmetricKeyAlgorithmTags.AES_192),
AES_256 (SymmetricKeyAlgorithmTags.AES_256),
TWOFISH (SymmetricKeyAlgorithmTags.TWOFISH),
CAMELLIA_128 (SymmetricKeyAlgorithmTags.CAMELLIA_128),
CAMELLIA_192 (SymmetricKeyAlgorithmTags.CAMELLIA_192),
CAMELLIA_256 (SymmetricKeyAlgorithmTags.CAMELLIA_256),
;
private static final Map<Integer, SymmetricKeyAlgorithm> MAP = new HashMap<>();
static {
for (SymmetricKeyAlgorithm s : SymmetricKeyAlgorithm.values()) {
MAP.put(s.algorithmId, s);
}
}
public static SymmetricKeyAlgorithm fromId(int id) {
return MAP.get(id);
}
private final int algorithmId;
SymmetricKeyAlgorithm(int algorithmId) {
this.algorithmId = algorithmId;
}
public int getAlgorithmId() {
return algorithmId;
}
}

View file

@ -0,0 +1,19 @@
/*
* Copyright 2018 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.
*/
/**
* Enums which map to OpenPGP's algorithm IDs.
*/
package org.pgpainless.algorithm;

View file

@ -0,0 +1,123 @@
/*
* Copyright 2018 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.decryption_verification;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.pgpainless.key.OpenPgpV4Fingerprint;
import org.pgpainless.key.protection.SecretKeyRingProtector;
public class DecryptionBuilder implements DecryptionBuilderInterface {
private InputStream inputStream;
private PGPSecretKeyRingCollection decryptionKeys;
private SecretKeyRingProtector decryptionKeyDecryptor;
private Set<PGPPublicKeyRing> verificationKeys = new HashSet<>();
private MissingPublicKeyCallback missingPublicKeyCallback = null;
@Override
public DecryptWith onInputStream(InputStream inputStream) {
this.inputStream = inputStream;
return new DecryptWithImpl();
}
class DecryptWithImpl implements DecryptWith {
@Override
public VerifyWith decryptWith(SecretKeyRingProtector decryptor, PGPSecretKeyRingCollection secretKeyRings) {
DecryptionBuilder.this.decryptionKeys = secretKeyRings;
DecryptionBuilder.this.decryptionKeyDecryptor = decryptor;
return new VerifyWithImpl();
}
@Override
public VerifyWith doNotDecrypt() {
DecryptionBuilder.this.decryptionKeys = null;
DecryptionBuilder.this.decryptionKeyDecryptor = null;
return new VerifyWithImpl();
}
}
class VerifyWithImpl implements VerifyWith {
@Override
public HandleMissingPublicKeys verifyWith(PGPPublicKeyRingCollection publicKeyRingCollection) {
Set<PGPPublicKeyRing> publicKeyRings = new HashSet<>();
for (Iterator<PGPPublicKeyRing> i = publicKeyRingCollection.getKeyRings(); i.hasNext(); ) {
publicKeyRings.add(i.next());
}
return verifyWith(publicKeyRings);
}
@Override
public HandleMissingPublicKeys verifyWith(Set<OpenPgpV4Fingerprint> trustedKeyIds,
PGPPublicKeyRingCollection publicKeyRingCollection) {
Set<PGPPublicKeyRing> publicKeyRings = new HashSet<>();
for (Iterator<PGPPublicKeyRing> i = publicKeyRingCollection.getKeyRings(); i.hasNext(); ) {
PGPPublicKeyRing p = i.next();
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(p);
if (trustedKeyIds.contains(fingerprint)) {
publicKeyRings.add(p);
}
}
return verifyWith(publicKeyRings);
}
@Override
public HandleMissingPublicKeys verifyWith(Set<PGPPublicKeyRing> publicKeyRings) {
DecryptionBuilder.this.verificationKeys = publicKeyRings;
return new HandleMissingPublicKeysImpl();
}
@Override
public Build doNotVerify() {
DecryptionBuilder.this.verificationKeys = null;
return new BuildImpl();
}
}
class HandleMissingPublicKeysImpl implements HandleMissingPublicKeys {
@Override
public Build handleMissingPublicKeysWith(MissingPublicKeyCallback callback) {
DecryptionBuilder.this.missingPublicKeyCallback = callback;
return new BuildImpl();
}
@Override
public Build ignoreMissingPublicKeys() {
DecryptionBuilder.this.missingPublicKeyCallback = null;
return new BuildImpl();
}
}
class BuildImpl implements Build {
@Override
public DecryptionStream build() throws IOException, PGPException {
return DecryptionStreamFactory.create(inputStream,
decryptionKeys, decryptionKeyDecryptor, verificationKeys, missingPublicKeyCallback);
}
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright 2018 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.decryption_verification;
import java.io.IOException;
import java.io.InputStream;
import java.util.Set;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.pgpainless.key.OpenPgpV4Fingerprint;
import org.pgpainless.key.protection.SecretKeyRingProtector;
public interface DecryptionBuilderInterface {
DecryptWith onInputStream(InputStream inputStream);
interface DecryptWith {
VerifyWith decryptWith(SecretKeyRingProtector decryptor, PGPSecretKeyRingCollection secretKeyRings);
VerifyWith doNotDecrypt();
}
interface VerifyWith {
HandleMissingPublicKeys verifyWith(PGPPublicKeyRingCollection publicKeyRings);
HandleMissingPublicKeys verifyWith(Set<OpenPgpV4Fingerprint> trustedFingerprints, PGPPublicKeyRingCollection publicKeyRings);
HandleMissingPublicKeys verifyWith(Set<PGPPublicKeyRing> publicKeyRings);
Build doNotVerify();
}
interface HandleMissingPublicKeys {
Build handleMissingPublicKeysWith(MissingPublicKeyCallback callback);
Build ignoreMissingPublicKeys();
}
interface Build {
DecryptionStream build() throws IOException, PGPException;
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright 2018 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.decryption_verification;
import java.io.IOException;
import java.io.InputStream;
public class DecryptionStream extends InputStream {
private final InputStream inputStream;
private final PainlessResult.Builder resultBuilder;
private boolean isClosed = false;
DecryptionStream(InputStream wrapped, PainlessResult.Builder resultBuilder) {
if (wrapped == null) {
throw new NullPointerException("Wrapped InputStream MUST NOT be null!");
}
this.inputStream = wrapped;
this.resultBuilder = resultBuilder;
}
@Override
public int read() throws IOException {
return inputStream.read();
}
@Override
public void close() throws IOException {
inputStream.close();
this.isClosed = true;
}
public PainlessResult getResult() {
if (!isClosed) {
throw new IllegalStateException("DecryptionStream MUST be closed before the result can be accessed.");
}
return resultBuilder.build();
}
}

View file

@ -0,0 +1,243 @@
/*
* Copyright 2018 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.decryption_verification;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPOnePassSignature;
import org.bouncycastle.openpgp.PGPOnePassSignatureList;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider;
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.key.OpenPgpV4Fingerprint;
import org.pgpainless.key.protection.SecretKeyRingProtector;
public final class DecryptionStreamFactory {
private static final Logger LOGGER = Logger.getLogger(DecryptionStreamFactory.class.getName());
private static final Level LEVEL = Level.FINE;
private final PGPSecretKeyRingCollection decryptionKeys;
private final SecretKeyRingProtector decryptionKeyDecryptor;
private final Set<PGPPublicKeyRing> verificationKeys = new HashSet<>();
private final MissingPublicKeyCallback missingPublicKeyCallback;
private final PainlessResult.Builder resultBuilder = PainlessResult.getBuilder();
private final PGPContentVerifierBuilderProvider verifierBuilderProvider = new BcPGPContentVerifierBuilderProvider();
private final KeyFingerPrintCalculator fingerCalc = new BcKeyFingerprintCalculator();
private final Map<OpenPgpV4Fingerprint, PGPOnePassSignature> verifiableOnePassSignatures = new HashMap<>();
private DecryptionStreamFactory(PGPSecretKeyRingCollection decryptionKeys,
SecretKeyRingProtector decryptor,
Set<PGPPublicKeyRing> verificationKeys,
MissingPublicKeyCallback missingPublicKeyCallback) {
this.decryptionKeys = decryptionKeys;
this.decryptionKeyDecryptor = decryptor;
this.verificationKeys.addAll(verificationKeys != null ? verificationKeys : Collections.emptyList());
this.missingPublicKeyCallback = missingPublicKeyCallback;
}
public static DecryptionStream create(InputStream inputStream,
PGPSecretKeyRingCollection decryptionKeys,
SecretKeyRingProtector decryptor,
Set<PGPPublicKeyRing> verificationKeys,
MissingPublicKeyCallback missingPublicKeyCallback)
throws IOException, PGPException {
DecryptionStreamFactory factory = new DecryptionStreamFactory(decryptionKeys,
decryptor,
verificationKeys,
missingPublicKeyCallback);
PGPObjectFactory objectFactory = new PGPObjectFactory(
PGPUtil.getDecoderStream(inputStream), new BcKeyFingerprintCalculator());
return new DecryptionStream(factory.wrap(objectFactory), factory.resultBuilder);
}
private InputStream wrap(PGPObjectFactory objectFactory) throws IOException, PGPException {
Object pgpObj;
while ((pgpObj = objectFactory.nextObject()) != null) {
if (pgpObj instanceof PGPEncryptedDataList) {
LOGGER.log(LEVEL, "Encountered PGPEncryptedDataList");
PGPEncryptedDataList encDataList = (PGPEncryptedDataList) pgpObj;
InputStream nextStream = decrypt(encDataList);
objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(nextStream), fingerCalc);
return wrap(objectFactory);
}
if (pgpObj instanceof PGPCompressedData) {
PGPCompressedData compressedData = (PGPCompressedData) pgpObj;
InputStream nextStream = compressedData.getDataStream();
resultBuilder.setCompressionAlgorithm(CompressionAlgorithm.fromId(compressedData.getAlgorithm()));
objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(nextStream), fingerCalc);
LOGGER.log(LEVEL, "Encountered PGPCompressedData: " +
CompressionAlgorithm.fromId(compressedData.getAlgorithm()));
return wrap(objectFactory);
}
if (pgpObj instanceof PGPOnePassSignatureList) {
PGPOnePassSignatureList onePassSignatures = (PGPOnePassSignatureList) pgpObj;
LOGGER.log(LEVEL, "Encountered PGPOnePassSignatureList of size " + onePassSignatures.size());
initOnePassSignatures(onePassSignatures);
return wrap(objectFactory);
}
if (pgpObj instanceof PGPLiteralData) {
LOGGER.log(LEVEL, "Found PGPLiteralData");
PGPLiteralData literalData = (PGPLiteralData) pgpObj;
InputStream literalDataInputStream = literalData.getInputStream();
if (verifiableOnePassSignatures.isEmpty()) {
LOGGER.log(LEVEL, "No OnePassSignatures found -> We are done");
return literalDataInputStream;
}
return new SignatureVerifyingInputStream(literalDataInputStream,
objectFactory, verifiableOnePassSignatures, resultBuilder);
}
}
throw new PGPException("No Literal Data Packet found");
}
private InputStream decrypt(PGPEncryptedDataList encryptedDataList)
throws PGPException {
Iterator<?> iterator = encryptedDataList.getEncryptedDataObjects();
if (!iterator.hasNext()) {
throw new PGPException("Decryption failed - EncryptedDataList has no items");
}
PGPPrivateKey decryptionKey = null;
PGPPublicKeyEncryptedData encryptedSessionKey = null;
while (iterator.hasNext()) {
PGPPublicKeyEncryptedData encryptedData = (PGPPublicKeyEncryptedData) iterator.next();
long keyId = encryptedData.getKeyID();
resultBuilder.addRecipientKeyId(keyId);
LOGGER.log(LEVEL, "PGPEncryptedData is encrypted for key " + Long.toHexString(keyId));
PGPSecretKey secretKey = decryptionKeys.getSecretKey(keyId);
if (secretKey != null) {
LOGGER.log(LEVEL, "Found respective secret key " + Long.toHexString(keyId));
encryptedSessionKey = encryptedData;
decryptionKey = secretKey.extractPrivateKey(decryptionKeyDecryptor.getDecryptor(keyId));
resultBuilder.setDecryptionFingerprint(new OpenPgpV4Fingerprint(secretKey));
}
}
if (decryptionKey == null) {
throw new PGPException("Decryption failed - No suitable decryption key found");
}
PublicKeyDataDecryptorFactory keyDecryptor = new BcPublicKeyDataDecryptorFactory(decryptionKey);
SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm
.fromId(encryptedSessionKey.getSymmetricAlgorithm(keyDecryptor));
LOGGER.log(LEVEL, "Message is encrypted using " + symmetricKeyAlgorithm);
resultBuilder.setSymmetricKeyAlgorithm(symmetricKeyAlgorithm);
if (encryptedSessionKey.isIntegrityProtected()) {
LOGGER.log(LEVEL, "Message is integrity protected");
resultBuilder.setIntegrityProtected(true);
} else {
LOGGER.log(LEVEL, "Message is not integrity protected");
resultBuilder.setIntegrityProtected(false);
}
InputStream decryptionStream = encryptedSessionKey.getDataStream(keyDecryptor);
return decryptionStream;
}
private void initOnePassSignatures(PGPOnePassSignatureList onePassSignatureList) throws PGPException {
Iterator<PGPOnePassSignature> iterator = onePassSignatureList.iterator();
if (!iterator.hasNext()) {
throw new PGPException("Verification failed - No OnePassSignatures found");
}
while (iterator.hasNext()) {
PGPOnePassSignature signature = iterator.next();
final long keyId = signature.getKeyID();
resultBuilder.addUnverifiedSignatureKeyId(keyId);
LOGGER.log(LEVEL, "Message contains OnePassSignature from " + Long.toHexString(keyId));
// Find public key
PGPPublicKey verificationKey = null;
for (PGPPublicKeyRing publicKeyRing : verificationKeys) {
verificationKey = publicKeyRing.getPublicKey(keyId);
if (verificationKey != null) {
LOGGER.log(LEVEL, "Found respective public key " + Long.toHexString(keyId));
break;
}
}
if (verificationKey == null) {
LOGGER.log(Level.INFO, "No public key for signature of " + Long.toHexString(keyId) + " found.");
if (missingPublicKeyCallback == null) {
LOGGER.log(Level.INFO, "Skip signature of " + Long.toHexString(keyId));
continue;
}
PGPPublicKey missingPublicKey = missingPublicKeyCallback.onMissingPublicKeyEncountered(keyId);
if (missingPublicKey == null) {
LOGGER.log(Level.INFO, "Skip signature of " + Long.toHexString(keyId));
continue;
}
if (missingPublicKey.getKeyID() != keyId) {
throw new IllegalArgumentException("KeyID of the provided public key differs from the signatures keyId. " +
"The signature was created from " + Long.toHexString(keyId) + " while the provided key has ID " +
Long.toHexString(missingPublicKey.getKeyID()));
}
verificationKey = missingPublicKey;
}
signature.init(verifierBuilderProvider, verificationKey);
verifiableOnePassSignatures.put(new OpenPgpV4Fingerprint(verificationKey), signature);
}
}
}

View file

@ -0,0 +1,24 @@
/*
* Copyright 2018 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.decryption_verification;
import org.bouncycastle.openpgp.PGPPublicKey;
public interface MissingPublicKeyCallback {
PGPPublicKey onMissingPublicKeyEncountered(Long keyId);
}

View file

@ -0,0 +1,159 @@
/*
* Copyright 2018 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.decryption_verification;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.key.OpenPgpV4Fingerprint;
public class PainlessResult {
private final Set<Long> recipientKeyIds;
private final OpenPgpV4Fingerprint decryptionFingerprint;
private final Set<Long> unverifiedSignatureKeyIds;
private final Set<OpenPgpV4Fingerprint> verifiedSignaturesFingerprints;
private final SymmetricKeyAlgorithm symmetricKeyAlgorithm;
private final CompressionAlgorithm compressionAlgorithm;
private final boolean integrityProtected;
public PainlessResult(Set<Long> recipientKeyIds,
OpenPgpV4Fingerprint decryptionFingerprint,
SymmetricKeyAlgorithm symmetricKeyAlgorithm,
CompressionAlgorithm algorithm,
boolean integrityProtected,
Set<Long> unverifiedSignatureKeyIds,
Set<OpenPgpV4Fingerprint> verifiedSignaturesFingerprints) {
this.recipientKeyIds = Collections.unmodifiableSet(recipientKeyIds);
this.decryptionFingerprint = decryptionFingerprint;
this.symmetricKeyAlgorithm = symmetricKeyAlgorithm;
this.compressionAlgorithm = algorithm;
this.integrityProtected = integrityProtected;
this.unverifiedSignatureKeyIds = Collections.unmodifiableSet(unverifiedSignatureKeyIds);
this.verifiedSignaturesFingerprints = Collections.unmodifiableSet(verifiedSignaturesFingerprints);
}
public Set<Long> getRecipientKeyIds() {
return recipientKeyIds;
}
public boolean isEncrypted() {
return !getRecipientKeyIds().isEmpty();
}
public OpenPgpV4Fingerprint getDecryptionFingerprint() {
return decryptionFingerprint;
}
public SymmetricKeyAlgorithm getSymmetricKeyAlgorithm() {
return symmetricKeyAlgorithm;
}
public CompressionAlgorithm getCompressionAlgorithm() {
return compressionAlgorithm;
}
public boolean isIntegrityProtected() {
return integrityProtected;
}
public Set<Long> getAllSignatureKeyFingerprints() {
return unverifiedSignatureKeyIds;
}
public boolean isSigned() {
return !unverifiedSignatureKeyIds.isEmpty();
}
public Set<OpenPgpV4Fingerprint> getVerifiedSignaturesFingerprints() {
return verifiedSignaturesFingerprints;
}
public boolean isVerified() {
return !verifiedSignaturesFingerprints.isEmpty();
}
public boolean containsVerifiedSignatureFrom(PGPPublicKeyRing publicKeys) {
for (PGPPublicKey key : publicKeys) {
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(key);
if (verifiedSignaturesFingerprints.contains(fingerprint)) {
return true;
}
}
return false;
}
static Builder getBuilder() {
return new Builder();
}
static class Builder {
private final Set<Long> recipientFingerprints = new HashSet<>();
private OpenPgpV4Fingerprint decryptionFingerprint;
private final Set<Long> unverifiedSignatureKeyIds = new HashSet<>();
private final Set<OpenPgpV4Fingerprint> verifiedSignatureFingerprints = new HashSet<>();
private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.NULL;
private CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED;
private boolean integrityProtected = false;
public Builder addRecipientKeyId(Long keyId) {
this.recipientFingerprints.add(keyId);
return this;
}
public Builder setDecryptionFingerprint(OpenPgpV4Fingerprint fingerprint) {
this.decryptionFingerprint = fingerprint;
return this;
}
public Builder setCompressionAlgorithm(CompressionAlgorithm algorithm) {
this.compressionAlgorithm = algorithm;
return this;
}
public Builder addUnverifiedSignatureKeyId(Long keyId) {
this.unverifiedSignatureKeyIds.add(keyId);
return this;
}
public Builder addVerifiedSignatureFingerprint(OpenPgpV4Fingerprint fingerprint) {
this.verifiedSignatureFingerprints.add(fingerprint);
return this;
}
public Builder setSymmetricKeyAlgorithm(SymmetricKeyAlgorithm symmetricKeyAlgorithm) {
this.symmetricKeyAlgorithm = symmetricKeyAlgorithm;
return this;
}
public Builder setIntegrityProtected(boolean integrityProtected) {
this.integrityProtected = integrityProtected;
return this;
}
public PainlessResult build() {
return new PainlessResult(recipientFingerprints, decryptionFingerprint, symmetricKeyAlgorithm, compressionAlgorithm, integrityProtected, unverifiedSignatureKeyIds, verifiedSignatureFingerprints);
}
}
}

View file

@ -0,0 +1,174 @@
/*
* Copyright 2018 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.decryption_verification;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.SignatureException;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPOnePassSignature;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureList;
import org.pgpainless.key.OpenPgpV4Fingerprint;
public class SignatureVerifyingInputStream extends FilterInputStream {
private static final Logger LOGGER = Logger.getLogger(SignatureVerifyingInputStream.class.getName());
private static final Level LEVEL = Level.INFO;
private final PGPObjectFactory objectFactory;
private final Map<OpenPgpV4Fingerprint, PGPOnePassSignature> onePassSignatures;
private final PainlessResult.Builder resultBuilder;
private boolean validated = false;
protected SignatureVerifyingInputStream(InputStream inputStream,
PGPObjectFactory objectFactory,
Map<OpenPgpV4Fingerprint, PGPOnePassSignature> onePassSignatures,
PainlessResult.Builder resultBuilder) {
super(inputStream);
this.objectFactory = objectFactory;
this.resultBuilder = resultBuilder;
this.onePassSignatures = onePassSignatures;
LOGGER.log(LEVEL, "Begin verifying OnePassSignatures");
}
private void updateOnePassSignatures(byte data) {
for (PGPOnePassSignature signature : onePassSignatures.values()) {
signature.update(data);
}
}
private void updateOnePassSignatures(byte[] b, int off, int len) {
for (PGPOnePassSignature signature : onePassSignatures.values()) {
signature.update(b, off, len);
}
}
private void validateOnePassSignatures() throws IOException {
if (validated) {
LOGGER.log(LEVEL, "Validated signatures already. Skip");
return;
}
validated = true;
if (onePassSignatures.isEmpty()) {
LOGGER.log(LEVEL, "No One-Pass-Signatures found -> No validation");
return;
}
try {
PGPSignatureList signatureList = null;
Object obj = objectFactory.nextObject();
while (obj != null && signatureList == null) {
if (obj instanceof PGPSignatureList) {
signatureList = (PGPSignatureList) obj;
} else {
obj = objectFactory.nextObject();
}
}
if (signatureList == null || signatureList.isEmpty()) {
throw new IOException("Verification failed - No Signatures found");
}
for (PGPSignature signature : signatureList) {
OpenPgpV4Fingerprint fingerprint = null;
for (OpenPgpV4Fingerprint f : onePassSignatures.keySet()) {
if (f.getKeyId() == signature.getKeyID()) {
fingerprint = f;
break;
}
}
PGPOnePassSignature onePassSignature;
if (fingerprint == null || (onePassSignature = onePassSignatures.get(fingerprint)) == null) {
LOGGER.log(LEVEL, "Found Signature without respective OnePassSignature packet -> skip");
continue;
}
if (!onePassSignature.verify(signature)) {
throw new SignatureException("Bad Signature of key " + signature.getKeyID());
} else {
LOGGER.log(LEVEL, "Verified signature of key " + Long.toHexString(signature.getKeyID()));
resultBuilder.addVerifiedSignatureFingerprint(fingerprint);
}
}
} catch (PGPException | SignatureException e) {
throw new IOException(e.getMessage(), e);
}
}
@Override
public int read() throws IOException {
final int data = super.read();
final boolean endOfStream = data == -1;
if (endOfStream) {
validateOnePassSignatures();
} else {
updateOnePassSignatures((byte) data);
}
return data;
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int read = super.read(b, off, len);
final boolean endOfStream = read == -1;
if (endOfStream) {
validateOnePassSignatures();
} else {
updateOnePassSignatures(b, off, read);
}
return read;
}
@Override
public long skip(long n) {
throw new UnsupportedOperationException("skip() is not supported");
}
@Override
public synchronized void mark(int readlimit) {
throw new UnsupportedOperationException("mark() not supported");
}
@Override
public synchronized void reset() {
throw new UnsupportedOperationException("reset() is not supported");
}
@Override
public boolean markSupported() {
return false;
}
}

View file

@ -0,0 +1,19 @@
/*
* Copyright 2018 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 used to decryption and verification of OpenPGP encrypted / signed data.
*/
package org.pgpainless.decryption_verification;

View file

@ -0,0 +1,345 @@
/*
* Copyright 2018 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.encryption_signing;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.key.selection.key.PublicKeySelectionStrategy;
import org.pgpainless.key.selection.key.SecretKeySelectionStrategy;
import org.pgpainless.key.selection.key.impl.And;
import org.pgpainless.key.selection.key.impl.EncryptionKeySelectionStrategy;
import org.pgpainless.key.selection.key.impl.NoRevocation;
import org.pgpainless.key.selection.key.impl.SignatureKeySelectionStrategy;
import org.pgpainless.key.selection.keyring.PublicKeyRingSelectionStrategy;
import org.pgpainless.key.selection.keyring.SecretKeyRingSelectionStrategy;
import org.pgpainless.util.MultiMap;
public class EncryptionBuilder implements EncryptionBuilderInterface {
private OutputStream outputStream;
private final Set<PGPPublicKey> encryptionKeys = new HashSet<>();
private final Set<PGPSecretKey> signingKeys = new HashSet<>();
private SecretKeyRingProtector signingKeysDecryptor;
private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.AES_128;
private HashAlgorithm hashAlgorithm = HashAlgorithm.SHA256;
private CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED;
private boolean asciiArmor = false;
@Override
public ToRecipients onOutputStream(OutputStream outputStream) {
this.outputStream = outputStream;
return new ToRecipientsImpl();
}
class ToRecipientsImpl implements ToRecipients {
@Override
public WithAlgorithms toRecipients(PGPPublicKey... keys) {
for (PGPPublicKey k : keys) {
if (encryptionKeySelector().accept(null, k)) {
EncryptionBuilder.this.encryptionKeys.add(k);
} else {
throw new IllegalArgumentException("Key " + k.getKeyID() + " is not a valid encryption key.");
}
}
if (EncryptionBuilder.this.encryptionKeys.isEmpty()) {
throw new IllegalStateException("No valid encryption keys found!");
}
return new WithAlgorithmsImpl();
}
@Override
public WithAlgorithms toRecipients(PGPPublicKeyRing... keys) {
for (PGPPublicKeyRing ring : keys) {
for (PGPPublicKey k : ring) {
if (encryptionKeySelector().accept(null, k)) {
EncryptionBuilder.this.encryptionKeys.add(k);
}
}
}
if (EncryptionBuilder.this.encryptionKeys.isEmpty()) {
throw new IllegalStateException("No valid encryption keys found!");
}
return new WithAlgorithmsImpl();
}
@Override
public WithAlgorithms toRecipients(PGPPublicKeyRingCollection... keys) {
for (PGPPublicKeyRingCollection collection : keys) {
for (PGPPublicKeyRing ring : collection) {
for (PGPPublicKey k : ring) {
if (encryptionKeySelector().accept(null, k)) {
EncryptionBuilder.this.encryptionKeys.add(k);
}
}
}
}
if (EncryptionBuilder.this.encryptionKeys.isEmpty()) {
throw new IllegalStateException("No valid encryption keys found!");
}
return new WithAlgorithmsImpl();
}
@Override
public <O> WithAlgorithms toRecipients(PublicKeyRingSelectionStrategy<O> ringSelectionStrategy,
MultiMap<O, PGPPublicKeyRingCollection> keys) {
if (keys.isEmpty()) {
throw new IllegalArgumentException("Recipient map MUST NOT be empty.");
}
MultiMap<O, PGPPublicKeyRing> acceptedKeyRings = ringSelectionStrategy.selectKeyRingsFromCollections(keys);
for (O identifier : acceptedKeyRings.keySet()) {
Set<PGPPublicKeyRing> acceptedSet = acceptedKeyRings.get(identifier);
for (PGPPublicKeyRing ring : acceptedSet) {
for (PGPPublicKey k : ring) {
if (encryptionKeySelector().accept(null, k)) {
EncryptionBuilder.this.encryptionKeys.add(k);
}
}
}
}
if (EncryptionBuilder.this.encryptionKeys.isEmpty()) {
throw new IllegalStateException("No valid encryption keys found!");
}
return new WithAlgorithmsImpl();
}
@Override
public SignWith doNotEncrypt() {
return new SignWithImpl();
}
}
class WithAlgorithmsImpl implements WithAlgorithms {
@Override
public WithAlgorithms andToSelf(PGPPublicKey... keys) {
if (keys.length == 0) {
throw new IllegalArgumentException("Recipient list MUST NOT be empty.");
}
for (PGPPublicKey k : keys) {
if (encryptionKeySelector().accept(null, k)) {
EncryptionBuilder.this.encryptionKeys.add(k);
} else {
throw new IllegalArgumentException("Key " + k.getKeyID() + " is not a valid encryption key.");
}
}
return this;
}
@Override
public WithAlgorithms andToSelf(PGPPublicKeyRing... keys) {
if (keys.length == 0) {
throw new IllegalArgumentException("Recipient list MUST NOT be empty.");
}
for (PGPPublicKeyRing ring : keys) {
for (Iterator<PGPPublicKey> i = ring.getPublicKeys(); i.hasNext(); ) {
PGPPublicKey key = i.next();
if (encryptionKeySelector().accept(null, key)) {
EncryptionBuilder.this.encryptionKeys.add(key);
}
}
}
return this;
}
@Override
public WithAlgorithms andToSelf(PGPPublicKeyRingCollection keys) {
for (PGPPublicKeyRing ring : keys) {
for (Iterator<PGPPublicKey> i = ring.getPublicKeys(); i.hasNext(); ) {
PGPPublicKey key = i.next();
if (encryptionKeySelector().accept(null, key)) {
EncryptionBuilder.this.encryptionKeys.add(key);
}
}
}
return this;
}
public <O> WithAlgorithms andToSelf(PublicKeyRingSelectionStrategy<O> ringSelectionStrategy,
MultiMap<O, PGPPublicKeyRingCollection> keys) {
if (keys.isEmpty()) {
throw new IllegalArgumentException("Recipient list MUST NOT be empty.");
}
MultiMap<O, PGPPublicKeyRing> acceptedKeyRings =
ringSelectionStrategy.selectKeyRingsFromCollections(keys);
for (O identifier : acceptedKeyRings.keySet()) {
Set<PGPPublicKeyRing> acceptedSet = acceptedKeyRings.get(identifier);
for (PGPPublicKeyRing k : acceptedSet) {
for (Iterator<PGPPublicKey> i = k.getPublicKeys(); i.hasNext(); ) {
PGPPublicKey key = i.next();
if (encryptionKeySelector().accept(null, key)) {
EncryptionBuilder.this.encryptionKeys.add(key);
}
}
}
}
return this;
}
@Override
public SignWith usingAlgorithms(SymmetricKeyAlgorithm symmetricKeyAlgorithm,
HashAlgorithm hashAlgorithm,
CompressionAlgorithm compressionAlgorithm) {
EncryptionBuilder.this.symmetricKeyAlgorithm = symmetricKeyAlgorithm;
EncryptionBuilder.this.hashAlgorithm = hashAlgorithm;
EncryptionBuilder.this.compressionAlgorithm = compressionAlgorithm;
return new SignWithImpl();
}
@Override
public SignWith usingSecureAlgorithms() {
EncryptionBuilder.this.symmetricKeyAlgorithm = SymmetricKeyAlgorithm.AES_256;
EncryptionBuilder.this.hashAlgorithm = HashAlgorithm.SHA512;
EncryptionBuilder.this.compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED;
return new SignWithImpl();
}
}
class SignWithImpl implements SignWith {
@Override
public <O> Armor signWith(SecretKeyRingProtector decryptor, PGPSecretKey... keys) {
if (keys.length == 0) {
throw new IllegalArgumentException("Recipient list MUST NOT be empty.");
}
for (PGPSecretKey s : keys) {
if (EncryptionBuilder.this.<O>signingKeySelector().accept(null, s)) {
signingKeys.add(s);
} else {
throw new IllegalArgumentException("Key " + s.getKeyID() + " is not a valid signing key.");
}
}
EncryptionBuilder.this.signingKeysDecryptor = decryptor;
return new ArmorImpl();
}
@Override
public <O> Armor signWith(SecretKeyRingProtector decryptor, PGPSecretKeyRing... keys) {
if (keys.length == 0) {
throw new IllegalArgumentException("Recipient list MUST NOT be empty.");
}
for (PGPSecretKeyRing key : keys) {
for (Iterator<PGPSecretKey> i = key.getSecretKeys(); i.hasNext(); ) {
PGPSecretKey s = i.next();
if (EncryptionBuilder.this.<O>signingKeySelector().accept(null, s)) {
EncryptionBuilder.this.signingKeys.add(s);
}
}
}
EncryptionBuilder.this.signingKeysDecryptor = decryptor;
return new ArmorImpl();
}
@Override
public <O> Armor signWith(SecretKeyRingSelectionStrategy<O> ringSelectionStrategy,
SecretKeyRingProtector decryptor,
MultiMap<O, PGPSecretKeyRingCollection> keys) {
if (keys.isEmpty()) {
throw new IllegalArgumentException("Recipient list MUST NOT be empty.");
}
MultiMap<O, PGPSecretKeyRing> acceptedKeyRings =
ringSelectionStrategy.selectKeyRingsFromCollections(keys);
for (O identifier : acceptedKeyRings.keySet()) {
Set<PGPSecretKeyRing> acceptedSet = acceptedKeyRings.get(identifier);
for (PGPSecretKeyRing k : acceptedSet) {
for (Iterator<PGPSecretKey> i = k.getSecretKeys(); i.hasNext(); ) {
PGPSecretKey s = i.next();
if (EncryptionBuilder.this.<O>signingKeySelector().accept(null, s)) {
EncryptionBuilder.this.signingKeys.add(s);
}
}
}
}
return new ArmorImpl();
}
@Override
public Armor doNotSign() {
return new ArmorImpl();
}
}
class ArmorImpl implements Armor {
@Override
public EncryptionStream asciiArmor() throws IOException, PGPException {
EncryptionBuilder.this.asciiArmor = true;
return build();
}
@Override
public EncryptionStream noArmor() throws IOException, PGPException {
EncryptionBuilder.this.asciiArmor = false;
return build();
}
private EncryptionStream build() throws IOException, PGPException {
Set<PGPPrivateKey> privateKeys = new HashSet<>();
for (PGPSecretKey secretKey : signingKeys) {
privateKeys.add(secretKey.extractPrivateKey(signingKeysDecryptor.getDecryptor(secretKey.getKeyID())));
}
return EncryptionStream.create(
EncryptionBuilder.this.outputStream,
EncryptionBuilder.this.encryptionKeys,
privateKeys,
EncryptionBuilder.this.symmetricKeyAlgorithm,
EncryptionBuilder.this.hashAlgorithm,
EncryptionBuilder.this.compressionAlgorithm,
EncryptionBuilder.this.asciiArmor);
}
}
<O> PublicKeySelectionStrategy<O> encryptionKeySelector() {
return new And.PubKeySelectionStrategy<>(
new NoRevocation.PubKeySelectionStrategy<>(),
new EncryptionKeySelectionStrategy<>());
}
<O> SecretKeySelectionStrategy<O> signingKeySelector() {
return new And.SecKeySelectionStrategy<>(
new NoRevocation.SecKeySelectionStrategy<>(),
new SignatureKeySelectionStrategy<>());
}
}

View file

@ -0,0 +1,98 @@
/*
* Copyright 2018 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.encryption_signing;
import java.io.IOException;
import java.io.OutputStream;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.exception.SecretKeyNotFoundException;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.key.selection.keyring.PublicKeyRingSelectionStrategy;
import org.pgpainless.key.selection.keyring.SecretKeyRingSelectionStrategy;
import org.pgpainless.util.MultiMap;
public interface EncryptionBuilderInterface {
ToRecipients onOutputStream(OutputStream outputStream);
interface ToRecipients {
WithAlgorithms toRecipients(PGPPublicKey... keys);
WithAlgorithms toRecipients(PGPPublicKeyRing... keys);
WithAlgorithms toRecipients(PGPPublicKeyRingCollection... keys);
<O> WithAlgorithms toRecipients(PublicKeyRingSelectionStrategy<O> selectionStrategy,
MultiMap<O, PGPPublicKeyRingCollection> keys);
SignWith doNotEncrypt();
}
interface WithAlgorithms {
WithAlgorithms andToSelf(PGPPublicKey... keys);
WithAlgorithms andToSelf(PGPPublicKeyRing... keys);
WithAlgorithms andToSelf(PGPPublicKeyRingCollection keys);
<O> WithAlgorithms andToSelf(PublicKeyRingSelectionStrategy<O> selectionStrategy,
MultiMap<O, PGPPublicKeyRingCollection> keys);
SignWith usingAlgorithms(SymmetricKeyAlgorithm symmetricKeyAlgorithm,
HashAlgorithm hashAlgorithm,
CompressionAlgorithm compressionAlgorithm);
SignWith usingSecureAlgorithms();
}
interface SignWith {
<O> Armor signWith(SecretKeyRingProtector decryptor, PGPSecretKey... keys);
<O> Armor signWith(SecretKeyRingProtector decryptor, PGPSecretKeyRing... keyRings);
<O> Armor signWith(SecretKeyRingSelectionStrategy<O> selectionStrategy,
SecretKeyRingProtector decryptor,
MultiMap<O, PGPSecretKeyRingCollection> keys)
throws SecretKeyNotFoundException;
Armor doNotSign();
}
interface Armor {
EncryptionStream asciiArmor() throws IOException, PGPException;
EncryptionStream noArmor() throws IOException, PGPException;
}
}

View file

@ -0,0 +1,265 @@
/*
* Copyright 2018 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.encryption_signing;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.decryption_verification.PainlessResult;
/**
* This class is based upon Jens Neuhalfen's Bouncy-GPG PGPEncryptingStream.
* @see <a href="https://github.com/neuhalje/bouncy-gpg/blob/master/src/main/java/name/neuhalfen/projects/crypto/bouncycastle/openpgp/encrypting/PGPEncryptingStream.java">Source</a>
*/
public final class EncryptionStream extends OutputStream {
private static final Logger LOGGER = Logger.getLogger(EncryptionStream.class.getName());
private static final Level LEVEL = Level.FINE;
private static final int BUFFER_SIZE = 1 << 8;
private final PainlessResult result;
private List<PGPSignatureGenerator> signatureGenerators = new ArrayList<>();
private boolean closed = false;
// ASCII Armor
private ArmoredOutputStream armorOutputStream = null;
// Public Key Encryption of Symmetric Session Key
private OutputStream publicKeyEncryptedStream = null;
// Data Compression
private PGPCompressedDataGenerator compressedDataGenerator;
private BCPGOutputStream basicCompressionStream;
// Literal Data
private PGPLiteralDataGenerator literalDataGenerator;
private OutputStream literalDataStream;
private EncryptionStream(OutputStream targetOutputStream,
Set<PGPPublicKey> encryptionKeys,
Set<PGPPrivateKey> signingKeys,
SymmetricKeyAlgorithm symmetricKeyAlgorithm,
HashAlgorithm hashAlgorithm,
CompressionAlgorithm compressionAlgorithm,
boolean asciiArmor)
throws IOException, PGPException {
// Currently outermost Stream
OutputStream outerMostStream;
if (asciiArmor) {
LOGGER.log(LEVEL, "Wrap encryption output in ASCII armor");
armorOutputStream = new ArmoredOutputStream(targetOutputStream);
outerMostStream = armorOutputStream;
} else {
LOGGER.log(LEVEL, "Encryption output will be binary");
outerMostStream = targetOutputStream;
}
// If we want to encrypt
if (!encryptionKeys.isEmpty()) {
LOGGER.log(LEVEL, "At least one encryption key is available -> encrypt using " + symmetricKeyAlgorithm);
BcPGPDataEncryptorBuilder dataEncryptorBuilder =
new BcPGPDataEncryptorBuilder(symmetricKeyAlgorithm.getAlgorithmId());
LOGGER.log(LEVEL, "Integrity protection enabled");
dataEncryptorBuilder.setWithIntegrityPacket(true);
PGPEncryptedDataGenerator encryptedDataGenerator =
new PGPEncryptedDataGenerator(dataEncryptorBuilder);
for (PGPPublicKey key : encryptionKeys) {
LOGGER.log(LEVEL, "Encrypt for key " + Long.toHexString(key.getKeyID()));
encryptedDataGenerator.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(key));
}
publicKeyEncryptedStream = encryptedDataGenerator.open(outerMostStream, new byte[BUFFER_SIZE]);
outerMostStream = publicKeyEncryptedStream;
}
// If we want to sign, prepare for signing
if (!signingKeys.isEmpty()) {
LOGGER.log(LEVEL, "At least one signing key is available -> sign " + hashAlgorithm + " hash of message");
for (PGPPrivateKey privateKey : signingKeys) {
LOGGER.log(LEVEL, "Sign using key " + Long.toHexString(privateKey.getKeyID()));
BcPGPContentSignerBuilder contentSignerBuilder = new BcPGPContentSignerBuilder(
privateKey.getPublicKeyPacket().getAlgorithm(), hashAlgorithm.getAlgorithmId());
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, privateKey);
signatureGenerators.add(signatureGenerator);
}
}
LOGGER.log(LEVEL, "Compress using " + compressionAlgorithm);
// Compression
compressedDataGenerator = new PGPCompressedDataGenerator(
compressionAlgorithm.getAlgorithmId());
basicCompressionStream = new BCPGOutputStream(compressedDataGenerator.open(outerMostStream));
// If we want to sign, sign!
for (PGPSignatureGenerator signatureGenerator : signatureGenerators) {
signatureGenerator.generateOnePassVersion(false).encode(basicCompressionStream);
}
literalDataGenerator = new PGPLiteralDataGenerator();
literalDataStream = literalDataGenerator.open(basicCompressionStream,
PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, new Date(), new byte[BUFFER_SIZE]);
// Prepare result
Set<Long> recipientKeyIds = new HashSet<>();
for (PGPPublicKey recipient : encryptionKeys) {
recipientKeyIds.add(recipient.getKeyID());
}
Set<Long> signingKeyIds = new HashSet<>();
for (PGPPrivateKey signer : signingKeys) {
signingKeyIds.add(signer.getKeyID());
}
this.result = new PainlessResult(recipientKeyIds,
null, symmetricKeyAlgorithm,
compressionAlgorithm, true,
signingKeyIds, Collections.emptySet());
}
static EncryptionStream create(OutputStream outputStream,
Set<PGPPublicKey> encryptionKeys,
Set<PGPPrivateKey> signingKeys,
SymmetricKeyAlgorithm symmetricKeyAlgorithm,
HashAlgorithm hashAlgorithm,
CompressionAlgorithm compressionAlgorithm,
boolean asciiArmor)
throws IOException, PGPException {
requireNonNull(outputStream, "targetOutputStream");
requireNonNull(encryptionKeys, "encryptionKeys");
requireNonNull(signingKeys, "signingKeys");
requireNonNull(symmetricKeyAlgorithm, "symmetricKeyAlgorithm");
requireNonNull(hashAlgorithm, "hashAlgorithm");
requireNonNull(compressionAlgorithm, "compressionAlgorithm");
return new EncryptionStream(outputStream,
encryptionKeys,
signingKeys,
symmetricKeyAlgorithm,
hashAlgorithm,
compressionAlgorithm,
asciiArmor);
}
@Override
public void write(int data) throws IOException {
literalDataStream.write(data);
for (PGPSignatureGenerator signatureGenerator : signatureGenerators) {
byte asByte = (byte) (data & 0xff);
signatureGenerator.update(asByte);
}
}
@Override
public void write(byte[] buffer) throws IOException {
write(buffer, 0, buffer.length);
}
@Override
public void write(byte[] buffer, int off, int len) throws IOException {
literalDataStream.write(buffer, 0, len);
for (PGPSignatureGenerator signatureGenerator : signatureGenerators) {
signatureGenerator.update(buffer, 0, len);
}
}
@Override
public void flush() throws IOException {
literalDataStream.flush();
}
@Override
public void close() throws IOException {
if (!closed) {
// Literal Data
literalDataStream.flush();
literalDataStream.close();
literalDataGenerator.close();
// Signing
for (PGPSignatureGenerator signatureGenerator : signatureGenerators) {
try {
signatureGenerator.generate().encode(basicCompressionStream);
} catch (PGPException e) {
throw new IOException(e);
}
}
// Compressed Data
compressedDataGenerator.close();
// Public Key Encryption
if (publicKeyEncryptedStream != null) {
publicKeyEncryptedStream.flush();
publicKeyEncryptedStream.close();
}
// Armor
if (armorOutputStream != null) {
armorOutputStream.flush();
armorOutputStream.close();
}
closed = true;
}
}
private static void requireNonNull(Object o, String name) {
if (o == null) {
throw new IllegalArgumentException("Argument '" + name + "' MUST NOT be null.");
}
}
public PainlessResult getResult() {
return result;
}
}

View file

@ -0,0 +1,19 @@
/*
* Copyright 2018 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 used to encrypt or sign data using OpenPGP.
*/
package org.pgpainless.encryption_signing;

View file

@ -0,0 +1,38 @@
/*
* Copyright 2018 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.exception;
import org.bouncycastle.openpgp.PGPException;
public class PublicKeyNotFoundException extends Exception {
private static final long serialVersionUID = 1L;
private long keyId;
public PublicKeyNotFoundException(long keyId) {
super("No PGPPublicKey with id " + Long.toHexString(keyId) + " (" + keyId + ") found.");
this.keyId = keyId;
}
public PublicKeyNotFoundException(PGPException e) {
}
public long getKeyId() {
return keyId;
}
}

View file

@ -0,0 +1,32 @@
/*
* Copyright 2018 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.exception;
public class SecretKeyNotFoundException extends Exception {
private static final long serialVersionUID = 1L;
private long keyId;
public SecretKeyNotFoundException(long keyId) {
super("No PGPSecretKey with id " + Long.toHexString(keyId) + " (" + keyId + ") found.");
this.keyId = keyId;
}
public long getKeyId() {
return keyId;
}
}

View file

@ -0,0 +1,19 @@
/*
* Copyright 2018 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.
*/
/**
* Exceptions.
*/
package org.pgpainless.exception;

View file

@ -0,0 +1,149 @@
/*
* Copyright 2018 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;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Arrays;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.util.encoders.Hex;
/**
* This class represents an hex encoded, uppercase OpenPGP v4 fingerprint.
*/
public class OpenPgpV4Fingerprint implements CharSequence, Comparable<OpenPgpV4Fingerprint> {
private final String fingerprint;
/**
* Create an {@link OpenPgpV4Fingerprint}.
* @see <a href="https://xmpp.org/extensions/xep-0373.html#annoucning-pubkey">
* XEP-0373 §4.1: The OpenPGP Public-Key Data Node about how to obtain the fingerprint</a>
* @param fingerprint hexadecimal representation of the fingerprint.
*/
public OpenPgpV4Fingerprint(String fingerprint) {
if (fingerprint == null) {
throw new NullPointerException("Fingerprint MUST NOT be null.");
}
String fp = fingerprint.trim().toUpperCase();
if (!isValid(fp)) {
throw new IllegalArgumentException("Fingerprint " + fingerprint +
" does not appear to be a valid OpenPGP v4 fingerprint.");
}
this.fingerprint = fp;
}
public OpenPgpV4Fingerprint(byte[] bytes) {
this(new String(bytes, Charset.forName("UTF-8")));
}
public OpenPgpV4Fingerprint(PGPPublicKey key) {
this(Hex.encode(key.getFingerprint()));
if (key.getVersion() != 4) {
throw new IllegalArgumentException("Key is not a v4 OpenPgp key.");
}
}
public OpenPgpV4Fingerprint(PGPSecretKey key) {
this(key.getPublicKey());
}
public OpenPgpV4Fingerprint(PGPPublicKeyRing ring) {
this(ring.getPublicKey());
}
public OpenPgpV4Fingerprint(PGPSecretKeyRing ring) {
this(ring.getPublicKey());
}
/**
* Check, whether the fingerprint consists of 40 valid hexadecimal characters.
* @param fp fingerprint to check.
* @return true if fingerprint is valid.
*/
private boolean isValid(String fp) {
return fp.matches("[0-9A-F]{40}");
}
/**
* Return the key id of the OpenPGP public key this {@link OpenPgpV4Fingerprint} belongs to.
*
* @see <a href="https://tools.ietf.org/html/rfc4880#section-12.2">
* RFC-4880 §12.2: Key IDs and Fingerprints</a>
* @return key id
*/
public long getKeyId() {
byte[] bytes = new BigInteger(toString(), 16).toByteArray();
if (bytes.length != toString().length() / 2) {
bytes = Arrays.copyOfRange(bytes, 1, bytes.length);
}
byte[] lower8Bytes = Arrays.copyOfRange(bytes, 12, 20);
ByteBuffer byteBuffer = ByteBuffer.allocate(8);
byteBuffer.put(lower8Bytes);
byteBuffer.flip();
return byteBuffer.getLong();
}
@Override
public boolean equals(Object other) {
if (other == null) {
return false;
}
if (!(other instanceof CharSequence)) {
return false;
}
return this.toString().equals(other.toString());
}
@Override
public int hashCode() {
return fingerprint.hashCode();
}
@Override
public int length() {
return fingerprint.length();
}
@Override
public char charAt(int i) {
return fingerprint.charAt(i);
}
@Override
public CharSequence subSequence(int i, int i1) {
return fingerprint.subSequence(i, i1);
}
@Override
public String toString() {
return fingerprint;
}
@Override
public int compareTo(OpenPgpV4Fingerprint openPgpV4Fingerprint) {
return fingerprint.compareTo(openPgpV4Fingerprint.fingerprint);
}
}

View file

@ -0,0 +1,99 @@
/*
* Copyright 2018 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.collection;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.pgpainless.PGPainless;
public class KeyRingCollection {
private static final Logger LOGGER = Logger.getLogger(KeyRingCollection.class.getName());
private PGPPublicKeyRingCollection publicKeys;
private PGPSecretKeyRingCollection secretKeys;
public KeyRingCollection(PGPPublicKeyRingCollection publicKeyRings, PGPSecretKeyRingCollection secretKeyRings) {
this.publicKeys = publicKeyRings;
this.secretKeys = secretKeyRings;
}
public KeyRingCollection(File pubRingFile, File secRingFile) throws IOException, PGPException {
if (pubRingFile != null) {
InputStream pubRingIn = new FileInputStream(pubRingFile);
this.publicKeys = PGPainless.readKeyRing().publicKeyRingCollection(pubRingIn);
pubRingIn.close();
}
if (secRingFile != null) {
InputStream secRingIn = new FileInputStream(secRingFile);
this.secretKeys = PGPainless.readKeyRing().secretKeyRingCollection(secRingIn);
secRingIn.close();
}
}
public KeyRingCollection(PGPPublicKeyRingCollection publicKeyRings) {
this(publicKeyRings, null);
}
public KeyRingCollection(PGPSecretKeyRingCollection secretKeyRings) {
this(null, secretKeyRings);
}
public void importPublicKeys(PGPPublicKeyRingCollection publicKeyRings) {
if (this.publicKeys == null) {
this.publicKeys = publicKeyRings;
return;
}
for (PGPPublicKeyRing keyRing : publicKeyRings) {
try {
this.publicKeys = PGPPublicKeyRingCollection.addPublicKeyRing(this.publicKeys, keyRing);
} catch (IllegalArgumentException e) {
// TODO: merge key rings.
LOGGER.log(Level.FINE, "Keyring " + Long.toHexString(keyRing.getPublicKey().getKeyID()) +
" is already included in the collection. Skip!");
}
}
}
public void importSecretKeys(PGPSecretKeyRingCollection secretKeyRings) {
if (this.secretKeys == null) {
this.secretKeys = secretKeyRings;
return;
}
for (PGPSecretKeyRing keyRing : secretKeyRings) {
try {
this.secretKeys = PGPSecretKeyRingCollection.addSecretKeyRing(this.secretKeys, keyRing);
} catch (IllegalArgumentException e) {
// TODO: merge key rings.
LOGGER.log(Level.FINE, "Keyring " + Long.toHexString(keyRing.getPublicKey().getKeyID()) +
" is already included in the collection. Skip!");
}
}
}
}

View file

@ -0,0 +1,70 @@
/*
* Copyright 2018 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.collection;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.pgpainless.key.OpenPgpV4Fingerprint;
public class PGPKeyRing {
private PGPPublicKeyRing publicKeys;
private PGPSecretKeyRing secretKeys;
public PGPKeyRing(PGPPublicKeyRing publicKeys, PGPSecretKeyRing secretKeys) {
if (secretKeys == null && publicKeys == null) {
throw new IllegalArgumentException("publicKeys and secretKeys MUST NOT be both null.");
}
if (publicKeys != null && secretKeys != null) {
if (publicKeys.getPublicKey().getKeyID() != secretKeys.getPublicKey().getKeyID()) {
throw new IllegalArgumentException("publicKeys and secretKeys must have the same master key.");
}
}
this.publicKeys = publicKeys;
this.secretKeys = secretKeys;
}
public long getKeyId() {
return getMasterKey().getKeyID();
}
public PGPPublicKey getMasterKey() {
PGPPublicKey publicKey = hasSecretKeys() ? secretKeys.getPublicKey() : publicKeys.getPublicKey();
if (!publicKey.isMasterKey()) {
throw new IllegalStateException("Expected master key is not a master key");
}
return publicKey;
}
public OpenPgpV4Fingerprint getV4Fingerprint() {
return new OpenPgpV4Fingerprint(getMasterKey());
}
public boolean hasSecretKeys() {
return secretKeys != null;
}
public PGPPublicKeyRing getPublicKeys() {
return publicKeys;
}
public PGPSecretKeyRing getSecretKeys() {
return secretKeys;
}
}

View file

@ -0,0 +1,19 @@
/*
* Copyright 2018 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.
*/
/**
* OpenPGP key collections.
*/
package org.pgpainless.key.collection;

View file

@ -0,0 +1,240 @@
/*
* Copyright 2018 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.generation;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPKeyPair;
import org.bouncycastle.openpgp.PGPKeyRingGenerator;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.key.collection.PGPKeyRing;
import org.pgpainless.key.generation.type.ECDH;
import org.pgpainless.key.generation.type.ECDSA;
import org.pgpainless.key.generation.type.KeyType;
import org.pgpainless.key.generation.type.RSA_GENERAL;
import org.pgpainless.key.generation.type.curve.EllipticCurve;
import org.pgpainless.key.generation.type.length.RsaLength;
import org.pgpainless.util.KeyRingSubKeyFix;
import org.pgpainless.util.Passphrase;
public class KeyRingBuilder implements KeyRingBuilderInterface {
private final Charset UTF8 = Charset.forName("UTF-8");
private List<KeySpec> keySpecs = new ArrayList<>();
private String userId;
private Passphrase passphrase;
/**
* Creates a simple RSA KeyPair of length {@code length} with user-id {@code userId}.
* The KeyPair consists of a single RSA master key which is used for signing, encryption and certification.
*
* @param userId user id.
* @param length length in bits.
* @return {@link PGPSecretKeyRing} containing the KeyPair.
* @throws PGPException
* @throws NoSuchAlgorithmException
* @throws NoSuchProviderException
* @throws InvalidAlgorithmParameterException
*/
public PGPKeyRing simpleRsaKeyRing(String userId, RsaLength length)
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
return withMasterKey(
KeySpec.getBuilder(RSA_GENERAL.withLength(length))
.withDefaultKeyFlags()
.withDefaultAlgorithms())
.withPrimaryUserId(userId)
.withoutPassphrase()
.build();
}
/**
* Creates a key ring consisting of an ECDSA master key and an ECDH sub-key.
* The ECDSA master key is used for signing messages and certifying the sub key.
* The ECDH sub-key is used for encryption of messages.
*
* @param userId user-id
* @return {@link PGPSecretKeyRing} containing the key pairs.
* @throws PGPException
* @throws NoSuchAlgorithmException
* @throws NoSuchProviderException
* @throws InvalidAlgorithmParameterException
*/
public PGPKeyRing simpleEcKeyRing(String userId)
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
return withSubKey(
KeySpec.getBuilder(ECDH.fromCurve(EllipticCurve._P256))
.withKeyFlags(KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS)
.withDefaultAlgorithms())
.withMasterKey(
KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256))
.withKeyFlags(KeyFlag.AUTHENTICATION, KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)
.withDefaultAlgorithms())
.withPrimaryUserId(userId)
.withoutPassphrase()
.build();
}
@Override
public KeyRingBuilderInterface withSubKey(KeySpec type) {
KeyRingBuilder.this.keySpecs.add(type);
return this;
}
@Override
public WithPrimaryUserId withMasterKey(KeySpec spec) {
if ((spec.getSubpackets().getKeyFlags() & KeyFlags.CERTIFY_OTHER) == 0) {
throw new IllegalArgumentException("Certification Key MUST have KeyFlag CERTIFY_OTHER");
}
KeyRingBuilder.this.keySpecs.add(0, spec);
return new WithPrimaryUserIdImpl();
}
class WithPrimaryUserIdImpl implements WithPrimaryUserId {
@Override
public WithPassphrase withPrimaryUserId(String userId) {
KeyRingBuilder.this.userId = userId;
return new WithPassphraseImpl();
}
@Override
public WithPassphrase withPrimaryUserId(byte[] userId) {
return withPrimaryUserId(new String(userId, UTF8));
}
}
class WithPassphraseImpl implements WithPassphrase {
@Override
public Build withPassphrase(Passphrase passphrase) {
KeyRingBuilder.this.passphrase = passphrase;
return new BuildImpl();
}
@Override
public Build withoutPassphrase() {
KeyRingBuilder.this.passphrase = null;
return new BuildImpl();
}
class BuildImpl implements Build {
@Override
public PGPKeyRing build() throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
InvalidAlgorithmParameterException {
// Hash Calculator
PGPDigestCalculator calculator = new JcaPGPDigestCalculatorProviderBuilder()
.setProvider(BouncyCastleProvider.PROVIDER_NAME)
.build()
.get(HashAlgorithm.SHA1.getAlgorithmId());
// Encryptor for encrypting secret keys
PBESecretKeyEncryptor encryptor = passphrase == null ?
null : // unencrypted key pair, otherwise AES-256 encrypted
new JcePBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256, calculator)
.setProvider(BouncyCastleProvider.PROVIDER_NAME)
.build(passphrase != null ? passphrase.getChars() : null);
if (passphrase != null) {
passphrase.clear();
}
// First key is the Master Key
KeySpec certKeySpec = keySpecs.get(0);
// Remove master key, so that we later only add sub keys.
keySpecs.remove(0);
// Generate Master Key
PGPKeyPair certKey = generateKeyPair(certKeySpec);
// Signer for creating self-signature
PGPContentSignerBuilder signer = new JcaPGPContentSignerBuilder(
certKey.getPublicKey().getAlgorithm(), HashAlgorithm.SHA512.getAlgorithmId())
.setProvider(BouncyCastleProvider.PROVIDER_NAME);
PGPSignatureSubpacketVector hashedSubPackets = certKeySpec.getSubpackets();
// Generator which the user can get the key pair from
PGPKeyRingGenerator ringGenerator = new PGPKeyRingGenerator(
PGPSignature.POSITIVE_CERTIFICATION, certKey,
userId, calculator,
hashedSubPackets, null, signer, encryptor);
for (KeySpec subKeySpec : keySpecs) {
PGPKeyPair subKey = generateKeyPair(subKeySpec);
if (subKeySpec.isInheritedSubPackets()) {
ringGenerator.addSubKey(subKey);
} else {
ringGenerator.addSubKey(subKey, subKeySpec.getSubpackets(), null);
}
}
PGPPublicKeyRing publicKeys = ringGenerator.generatePublicKeyRing();
PGPSecretKeyRing secretKeys = ringGenerator.generateSecretKeyRing();
// TODO: Remove once BC 1.61 is released
secretKeys = KeyRingSubKeyFix.repairSubkeyPackets(secretKeys, null, null);
return new PGPKeyRing(publicKeys, secretKeys);
}
private PGPKeyPair generateKeyPair(KeySpec spec)
throws NoSuchProviderException, NoSuchAlgorithmException, PGPException,
InvalidAlgorithmParameterException {
KeyType type = spec.getKeyType();
KeyPairGenerator certKeyGenerator = KeyPairGenerator.getInstance(
type.getName(), BouncyCastleProvider.PROVIDER_NAME);
certKeyGenerator.initialize(type.getAlgorithmSpec());
// Create raw Key Pair
KeyPair keyPair = certKeyGenerator.generateKeyPair();
// Form PGP key pair
PGPKeyPair pgpKeyPair = new JcaPGPKeyPair(type.getAlgorithm().getAlgorithmId(),
keyPair, new Date());
return pgpKeyPair;
}
}
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright 2018 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.generation;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import org.bouncycastle.openpgp.PGPException;
import org.pgpainless.key.collection.PGPKeyRing;
import org.pgpainless.util.Passphrase;
public interface KeyRingBuilderInterface {
KeyRingBuilderInterface withSubKey(KeySpec keySpec);
WithPrimaryUserId withMasterKey(KeySpec keySpec);
interface WithPrimaryUserId {
WithPassphrase withPrimaryUserId(String userId);
WithPassphrase withPrimaryUserId(byte[] userId);
}
interface WithPassphrase {
Build withPassphrase(Passphrase passphrase);
Build withoutPassphrase();
}
interface Build {
PGPKeyRing build() throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
InvalidAlgorithmParameterException;
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright 2018 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.generation;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
import org.pgpainless.key.generation.type.KeyType;
public class KeySpec {
private final KeyType keyType;
private final PGPSignatureSubpacketGenerator subpacketGenerator;
private final boolean inheritedSubPackets;
KeySpec(KeyType type,
PGPSignatureSubpacketGenerator subpacketGenerator,
boolean inheritedSubPackets) {
this.keyType = type;
this.subpacketGenerator = subpacketGenerator;
this.inheritedSubPackets = inheritedSubPackets;
}
KeyType getKeyType() {
return keyType;
}
PGPSignatureSubpacketVector getSubpackets() {
return subpacketGenerator.generate();
}
boolean isInheritedSubPackets() {
return inheritedSubPackets;
}
public static KeySpecBuilder getBuilder(KeyType type) {
return new KeySpecBuilder(type);
}
}

View file

@ -0,0 +1,171 @@
/*
* Copyright 2018 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.generation;
import org.bouncycastle.bcpg.sig.Features;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.pgpainless.algorithm.AlgorithmSuite;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.Feature;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.key.generation.type.KeyType;
public class KeySpecBuilder implements KeySpecBuilderInterface {
private KeyType type;
private PGPSignatureSubpacketGenerator hashedSubPackets = new PGPSignatureSubpacketGenerator();
KeySpecBuilder(KeyType type) {
this.type = type;
}
@Override
public WithDetailedConfiguration withKeyFlags(KeyFlag... flags) {
int val = 0;
for (KeyFlag f : flags) {
val |= f.getFlag();
}
this.hashedSubPackets.setKeyFlags(false, val);
return new WithDetailedConfigurationImpl();
}
@Override
public WithDetailedConfiguration withDefaultKeyFlags() {
return withKeyFlags(
KeyFlag.CERTIFY_OTHER,
KeyFlag.SIGN_DATA,
KeyFlag.ENCRYPT_COMMS,
KeyFlag.ENCRYPT_STORAGE,
KeyFlag.AUTHENTICATION);
}
@Override
public KeySpec withInheritedSubPackets() {
return new KeySpec(type, null, true);
}
class WithDetailedConfigurationImpl implements WithDetailedConfiguration {
@Deprecated
@Override
public WithPreferredSymmetricAlgorithms withDetailedConfiguration() {
return new WithPreferredSymmetricAlgorithmsImpl();
}
@Override
public KeySpec withDefaultAlgorithms() {
AlgorithmSuite defaultSuite = AlgorithmSuite.getDefaultAlgorithmSuite();
hashedSubPackets.setPreferredCompressionAlgorithms(false, defaultSuite.getCompressionAlgorithmIds());
hashedSubPackets.setPreferredSymmetricAlgorithms(false, defaultSuite.getSymmetricKeyAlgorithmIds());
hashedSubPackets.setPreferredHashAlgorithms(false, defaultSuite.getHashAlgorithmIds());
hashedSubPackets.setFeature(false, Features.FEATURE_MODIFICATION_DETECTION);
return new KeySpec(
KeySpecBuilder.this.type,
KeySpecBuilder.this.hashedSubPackets,
false);
}
}
class WithPreferredSymmetricAlgorithmsImpl implements WithPreferredSymmetricAlgorithms {
@Override
public WithPreferredHashAlgorithms withPreferredSymmetricAlgorithms(SymmetricKeyAlgorithm... algorithms) {
int[] ids = new int[algorithms.length];
for (int i = 0; i < ids.length; i++) {
ids[i] = algorithms[i].getAlgorithmId();
}
KeySpecBuilder.this.hashedSubPackets.setPreferredSymmetricAlgorithms(false, ids);
return new WithPreferredHashAlgorithmsImpl();
}
@Override
public WithPreferredHashAlgorithms withDefaultSymmetricAlgorithms() {
KeySpecBuilder.this.hashedSubPackets.setPreferredSymmetricAlgorithms(false,
AlgorithmSuite.getDefaultAlgorithmSuite().getSymmetricKeyAlgorithmIds());
return new WithPreferredHashAlgorithmsImpl();
}
@Override
public WithFeatures withDefaultAlgorithms() {
hashedSubPackets.setPreferredSymmetricAlgorithms(false,
AlgorithmSuite.getDefaultAlgorithmSuite().getSymmetricKeyAlgorithmIds());
hashedSubPackets.setPreferredCompressionAlgorithms(false,
AlgorithmSuite.getDefaultAlgorithmSuite().getCompressionAlgorithmIds());
hashedSubPackets.setPreferredHashAlgorithms(false,
AlgorithmSuite.getDefaultAlgorithmSuite().getHashAlgorithmIds());
return new WithFeaturesImpl();
}
}
class WithPreferredHashAlgorithmsImpl implements WithPreferredHashAlgorithms {
@Override
public WithPreferredCompressionAlgorithms withPreferredHashAlgorithms(HashAlgorithm... algorithms) {
int[] ids = new int[algorithms.length];
for (int i = 0; i < ids.length; i++) {
ids[i] = algorithms[i].getAlgorithmId();
}
KeySpecBuilder.this.hashedSubPackets.setPreferredHashAlgorithms(false, ids);
return new WithPreferredCompressionAlgorithmsImpl();
}
@Override
public WithPreferredCompressionAlgorithms withDefaultHashAlgorithms() {
KeySpecBuilder.this.hashedSubPackets.setPreferredHashAlgorithms(false,
AlgorithmSuite.getDefaultAlgorithmSuite().getHashAlgorithmIds());
return new WithPreferredCompressionAlgorithmsImpl();
}
}
class WithPreferredCompressionAlgorithmsImpl implements WithPreferredCompressionAlgorithms {
@Override
public WithFeatures withPreferredCompressionAlgorithms(CompressionAlgorithm... algorithms) {
int[] ids = new int[algorithms.length];
for (int i = 0; i < ids.length; i++) {
ids[i] = algorithms[i].getAlgorithmId();
}
KeySpecBuilder.this.hashedSubPackets.setPreferredCompressionAlgorithms(false, ids);
return new WithFeaturesImpl();
}
@Override
public WithFeatures withDefaultCompressionAlgorithms() {
KeySpecBuilder.this.hashedSubPackets.setPreferredCompressionAlgorithms(false,
AlgorithmSuite.getDefaultAlgorithmSuite().getCompressionAlgorithmIds());
return new WithFeaturesImpl();
}
}
class WithFeaturesImpl implements WithFeatures {
@Override
public WithFeatures withFeature(Feature feature) {
KeySpecBuilder.this.hashedSubPackets.setFeature(false, feature.getFeatureId());
return this;
}
@Override
public KeySpec done() {
return new KeySpec(
KeySpecBuilder.this.type,
hashedSubPackets,
false);
}
}
}

View file

@ -0,0 +1,72 @@
/*
* Copyright 2018 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.generation;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.Feature;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
public interface KeySpecBuilderInterface {
WithDetailedConfiguration withKeyFlags(KeyFlag... flags);
WithDetailedConfiguration withDefaultKeyFlags();
KeySpec withInheritedSubPackets();
interface WithDetailedConfiguration {
WithPreferredSymmetricAlgorithms withDetailedConfiguration();
KeySpec withDefaultAlgorithms();
}
interface WithPreferredSymmetricAlgorithms {
WithPreferredHashAlgorithms withPreferredSymmetricAlgorithms(SymmetricKeyAlgorithm... algorithms);
WithPreferredHashAlgorithms withDefaultSymmetricAlgorithms();
WithFeatures withDefaultAlgorithms();
}
interface WithPreferredHashAlgorithms {
WithPreferredCompressionAlgorithms withPreferredHashAlgorithms(HashAlgorithm... algorithms);
WithPreferredCompressionAlgorithms withDefaultHashAlgorithms();
}
interface WithPreferredCompressionAlgorithms {
WithFeatures withPreferredCompressionAlgorithms(CompressionAlgorithm... algorithms);
WithFeatures withDefaultCompressionAlgorithms();
}
interface WithFeatures {
WithFeatures withFeature(Feature feature);
KeySpec done();
}
}

View file

@ -0,0 +1,19 @@
/*
* Copyright 2018 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 related to OpenPGP key generation.
*/
package org.pgpainless.key.generation;

View file

@ -0,0 +1,50 @@
/*
* Copyright 2018 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.generation.type;
import java.security.spec.AlgorithmParameterSpec;
import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
import org.pgpainless.algorithm.PublicKeyAlgorithm;
import org.pgpainless.key.generation.type.curve.EllipticCurve;
public class ECDH implements KeyType {
private final EllipticCurve curve;
ECDH(EllipticCurve curve) {
this.curve = curve;
}
public static ECDH fromCurve(EllipticCurve curve) {
return new ECDH(curve);
}
@Override
public String getName() {
return "ECDH";
}
@Override
public PublicKeyAlgorithm getAlgorithm() {
return PublicKeyAlgorithm.ECDH;
}
@Override
public AlgorithmParameterSpec getAlgorithmSpec() {
return new ECNamedCurveGenParameterSpec(curve.getName());
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright 2018 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.generation.type;
import org.pgpainless.algorithm.PublicKeyAlgorithm;
import org.pgpainless.key.generation.type.curve.EllipticCurve;
public class ECDSA extends ECDH {
ECDSA(EllipticCurve curve) {
super(curve);
}
public static ECDSA fromCurve(EllipticCurve curve) {
return new ECDSA(curve);
}
@Override
public String getName() {
return "ECDSA";
}
@Override
public PublicKeyAlgorithm getAlgorithm() {
return PublicKeyAlgorithm.ECDSA;
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright 2018 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.generation.type;
import org.pgpainless.algorithm.PublicKeyAlgorithm;
import org.pgpainless.key.generation.type.length.ElGamalLength;
public class ElGamal_ENCRYPT extends ElGamal_GENERAL {
ElGamal_ENCRYPT(ElGamalLength length) {
super(length);
}
@Override
public PublicKeyAlgorithm getAlgorithm() {
return PublicKeyAlgorithm.ELGAMAL_ENCRYPT;
}
}

View file

@ -0,0 +1,50 @@
/*
* Copyright 2018 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.generation.type;
import java.security.spec.AlgorithmParameterSpec;
import org.bouncycastle.jce.spec.ElGamalParameterSpec;
import org.pgpainless.algorithm.PublicKeyAlgorithm;
import org.pgpainless.key.generation.type.length.ElGamalLength;
public class ElGamal_GENERAL implements KeyType {
private final ElGamalLength length;
ElGamal_GENERAL(ElGamalLength length) {
this.length = length;
}
public static ElGamal_GENERAL withLength(ElGamalLength length) {
return new ElGamal_GENERAL(length);
}
@Override
public String getName() {
return "ElGamal";
}
@Override
public PublicKeyAlgorithm getAlgorithm() {
return PublicKeyAlgorithm.ELGAMAL_GENERAL;
}
@Override
public AlgorithmParameterSpec getAlgorithmSpec() {
return new ElGamalParameterSpec(length.getP(), length.getG());
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2018 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.generation.type;
import java.security.spec.AlgorithmParameterSpec;
import org.pgpainless.algorithm.PublicKeyAlgorithm;
public interface KeyType {
String getName();
PublicKeyAlgorithm getAlgorithm();
AlgorithmParameterSpec getAlgorithmSpec();
}

View file

@ -0,0 +1,31 @@
/*
* Copyright 2018 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.generation.type;
import org.pgpainless.algorithm.PublicKeyAlgorithm;
import org.pgpainless.key.generation.type.length.RsaLength;
public class RSA_ENCRYPT extends RSA_GENERAL {
RSA_ENCRYPT(RsaLength length) {
super(length);
}
@Override
public PublicKeyAlgorithm getAlgorithm() {
return PublicKeyAlgorithm.RSA_ENCRYPT;
}
}

View file

@ -0,0 +1,50 @@
/*
* Copyright 2018 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.generation.type;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec;
import org.pgpainless.algorithm.PublicKeyAlgorithm;
import org.pgpainless.key.generation.type.length.RsaLength;
public class RSA_GENERAL implements KeyType {
private final RsaLength length;
RSA_GENERAL(RsaLength length) {
this.length = length;
}
public static RSA_GENERAL withLength(RsaLength length) {
return new RSA_GENERAL(length);
}
@Override
public String getName() {
return "RSA";
}
@Override
public PublicKeyAlgorithm getAlgorithm() {
return PublicKeyAlgorithm.RSA_GENERAL;
}
@Override
public AlgorithmParameterSpec getAlgorithmSpec() {
return new RSAKeyGenParameterSpec(length.getLength(), RSAKeyGenParameterSpec.F4);
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright 2018 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.generation.type;
import org.pgpainless.algorithm.PublicKeyAlgorithm;
import org.pgpainless.key.generation.type.length.RsaLength;
public class RSA_SIGN extends RSA_GENERAL {
RSA_SIGN(RsaLength length) {
super(length);
}
@Override
public PublicKeyAlgorithm getAlgorithm() {
return PublicKeyAlgorithm.RSA_SIGN;
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright 2018 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.generation.type.curve;
public enum EllipticCurve {
_P256("P-256"),
;
private final String name;
EllipticCurve(String name) {
this.name = name;
}
public String getName() {
return name;
}
}

View file

@ -0,0 +1,19 @@
/*
* Copyright 2018 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 related to elliptic curve cryptography.
*/
package org.pgpainless.key.generation.type.curve;

View file

@ -0,0 +1,36 @@
/*
* Copyright 2018 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.generation.type.length;
public enum DiffieHellmanLength implements KeyLength {
_1024(1024),
_2048(2048),
_3072(3072),
;
private final int length;
DiffieHellmanLength(int length) {
this.length = length;
}
@Override
public int getLength() {
return length;
}
}

View file

@ -0,0 +1,87 @@
/*
* Copyright 2018 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.generation.type.length;
import java.math.BigInteger;
/**
* The following primes are taken from RFC-3526.
*
* @see <a href="https://www.ietf.org/rfc/rfc3526.txt">
* RFC-3526: More Modular Exponential (MODP) Diffie-Hellman groups for Internet Key Exchange (IKE)</a>
*/
public enum ElGamalLength implements KeyLength {
/**
* prime: 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 }.
* generator: 2
*/
_1536(1536, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", "2"),
/**
* prime: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }.
* generator: 2
*/
_2048(2048, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", "2"),
/**
* prime: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 }.
* generator: 2
*/
_3072(3072, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF", "2"),
/**
* prime: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 }.
* generator: 2
*/
_4096(4096, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF", "2"),
/**
* prime: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 }.
* generator: 2
*/
_6144(6144, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF", "2"),
/**
* prime: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 }.
* generator: 2
*/
_8192(8192, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF", "2")
;
private final int length;
private final BigInteger p;
private final BigInteger g;
ElGamalLength(int length, String p, String g) {
this.length = length;
this.p = new BigInteger(p, 16);
this.g = new BigInteger(g, 16);
}
@Override
public int getLength() {
return length;
}
public BigInteger getP() {
return p;
}
public BigInteger getG() {
return g;
}
}

View file

@ -0,0 +1,21 @@
/*
* Copyright 2018 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.generation.type.length;
public interface KeyLength {
int getLength();
}

View file

@ -0,0 +1,38 @@
/*
* Copyright 2018 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.generation.type.length;
public enum RsaLength implements KeyLength {
@Deprecated
_1024(1024),
@Deprecated
_2048(2048),
_3072(3072),
_4096(4096),
_8192(8192),
;
private final int length;
RsaLength(int length) {
this.length = length;
}
@Override
public int getLength() {
return length;
}
}

View file

@ -0,0 +1,19 @@
/*
* Copyright 2018 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 describing the lengths of different public key crypto systems.
*/
package org.pgpainless.key.generation.type.length;

View file

@ -0,0 +1,19 @@
/*
* Copyright 2018 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 describing different OpenPGP key types.
*/
package org.pgpainless.key.generation.type;

View file

@ -0,0 +1,19 @@
/*
* Copyright 2018 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 related to OpenPGP keys.
*/
package org.pgpainless.key;

View file

@ -0,0 +1,145 @@
/*
* Copyright 2018 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.parsing;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
import org.pgpainless.key.collection.PGPKeyRing;
public class KeyRingReader {
public static final Charset UTF8 = Charset.forName("UTF-8");
public PGPPublicKeyRing publicKeyRing(InputStream inputStream) throws IOException {
return readPublicKeyRing(inputStream);
}
public PGPPublicKeyRing publicKeyRing(byte[] bytes) throws IOException {
return publicKeyRing(new ByteArrayInputStream(bytes));
}
public PGPPublicKeyRing publicKeyRing(String asciiArmored) throws IOException {
return publicKeyRing(asciiArmored.getBytes(UTF8));
}
public PGPPublicKeyRingCollection publicKeyRingCollection(InputStream inputStream)
throws IOException, PGPException {
return readPublicKeyRingCollection(inputStream);
}
public PGPPublicKeyRingCollection publicKeyRingCollection(byte[] bytes) throws IOException, PGPException {
return publicKeyRingCollection(new ByteArrayInputStream(bytes));
}
public PGPPublicKeyRingCollection publicKeyRingCollection(String asciiArmored) throws IOException, PGPException {
return publicKeyRingCollection(asciiArmored.getBytes(UTF8));
}
public PGPSecretKeyRing secretKeyRing(InputStream inputStream) throws IOException, PGPException {
return readSecretKeyRing(inputStream);
}
public PGPSecretKeyRing secretKeyRing(byte[] bytes) throws IOException, PGPException {
return secretKeyRing(new ByteArrayInputStream(bytes));
}
public PGPSecretKeyRing secretKeyRing(String asciiArmored) throws IOException, PGPException {
return secretKeyRing(asciiArmored.getBytes(UTF8));
}
public PGPSecretKeyRingCollection secretKeyRingCollection(InputStream inputStream)
throws IOException, PGPException {
return readSecretKeyRingCollection(inputStream);
}
public PGPSecretKeyRingCollection secretKeyRingCollection(byte[] bytes) throws IOException, PGPException {
return secretKeyRingCollection(new ByteArrayInputStream(bytes));
}
public PGPSecretKeyRingCollection secretKeyRingCollection(String asciiArmored) throws IOException, PGPException {
return secretKeyRingCollection(asciiArmored.getBytes(UTF8));
}
public PGPKeyRing keyRing(InputStream publicIn, InputStream secretIn) throws IOException, PGPException {
return readKeyRing(publicIn, secretIn);
}
public PGPKeyRing keyRing(byte[] publicBytes, byte[] secretBytes) throws IOException, PGPException {
return keyRing(
publicBytes != null ? new ByteArrayInputStream(publicBytes) : null,
secretBytes != null ? new ByteArrayInputStream(secretBytes) : null
);
}
public PGPKeyRing keyRing(String asciiPublic, String asciiSecret) throws IOException, PGPException {
return keyRing(
asciiPublic != null ? asciiPublic.getBytes(UTF8) : null,
asciiSecret != null ? asciiSecret.getBytes(UTF8) : null
);
}
/*
STATIC METHODS
*/
public static PGPPublicKeyRing readPublicKeyRing(InputStream inputStream) throws IOException {
return new PGPPublicKeyRing(
PGPUtil.getDecoderStream(inputStream),
new BcKeyFingerprintCalculator());
}
public static PGPPublicKeyRingCollection readPublicKeyRingCollection(InputStream inputStream)
throws IOException, PGPException {
return new PGPPublicKeyRingCollection(
PGPUtil.getDecoderStream(inputStream),
new BcKeyFingerprintCalculator());
}
public static PGPSecretKeyRing readSecretKeyRing(InputStream inputStream) throws IOException, PGPException {
return new PGPSecretKeyRing(
PGPUtil.getDecoderStream(inputStream),
new BcKeyFingerprintCalculator());
}
public static PGPSecretKeyRingCollection readSecretKeyRingCollection(InputStream inputStream)
throws IOException, PGPException {
return new PGPSecretKeyRingCollection(
PGPUtil.getDecoderStream(inputStream),
new BcKeyFingerprintCalculator());
}
public static PGPKeyRing readKeyRing(InputStream publicIn, InputStream secretIn) throws IOException, PGPException {
PGPPublicKeyRing publicKeys = null;
if (publicIn != null) {
publicKeys = readPublicKeyRing(publicIn);
}
PGPSecretKeyRing secretKeys = null;
if (secretIn != null) {
secretKeys = readSecretKeyRing(secretIn);
}
return new PGPKeyRing(publicKeys, secretKeys);
}
}

View file

@ -0,0 +1,19 @@
/*
* Copyright 2018 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 related to OpenPGP key reading.
*/
package org.pgpainless.key.parsing;

View file

@ -0,0 +1,44 @@
/*
* Copyright 2018 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;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
public class KeyRingProtectionSettings {
private final SymmetricKeyAlgorithm encryptionAlgorithm;
private final HashAlgorithm hashAlgorithm;
private final int s2kCount;
public KeyRingProtectionSettings(SymmetricKeyAlgorithm encryptionAlgorithm, HashAlgorithm hashAlgorithm, int s2kCount) {
this.encryptionAlgorithm = encryptionAlgorithm;
this.hashAlgorithm = hashAlgorithm;
this.s2kCount = s2kCount;
}
public SymmetricKeyAlgorithm getEncryptionAlgorithm() {
return encryptionAlgorithm;
}
public HashAlgorithm getHashAlgorithm() {
return hashAlgorithm;
}
public int getS2kCount() {
return s2kCount;
}
}

View file

@ -0,0 +1,71 @@
/*
* Copyright 2018 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;
import java.util.HashMap;
import java.util.Map;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyEncryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
import org.pgpainless.util.Passphrase;
/**
* Implementation of the {@link SecretKeyRingProtector} which holds a map of key ids and their passwords.
*/
public class PassphraseMapKeyRingProtector implements SecretKeyRingProtector {
private static final PGPDigestCalculatorProvider calculatorProvider = new BcPGPDigestCalculatorProvider();
private final Map<Long, Passphrase> passphrases = new HashMap<>();
private final KeyRingProtectionSettings protectionSettings;
public PassphraseMapKeyRingProtector(Map<Long, Passphrase> passphrases, KeyRingProtectionSettings protectionSettings) {
this.passphrases.putAll(passphrases);
this.protectionSettings = protectionSettings;
}
public void addPassphrase(Long keyId, Passphrase passphrase) {
this.passphrases.put(keyId, passphrase);
}
public void forgetPassphrase(Long keyId) {
Passphrase passphrase = passphrases.get(keyId);
passphrase.clear();
passphrases.remove(keyId);
}
@Override
public PBESecretKeyDecryptor getDecryptor(Long keyId) {
Passphrase passphrase = passphrases.get(keyId);
return new BcPBESecretKeyDecryptorBuilder(calculatorProvider)
.build(passphrase != null ? passphrase.getChars() : null);
}
@Override
public PBESecretKeyEncryptor getEncryptor(Long keyId) throws PGPException {
Passphrase passphrase = passphrases.get(keyId);
return new BcPBESecretKeyEncryptorBuilder(
protectionSettings.getEncryptionAlgorithm().getAlgorithmId(),
calculatorProvider.get(protectionSettings.getHashAlgorithm().getAlgorithmId()),
protectionSettings.getS2kCount())
.build(passphrase != null ? passphrase.getChars() : null);
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright 2018 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;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
public interface SecretKeyRingProtector {
/**
* Return a decryptor for the key of id {@code keyId}.
*
* @param keyId id of the key
* @return decryptor for the key
*/
PBESecretKeyDecryptor getDecryptor(Long keyId);
/**
* Return an encryptor for the key of id {@code keyId}.
*
* @param keyId id of the key
* @return encryptor for the key
* @throws PGPException if the encryptor cannot be created for some reason
*/
PBESecretKeyEncryptor getEncryptor(Long keyId) throws PGPException;
}

View file

@ -0,0 +1,34 @@
/*
* Copyright 2018 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;
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
/**
* Implementation of the {@link SecretKeyRingProtector} which assumes that all handled keys are not password protected.
*/
public class UnprotectedKeysProtector implements SecretKeyRingProtector {
@Override
public PBESecretKeyDecryptor getDecryptor(Long keyId) {
return null;
}
@Override
public PBESecretKeyEncryptor getEncryptor(Long keyId) {
return null;
}
}

View file

@ -0,0 +1,19 @@
/*
* Copyright 2018 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 related to OpenPGP secret key password protection.
*/
package org.pgpainless.key.protection;

View file

@ -0,0 +1,37 @@
/*
* Copyright 2018 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.selection.key;
import java.util.Set;
import org.pgpainless.util.MultiMap;
/**
* Interface that describes a selection strategy for OpenPGP keys.
* @param <K> Type of the Key
* @param <R> Type of the PGPKeyRing
* @param <O> Type that describes the owner of this key
*/
public interface KeySelectionStrategy<K, R, O> {
boolean accept(O identifier, K key);
Set<K> selectKeysFromKeyRing(O identifier, R ring);
MultiMap<O, K> selectKeysFromKeyRings(MultiMap<O, R> rings);
}

View file

@ -0,0 +1,54 @@
/*
* Copyright 2018 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.selection.key;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.pgpainless.util.MultiMap;
/**
* Key Selection Strategy which accepts {@link PGPPublicKey}s that are accepted by the abstract method
* {@link #accept(Object, Object)}.
*
* @param <O> Type that describes the owner of the key.
*/
public abstract class PublicKeySelectionStrategy<O> implements KeySelectionStrategy<PGPPublicKey, PGPPublicKeyRing, O> {
@Override
public Set<PGPPublicKey> selectKeysFromKeyRing(O identifier, PGPPublicKeyRing ring) {
Set<PGPPublicKey> keys = new HashSet<>();
for (Iterator<PGPPublicKey> i = ring.getPublicKeys(); i.hasNext(); ) {
PGPPublicKey key = i.next();
if (accept(identifier, key)) keys.add(key);
}
return keys;
}
@Override
public MultiMap<O, PGPPublicKey> selectKeysFromKeyRings(MultiMap<O, PGPPublicKeyRing> keyRings) {
MultiMap<O, PGPPublicKey> keys = new MultiMap<>();
for (O identifier : keyRings.keySet()) {
for (PGPPublicKeyRing ring : keyRings.get(identifier)) {
keys.put(identifier, selectKeysFromKeyRing(identifier, ring));
}
}
return keys;
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright 2018 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.selection.key;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.pgpainless.util.MultiMap;
/**
* Key Selection Strategy which accepts {@link PGPSecretKey}s that are accepted by the abstract method
* {@link #accept(Object, Object)}.
*
* @param <O> Type that describes the owner of the key.
*/
public abstract class SecretKeySelectionStrategy<O> implements KeySelectionStrategy<PGPSecretKey, PGPSecretKeyRing, O> {
@Override
public Set<PGPSecretKey> selectKeysFromKeyRing(O identifier, PGPSecretKeyRing ring) {
Set<PGPSecretKey> keys = new HashSet<>();
for (Iterator<PGPSecretKey> i = ring.getSecretKeys(); i.hasNext(); ) {
PGPSecretKey key = i.next();
if (accept(identifier, key)) keys.add(key);
}
return keys;
}
@Override
public MultiMap<O, PGPSecretKey> selectKeysFromKeyRings(MultiMap<O, PGPSecretKeyRing> keyRings) {
MultiMap<O, PGPSecretKey> keys = new MultiMap<>();
for (O identifier : keyRings.keySet()) {
for (PGPSecretKeyRing ring : keyRings.get(identifier)) {
keys.put(identifier, selectKeysFromKeyRing(identifier, ring));
}
}
return keys;
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright 2018 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.selection.key.impl;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.pgpainless.key.selection.key.PublicKeySelectionStrategy;
import org.pgpainless.key.selection.key.SecretKeySelectionStrategy;
public class And {
public static class PubKeySelectionStrategy<O> extends PublicKeySelectionStrategy<O> {
private final PublicKeySelectionStrategy<O> left;
private final PublicKeySelectionStrategy<O> right;
public PubKeySelectionStrategy(PublicKeySelectionStrategy<O> left,
PublicKeySelectionStrategy<O> right) {
this.left = left;
this.right = right;
}
@Override
public boolean accept(O identifier, PGPPublicKey key) {
return left.accept(identifier, key) && right.accept(identifier, key);
}
}
public static class SecKeySelectionStrategy<O> extends SecretKeySelectionStrategy<O> {
private final SecretKeySelectionStrategy<O> left;
private final SecretKeySelectionStrategy<O> right;
public SecKeySelectionStrategy(SecretKeySelectionStrategy<O> left,
SecretKeySelectionStrategy<O> right) {
this.left = left;
this.right = right;
}
@Override
public boolean accept(O identifier, PGPSecretKey key) {
return left.accept(identifier, key) && right.accept(identifier, key);
}
}
}

View file

@ -0,0 +1,32 @@
/*
* Copyright 2018 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.selection.key.impl;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.pgpainless.key.selection.key.PublicKeySelectionStrategy;
/**
* Key Selection Strategy that only accepts {@link PGPPublicKey}s which are capable of encryption.
*
* @param <O> Type that describes the owner of the key (not used for decision).
*/
public class EncryptionKeySelectionStrategy<O> extends PublicKeySelectionStrategy<O> {
@Override
public boolean accept(O identifier, PGPPublicKey key) {
return key.isEncryptionKey();
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright 2018 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.selection.key.impl;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.pgpainless.key.selection.key.PublicKeySelectionStrategy;
import org.pgpainless.key.selection.key.SecretKeySelectionStrategy;
/**
* Key Selection Strategies that do accept only keys, which have no revocation.
*/
public class NoRevocation {
/**
* Key Selection Strategy which only accepts {@link PGPPublicKey}s which have no revocation.
*
* @param <O> Type that describes the owner of this key (not used for this decision).
*/
public static class PubKeySelectionStrategy<O> extends PublicKeySelectionStrategy<O> {
@Override
public boolean accept(O identifier, PGPPublicKey key) {
return !key.hasRevocation();
}
}
/**
* Key Selection Strategy which only accepts {@link PGPSecretKey}s which have no revocation.
*
* @param <O> Type that describes the owner of this key (not used for this decision).
*/
public static class SecKeySelectionStrategy<O> extends SecretKeySelectionStrategy<O> {
@Override
public boolean accept(O identifier, PGPSecretKey key) {
return !key.getPublicKey().hasRevocation();
}
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright 2018 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.selection.key.impl;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.pgpainless.key.selection.key.PublicKeySelectionStrategy;
import org.pgpainless.key.selection.key.SecretKeySelectionStrategy;
public class Or {
public static class PubKeySelectionStrategy<O> extends PublicKeySelectionStrategy<O> {
private final PublicKeySelectionStrategy<O> left;
private final PublicKeySelectionStrategy<O> right;
public PubKeySelectionStrategy(PublicKeySelectionStrategy<O> left,
PublicKeySelectionStrategy<O> right) {
this.left = left;
this.right = right;
}
@Override
public boolean accept(O identifier, PGPPublicKey key) {
return left.accept(identifier, key) || right.accept(identifier, key);
}
}
public static class SecKeySelectionStrategy<O> extends SecretKeySelectionStrategy<O> {
private final SecretKeySelectionStrategy<O> left;
private final SecretKeySelectionStrategy<O> right;
public SecKeySelectionStrategy(SecretKeySelectionStrategy<O> left,
SecretKeySelectionStrategy<O> right) {
this.left = left;
this.right = right;
}
@Override
public boolean accept(O identifier, PGPSecretKey key) {
return left.accept(identifier, key) || right.accept(identifier, key);
}
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright 2018 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.selection.key.impl;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.pgpainless.key.selection.key.SecretKeySelectionStrategy;
/**
* Key Selection Strategy that only accepts {@link PGPSecretKey}s which are capable of signing.
*
* @param <O> Type that describes the owner of the key (not used for this decision).
*/
public class SignatureKeySelectionStrategy<O> extends SecretKeySelectionStrategy<O> {
@Override
public boolean accept(O identifier, PGPSecretKey key) {
return key.isSigningKey();
}
}

View file

@ -0,0 +1,60 @@
/*
* Copyright 2018 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.selection.key.impl;
import java.util.Arrays;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
import org.pgpainless.key.selection.key.PublicKeySelectionStrategy;
public class SignedByMasterKey {
private static final Logger LOGGER = Logger.getLogger(SignedByMasterKey.class.getName());
public static class PubkeySelectionStrategy extends PublicKeySelectionStrategy<PGPPublicKey> {
@Override
public boolean accept(PGPPublicKey masterKey, PGPPublicKey key) {
// Same key -> accept
if (Arrays.equals(masterKey.getFingerprint(), key.getFingerprint())) {
return true;
}
Iterator<PGPSignature> signatures = key.getSignaturesForKeyID(masterKey.getKeyID());
while (signatures.hasNext()) {
PGPSignature signature = signatures.next();
if (signature.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
try {
signature.init(new BcPGPContentVerifierBuilderProvider(), masterKey);
return signature.verifyCertification(masterKey, key);
} catch (PGPException e) {
LOGGER.log(Level.WARNING, "Could not verify subkey signature of key " +
Long.toHexString(masterKey.getKeyID()) + " on key " + Long.toHexString(key.getKeyID()));
return false;
}
}
}
return false;
}
}
}

View file

@ -0,0 +1,19 @@
/*
* Copyright 2018 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.
*/
/**
* Implementations of Key Selection Strategies.
*/
package org.pgpainless.key.selection.key.impl;

View file

@ -0,0 +1,19 @@
/*
* Copyright 2018 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.
*/
/**
* Different Key Selection Strategies.
*/
package org.pgpainless.key.selection.key;

View file

@ -0,0 +1,29 @@
/*
* Copyright 2018 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.selection.keyring;
import java.util.Set;
import org.pgpainless.util.MultiMap;
public interface KeyRingSelectionStrategy<R, C, O> {
boolean accept(O identifier, R keyRing);
Set<R> selectKeyRingsFromCollection(O identifier, C keyRingCollection);
MultiMap<O, R> selectKeyRingsFromCollections(MultiMap<O, C> keyRingCollections);
}

View file

@ -0,0 +1,48 @@
/*
* Copyright 2018 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.selection.keyring;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.pgpainless.util.MultiMap;
public abstract class PublicKeyRingSelectionStrategy<O> implements KeyRingSelectionStrategy<PGPPublicKeyRing, PGPPublicKeyRingCollection, O> {
@Override
public Set<PGPPublicKeyRing> selectKeyRingsFromCollection(O identifier, PGPPublicKeyRingCollection keyRingCollection) {
Set<PGPPublicKeyRing> accepted = new HashSet<>();
for (Iterator<PGPPublicKeyRing> i = keyRingCollection.getKeyRings(); i.hasNext(); ) {
PGPPublicKeyRing ring = i.next();
if (accept(identifier, ring)) accepted.add(ring);
}
return accepted;
}
@Override
public MultiMap<O, PGPPublicKeyRing> selectKeyRingsFromCollections(MultiMap<O, PGPPublicKeyRingCollection> keyRingCollections) {
MultiMap<O, PGPPublicKeyRing> keyRings = new MultiMap<>();
for (O identifier : keyRingCollections.keySet()) {
for (PGPPublicKeyRingCollection collection : keyRingCollections.get(identifier)) {
keyRings.put(identifier, selectKeyRingsFromCollection(identifier, collection));
}
}
return keyRings;
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright 2018 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.selection.keyring;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.pgpainless.util.MultiMap;
public abstract class SecretKeyRingSelectionStrategy<O> implements KeyRingSelectionStrategy<PGPSecretKeyRing, PGPSecretKeyRingCollection, O> {
@Override
public Set<PGPSecretKeyRing> selectKeyRingsFromCollection(O identifier, PGPSecretKeyRingCollection keyRingCollection) {
Set<PGPSecretKeyRing> accepted = new HashSet<>();
for (Iterator<PGPSecretKeyRing> i = keyRingCollection.getKeyRings(); i.hasNext(); ) {
PGPSecretKeyRing ring = i.next();
if (accept(identifier, ring)) accepted.add(ring);
}
return accepted;
}
@Override
public MultiMap<O, PGPSecretKeyRing> selectKeyRingsFromCollections(MultiMap<O, PGPSecretKeyRingCollection> keyRingCollections) {
MultiMap<O, PGPSecretKeyRing> keyRings = new MultiMap<>();
for (O identifier : keyRingCollections.keySet()) {
for (PGPSecretKeyRingCollection collection : keyRingCollections.get(identifier)) {
keyRings.put(identifier, selectKeyRingsFromCollection(identifier, collection));
}
}
return keyRings;
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright 2018 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.selection.keyring.impl;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey;
public class Email {
public static class PubRingSelectionStrategy extends PartialUserId.PubRingSelectionStrategy {
@Override
public boolean accept(String email, PGPPublicKey key) {
// Ensure, that email address is encapsulated in "<",">"
if (!email.matches("^<.+>$")) {
email = "<" + email + ">";
}
return super.accept(email, key);
}
}
public static class SecRingSelectionStrategy extends PartialUserId.SecRingSelectionStrategy {
@Override
public boolean accept(String email, PGPSecretKey key) {
// Ensure, that email address is encapsulated in "<",">"
if (!email.matches("^<.+>$")) {
email = "<" + email + ">";
}
return super.accept(email, key);
}
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright 2018 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.selection.keyring.impl;
import java.util.Iterator;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.pgpainless.key.selection.keyring.PublicKeyRingSelectionStrategy;
import org.pgpainless.key.selection.keyring.SecretKeyRingSelectionStrategy;
import org.pgpainless.key.selection.keyring.PublicKeyRingSelectionStrategy;
public class ExactUserId {
public static class PubRingSelectionStrategy extends PublicKeyRingSelectionStrategy<String> {
@Override
public boolean accept(String identifier, PGPPublicKeyRing keyRing) {
Iterator<String> userIds = keyRing.getPublicKey().getUserIDs();
while (userIds.hasNext()) {
if (userIds.next().equals(identifier)) return true;
}
return false;
}
}
public static class SecRingSelectionStrategy extends SecretKeyRingSelectionStrategy<String> {
@Override
public boolean accept(String identifier, PGPSecretKeyRing keyRing) {
Iterator<String> userIds = keyRing.getPublicKey().getUserIDs();
while (userIds.hasNext()) {
if (userIds.next().equals(identifier)) return true;
}
return false;
}
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright 2018 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.selection.keyring.impl;
import java.util.Iterator;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.pgpainless.key.selection.key.PublicKeySelectionStrategy;
import org.pgpainless.key.selection.key.SecretKeySelectionStrategy;
public class PartialUserId {
public static class PubRingSelectionStrategy extends PublicKeySelectionStrategy<String> {
@Override
public boolean accept(String identifier, PGPPublicKey key) {
for (Iterator<String> userIds = key.getUserIDs(); userIds.hasNext(); ) {
String userId = userIds.next();
if (userId.contains(identifier)) {
return true;
}
}
return false;
}
}
public static class SecRingSelectionStrategy extends SecretKeySelectionStrategy<String> {
@Override
public boolean accept(String identifier, PGPSecretKey key) {
for (Iterator userIds = key.getUserIDs(); userIds.hasNext(); ) {
String userId = (String) userIds.next();
if (userId.contains(identifier)) {
return true;
}
}
return false;
}
}
}

View file

@ -0,0 +1,77 @@
/*
* Copyright 2018 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.selection.keyring.impl;
import java.util.Map;
import java.util.Set;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.pgpainless.key.selection.keyring.PublicKeyRingSelectionStrategy;
import org.pgpainless.key.selection.keyring.SecretKeyRingSelectionStrategy;
import org.pgpainless.util.MultiMap;
public class Whitelist {
public static class PubRingSelectionStrategy<O> extends PublicKeyRingSelectionStrategy<O> {
private final MultiMap<O, Long> whitelist;
public PubRingSelectionStrategy(MultiMap<O, Long> whitelist) {
this.whitelist = whitelist;
}
public PubRingSelectionStrategy(Map<O, Set<Long>> whitelist) {
this.whitelist = new MultiMap<>(whitelist);
}
@Override
public boolean accept(O identifier, PGPPublicKeyRing keyRing) {
Set<Long> whitelistedKeyIds = whitelist.get(identifier);
if (whitelistedKeyIds == null) {
return false;
}
return whitelistedKeyIds.contains(keyRing.getPublicKey().getKeyID());
}
}
public static class SecRingSelectionStrategy<O> extends SecretKeyRingSelectionStrategy<O> {
private final MultiMap<O, Long> whitelist;
public SecRingSelectionStrategy(MultiMap<O, Long> whitelist) {
this.whitelist = whitelist;
}
public SecRingSelectionStrategy(Map<O, Set<Long>> whitelist) {
this.whitelist = new MultiMap<>(whitelist);
}
@Override
public boolean accept(O identifier, PGPSecretKeyRing keyRing) {
Set<Long> whitelistedKeyIds = whitelist.get(identifier);
if (whitelistedKeyIds == null) {
return false;
}
return whitelistedKeyIds.contains(keyRing.getPublicKey().getKeyID());
}
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright 2018 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.selection.keyring.impl;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.pgpainless.key.selection.keyring.PublicKeyRingSelectionStrategy;
import org.pgpainless.key.selection.keyring.SecretKeyRingSelectionStrategy;
public class Wildcard {
public class PubRingSelectionStrategy<O> extends PublicKeyRingSelectionStrategy<O> {
@Override
public boolean accept(O identifier, PGPPublicKeyRing keyRing) {
return true;
}
}
public class SecRingSelectionStrategy<O> extends SecretKeyRingSelectionStrategy<O> {
@Override
public boolean accept(O identifier, PGPSecretKeyRing keyRing) {
return true;
}
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright 2018 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.selection.keyring.impl;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
public class XMPP {
public static class PubRingSelectionStrategy extends ExactUserId.PubRingSelectionStrategy {
@Override
public boolean accept(String jid, PGPPublicKeyRing keyRing) {
return super.accept("xmpp:" + jid, keyRing);
}
}
public static class SecRingSelectionStrategy extends ExactUserId.SecRingSelectionStrategy {
@Override
public boolean accept(String jid, PGPSecretKeyRing keyRing) {
return super.accept("xmpp:" + jid, keyRing);
}
}
}

View file

@ -0,0 +1,19 @@
/*
* Copyright 2018 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.
*/
/**
* Implementations of Key Ring Selection Strategies.
*/
package org.pgpainless.key.selection.keyring.impl;

View file

@ -0,0 +1,19 @@
/*
* Copyright 2018 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.
*/
/**
* Different Key Ring Selection Strategies.
*/
package org.pgpainless.key.selection.keyring;

View file

@ -0,0 +1,21 @@
/*
* Copyright 2018 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.
*/
/**
* PGPainless - Use OpenPGP Painlessly!
*
* @see <a href="http://pgpainless.org">org.pgpainless.core.org</a>
*/
package org.pgpainless;

View file

@ -0,0 +1,181 @@
/*
* Copyright 2018 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.symmetric_encryption;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.SecureRandom;
import java.util.Date;
import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPPBEEncryptedData;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.bc.BcPGPObjectFactory;
import org.bouncycastle.openpgp.operator.bc.BcPBEDataDecryptorFactory;
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
import org.bouncycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator;
import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
import org.bouncycastle.util.io.Streams;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.util.Passphrase;
/**
* Stolen from <a href="https://github.com/bcgit/bc-java/blob/master/pg/src/main/java/org/bouncycastle/openpgp/examples/PBEFileProcessor.java">
* Bouncycastle examples</a>.
*/
public class SymmetricEncryptorDecryptor {
/**
* Encrypt some {@code data} symmetrically using an {@code encryptionAlgorithm} and a given {@code password}.
* The input data will be compressed using the given {@code compressionAlgorithm} and packed in a modification
* detection package, which is then encrypted.
*
* @param data bytes that will be encrypted
* @param password password that will be used to encrypt the data
* @param encryptionAlgorithm symmetric algorithm that will be used to encrypt the data
* @param compressionAlgorithm compression algorithm that will be used to compress the data
* @return encrypted data
* @throws IOException IO is dangerous
* @throws PGPException OpenPGP is brittle
*/
public static byte[] symmetricallyEncrypt(byte[] data,
Passphrase password,
SymmetricKeyAlgorithm encryptionAlgorithm,
CompressionAlgorithm compressionAlgorithm)
throws IOException, PGPException {
byte[] compressedData = compress(data, compressionAlgorithm.getAlgorithmId());
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
new JcePGPDataEncryptorBuilder(encryptionAlgorithm.getAlgorithmId())
.setWithIntegrityPacket(true)
.setSecureRandom(new SecureRandom())
.setProvider("BC"));
encGen.addMethod(new JcePBEKeyEncryptionMethodGenerator(password.getChars()).setProvider("BC"));
OutputStream encOut = encGen.open(bOut, compressedData.length);
encOut.write(compressedData);
encOut.close();
return bOut.toByteArray();
}
/**
* Decrypt and decompress some symmetrically encrypted data using a password.
* Note, that decryption will fail if the given data is not integrity protected with a modification detection
* package.
*
* @param data encrypted data
* @param password password to decrypt the data
* @return decrypted data
* @throws IOException IO is dangerous
* @throws PGPException OpenPGP is brittle
*/
public static byte[] symmetricallyDecrypt(byte[] data, Passphrase password) throws IOException, PGPException {
InputStream in = new BufferedInputStream(new ByteArrayInputStream(data));
in = PGPUtil.getDecoderStream(in);
BcPGPObjectFactory pgpF = new BcPGPObjectFactory(in);
PGPEncryptedDataList enc;
Object o = pgpF.nextObject();
if (o instanceof PGPEncryptedDataList) {
enc = (PGPEncryptedDataList) o;
} else {
enc = (PGPEncryptedDataList) pgpF.nextObject();
}
PGPPBEEncryptedData pbe = (PGPPBEEncryptedData) enc.get(0);
InputStream clear = pbe.getDataStream(new BcPBEDataDecryptorFactory(
password.getChars(), new BcPGPDigestCalculatorProvider()));
BcPGPObjectFactory pgpFact = new BcPGPObjectFactory(clear);
o = pgpFact.nextObject();
if (o instanceof PGPCompressedData) {
PGPCompressedData cData = (PGPCompressedData) o;
pgpFact = new BcPGPObjectFactory(cData.getDataStream());
o = pgpFact.nextObject();
}
PGPLiteralData ld = (PGPLiteralData) o;
InputStream unc = ld.getInputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Streams.pipeAll(unc, outputStream);
outputStream.close();
if (pbe.isIntegrityProtected()) {
if (!pbe.verify()) {
throw new PGPException("Integrity check failed.");
}
} else {
throw new PGPException("Symmetrically encrypted data is not integrity protected.");
}
return outputStream.toByteArray();
}
/**
* Wrap some data in an OpenPGP compressed data package.
*
* @param clearData uncompressed data
* @param algorithm compression algorithm
* @return compressed data
* @throws IOException IO is dangerous
*/
private static byte[] compress(byte[] clearData, int algorithm) throws IOException {
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(algorithm);
OutputStream cos = comData.open(bOut);
PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
OutputStream pOut = lData.open(cos,
PGPLiteralData.BINARY,
PGPLiteralDataGenerator.CONSOLE,
clearData.length,
new Date()
);
pOut.write(clearData);
pOut.close();
comData.close();
return bOut.toByteArray();
}
}

View file

@ -0,0 +1,19 @@
/*
* Copyright 2018 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 related to OpenPGP symmetric encryption.
*/
package org.pgpainless.symmetric_encryption;

View file

@ -0,0 +1,321 @@
/*
* Copyright 2018 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 java.io.InputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPKeyRing;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
import org.bouncycastle.util.io.Streams;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.key.selection.key.PublicKeySelectionStrategy;
import org.pgpainless.key.selection.key.impl.And;
import org.pgpainless.key.selection.key.impl.NoRevocation;
import org.pgpainless.key.selection.key.impl.SignedByMasterKey;
public class BCUtil {
private static final Logger LOGGER = Logger.getLogger(BCUtil.class.getName());
/*
PGPXxxKeyRing -> PGPXxxKeyRingCollection
*/
public static PGPPublicKeyRingCollection keyRingsToKeyRingCollection(PGPPublicKeyRing... rings)
throws IOException, PGPException {
return new PGPPublicKeyRingCollection(Arrays.asList(rings));
}
public static PGPSecretKeyRingCollection keyRingsToKeyRingCollection(PGPSecretKeyRing... rings)
throws IOException, PGPException {
return new PGPSecretKeyRingCollection(Arrays.asList(rings));
}
public static PGPPublicKeyRing publicKeyRingFromSecretKeyRing(PGPSecretKeyRing secretKeys)
throws PGPException, IOException {
PGPSecretKeyRing fixedSecretKeys = KeyRingSubKeyFix.repairSubkeyPackets(secretKeys, null, null);
ByteArrayOutputStream buffer = new ByteArrayOutputStream(512);
for (PGPSecretKey secretKey : fixedSecretKeys) {
PGPPublicKey publicKey = secretKey.getPublicKey();
if (publicKey != null) {
publicKey.encode(buffer, false);
}
}
return new PGPPublicKeyRing(buffer.toByteArray(), new BcKeyFingerprintCalculator());
}
/*
PGPXxxKeyRingCollection -> PGPXxxKeyRing
*/
public static PGPSecretKeyRing getKeyRingFromCollection(PGPSecretKeyRingCollection collection, Long id)
throws PGPException, IOException {
PGPSecretKeyRing uncleanedRing = collection.getSecretKeyRing(id);
// Determine ids of signed keys
Set<Long> signedKeyIds = new HashSet<>();
signedKeyIds.add(id); // Add the signing key itself
Iterator<PGPPublicKey> signedPubKeys = uncleanedRing.getKeysWithSignaturesBy(id);
while (signedPubKeys.hasNext()) {
signedKeyIds.add(signedPubKeys.next().getKeyID());
}
PGPSecretKeyRing cleanedRing = uncleanedRing;
Iterator<PGPSecretKey> secretKeys = uncleanedRing.getSecretKeys();
while (secretKeys.hasNext()) {
PGPSecretKey secretKey = secretKeys.next();
if (!signedKeyIds.contains(secretKey.getKeyID())) {
cleanedRing = PGPSecretKeyRing.removeSecretKey(cleanedRing, secretKey);
}
}
return cleanedRing;
}
public static PGPPublicKeyRing getKeyRingFromCollection(PGPPublicKeyRingCollection collection, Long id)
throws PGPException {
PGPPublicKey key = collection.getPublicKey(id);
return removeUnassociatedKeysFromKeyRing(collection.getPublicKeyRing(id), key);
}
public static InputStream getPgpDecoderInputStream(byte[] bytes) throws IOException {
return getPgpDecoderInputStream(new ByteArrayInputStream(bytes));
}
public static InputStream getPgpDecoderInputStream(InputStream inputStream) throws IOException {
return PGPUtil.getDecoderStream(inputStream);
}
public static byte[] getDecodedBytes(byte[] bytes) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
Streams.pipeAll(getPgpDecoderInputStream(bytes), buffer);
return buffer.toByteArray();
}
public static byte[] getDecodedBytes(InputStream inputStream) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
Streams.pipeAll(inputStream, buffer);
return getDecodedBytes(buffer.toByteArray());
}
/**
* Remove all keys from the key ring, are either not having a subkey signature from the master key
* (identified by {@code masterKeyId}), or are revoked ("normal" key revocation, as well as subkey revocation).
*
* @param ring key ring
* @param masterKey master key
* @return "cleaned" key ring
*/
public static PGPPublicKeyRing removeUnassociatedKeysFromKeyRing(PGPPublicKeyRing ring, PGPPublicKey masterKey) {
if (!masterKey.isMasterKey()) {
throw new IllegalArgumentException("Given key is not a master key.");
}
// Only select keys which are signed by the master key and not revoked.
PublicKeySelectionStrategy<PGPPublicKey> selector = new And.PubKeySelectionStrategy<>(
new SignedByMasterKey.PubkeySelectionStrategy(),
new NoRevocation.PubKeySelectionStrategy<>());
PGPPublicKeyRing cleaned = ring;
Iterator<PGPPublicKey> publicKeys = ring.getPublicKeys();
while (publicKeys.hasNext()) {
PGPPublicKey publicKey = publicKeys.next();
if (!selector.accept(masterKey, publicKey)) {
cleaned = PGPPublicKeyRing.removePublicKey(cleaned, publicKey);
}
}
return cleaned;
}
/**
* Remove all keys from the key ring, are either not having a subkey signature from the master key
* (identified by {@code masterKeyId}), or are revoked ("normal" key revocation, as well as subkey revocation).
*
* @param ring key ring
* @param masterKey master key
* @return "cleaned" key ring
*/
public static PGPSecretKeyRing removeUnassociatedKeysFromKeyRing(PGPSecretKeyRing ring, PGPPublicKey masterKey) {
if (!masterKey.isMasterKey()) {
throw new IllegalArgumentException("Given key is not a master key.");
}
// Only select keys which are signed by the master key and not revoked.
PublicKeySelectionStrategy<PGPPublicKey> selector = new And.PubKeySelectionStrategy<>(
new SignedByMasterKey.PubkeySelectionStrategy(),
new NoRevocation.PubKeySelectionStrategy<>());
PGPSecretKeyRing cleaned = ring;
Iterator<PGPSecretKey> secretKeys = ring.getSecretKeys();
while (secretKeys.hasNext()) {
PGPSecretKey secretKey = secretKeys.next();
if (!selector.accept(masterKey, secretKey.getPublicKey())) {
cleaned = PGPSecretKeyRing.removeSecretKey(cleaned, secretKey);
}
}
return cleaned;
}
/**
* Return the {@link PGPPublicKey} which is the master key of the key ring.
*
* @param ring key ring
* @return master key
*/
public static PGPPublicKey getMasterKeyFrom(PGPPublicKeyRing ring) {
Iterator<PGPPublicKey> it = ring.getPublicKeys();
while (it.hasNext()) {
PGPPublicKey k = it.next();
if (k.isMasterKey()) {
// There can only be one master key, so we can immediately return
return k;
}
}
return null;
}
public static PGPPublicKey getMasterKeyFrom(PGPKeyRing ring) {
Iterator<PGPPublicKey> it = ring.getPublicKeys();
while (it.hasNext()) {
PGPPublicKey k = it.next();
if (k.isMasterKey()) {
// There can only be one master key, so we can immediately return
return k;
}
}
return null;
}
public static Set<Long> signingKeyIds(PGPSecretKeyRing ring) {
Set<Long> ids = new HashSet<>();
Iterator<PGPPublicKey> it = ring.getPublicKeys();
while (it.hasNext()) {
PGPPublicKey k = it.next();
boolean signingKey = false;
Iterator sit = k.getSignatures();
while (sit.hasNext()) {
Object n = sit.next();
if (!(n instanceof PGPSignature)) {
continue;
}
PGPSignature s = (PGPSignature) n;
if (!s.hasSubpackets()) {
continue;
}
try {
s.verifyCertification(ring.getPublicKey(s.getKeyID()));
} catch (PGPException e) {
LOGGER.log(Level.WARNING, "Could not verify signature on " + Long.toHexString(k.getKeyID()) + " made by " + Long.toHexString(s.getKeyID()));
continue;
}
PGPSignatureSubpacketVector hashed = s.getHashedSubPackets();
if (KeyFlag.fromInteger(hashed.getKeyFlags()).contains(KeyFlag.SIGN_DATA)) {
signingKey = true;
break;
}
}
if (signingKey) {
ids.add(k.getKeyID());
}
}
return ids;
}
public static boolean keyRingContainsKeyWithId(PGPPublicKeyRing ring, long keyId) {
return ring.getPublicKey(keyId) != null;
}
public static boolean keyRingContainsKeyWithId(PGPSecretKeyRing ring, long keyId) {
return ring.getSecretKey(keyId) != null;
}
/*
public static PGPKeyRing merge(PGPKeyRing one, PGPKeyRing other) {
PGPPublicKey masterOne = getMasterKeyFrom(one);
if (masterOne == null) {
throw new IllegalArgumentException("First PGPKeyRing has no master key");
}
PGPPublicKey masterOther = getMasterKeyFrom(other);
if (masterOther == null) {
throw new IllegalArgumentException("Other PGPKeyRing has no master key");
}
if (masterOne.getKeyID() != masterOther.getKeyID() ||
Arrays.equals(masterOne.getFingerprint(), masterOther.getFingerprint())) {
throw new IllegalArgumentException("Keys are not the same.");
}
PGPKeyRing merged = one;
boolean mergedIsSecret = (merged instanceof PGPSecretKeyRing);
boolean otherIsSecret = (other instanceof PGPSecretKeyRing);
for (Iterator it = other.getPublicKeys(); it.hasNext(); ) {
PGPPublicKey nextPublicKey = (PGPPublicKey) it.next();
PGPPublicKey pendant = merged.getPublicKey(nextPublicKey.getKeyID());
if (pendant == null) {
if (mergedIsSecret && otherIsSecret) {
// Add secret key
PGPSecretKey secretKey = ((PGPSecretKeyRing) other).getSecretKey(nextPublicKey.getKeyID());
merged = PGPSecretKeyRing.insertSecretKey((PGPSecretKeyRing) merged, secretKey);
} else {
if (mergedIsSecret) {
PGPSecretKeyRing mergedAsSecret = (PGPSecretKeyRing) merged;
PGPSecretKey secretKey = mergedAsSecret.getSecretKey(nextPublicKey.getKeyID());
if (secretKey == null) {
PGPPublicKeyRing mergedAsPublic = publicKeyRingFromSecretKeyRing((PGPSecretKeyRing) merged);
mergedAsPublic = PGPPublicKeyRing.insertPublicKey(mergedAsPublic, nextPublicKey);
mergedAsSecret = PGPSecretKeyRing.replacePublicKeys(mergedAsSecret, mergedAsPublic);
merged = mergedAsSecret;
}
}
}
}
}
}
*/
}

View file

@ -0,0 +1,110 @@
/*
* Copyright 2018 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.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyPacket;
import org.bouncycastle.bcpg.PublicSubkeyPacket;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
public class KeyRingSubKeyFix {
private static final Logger LOGGER = Logger.getLogger(KeyRingSubKeyFix.class.getName());
/**
* This method makes sure, that sub keys do consist of sub key packets.
* Bouncycastle versions up to and including 1.60 created {@link PGPSecretKeyRing}s which sub keys consisted of
* normal public key packets, which would result in lost keys when converting PGPSecretKeyRings to PGPPublicKeyRings.
*
* This method throws a {@link RuntimeException} of a {@link NoSuchFieldException} or {@link IllegalAccessException}.
*
* @see <a href="https://github.com/bcgit/bc-java/issues/381">Bouncycastle Java bug report #381</a>
*
* @param secretKeys possibly faulty PGPSecretKeyRing
* @param decryptor decryptor in case the keys are encrypted (can be null)
* @param encryptor encryptor to re-encrypt the keys in case they are encrypted (can be null)
*
* @return fixed PGPSecretKeyRing
*
* @throws PGPException in case we cannot dismantle or reassemble the key.
*/
public static PGPSecretKeyRing repairSubkeyPackets(PGPSecretKeyRing secretKeys,
PBESecretKeyDecryptor decryptor,
PBESecretKeyEncryptor encryptor)
throws PGPException {
PGPDigestCalculator calculator = new BcPGPDigestCalculatorProvider().get(HashAlgorithmTags.SHA1);
List<PGPSecretKey> _secretKeys = new ArrayList<>();
Iterator<PGPSecretKey> secretKeyIterator = secretKeys.iterator();
try {
while (secretKeyIterator.hasNext()) {
PGPSecretKey key = secretKeyIterator.next();
if (key.isMasterKey()) {
LOGGER.log(Level.INFO, Long.toHexString(key.getKeyID()) + " is master key. Skip.");
_secretKeys.add(key);
continue;
}
PGPSecretKey secSubKey = key;
PGPPublicKey pubSubKey = secSubKey.getPublicKey();
// check for public key packet type
Field publicPk = pubSubKey.getClass().getDeclaredField("publicPk");
publicPk.setAccessible(true);
PublicKeyPacket keyPacket = (PublicKeyPacket) publicPk.get(pubSubKey);
if (keyPacket instanceof PublicSubkeyPacket) {
// Sub key is already sub key
_secretKeys.add(secSubKey);
continue;
}
// Sub key is normal key -> fix
LOGGER.log(Level.INFO, "Subkey " + Long.toHexString(secSubKey.getKeyID()) + " does not have a subkey key packet. Convert it...");
keyPacket = new PublicSubkeyPacket(pubSubKey.getAlgorithm(), pubSubKey.getCreationTime(), keyPacket.getKey());
publicPk.set(pubSubKey, keyPacket);
PGPPrivateKey privateKey = secSubKey.extractPrivateKey(decryptor);
PGPSecretKey secretKey = new PGPSecretKey(privateKey, pubSubKey, calculator, false, encryptor);
_secretKeys.add(secretKey);
}
return new PGPSecretKeyRing(_secretKeys);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException("Cannot apply fix due to an error while using reflections.", e);
}
}
}

View file

@ -0,0 +1,135 @@
/*
* Copyright 2018 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.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class MultiMap<K, V> {
private final Map<K, Set<V>> map;
public MultiMap() {
map = new HashMap<>();
}
public MultiMap(MultiMap<K, V> other) {
this.map = new HashMap<>();
for (K k : other.map.keySet()) {
map.put(k, new HashSet<>(other.map.get(k)));
}
}
public MultiMap(Map<K, Set<V>> content) {
this.map = new HashMap<>(content);
}
public int size() {
return map.size();
}
public boolean isEmpty() {
return map.isEmpty();
}
public boolean containsKey(Object o) {
return map.containsKey(o);
}
public boolean containsValue(Object o) {
for (Set<V> values : map.values()) {
if (values.contains(o)) return true;
}
return false;
}
public Set<V> get(Object o) {
return map.get(o);
}
public void put(K k, V v) {
Set<V> values = map.get(k);
if (values == null) {
values = new HashSet<>();
map.put(k, values);
}
values.add(v);
}
public void put(K k, Set<V> vs) {
for (V v : vs) {
put(k, v);
}
}
public void remove(Object o) {
for (Set<V> values : map.values()) {
values.remove(o);
}
}
public void putAll(Map<? extends K, ? extends Set<V>> _map) {
for (K key : _map.keySet()) {
Set<V> vs = this.map.get(key);
if (vs == null) {
vs = new HashSet<>();
this.map.put(key, vs);
}
vs.addAll(_map.get(key));
}
}
public void clear() {
map.clear();
}
public Set<K> keySet() {
return map.keySet();
}
public Collection<Set<V>> values() {
return map.values();
}
public Set<Map.Entry<K, Set<V>>> entrySet() {
return map.entrySet();
}
@Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (!(o instanceof MultiMap)) {
return false;
}
if (this == o) {
return true;
}
return map.equals(((MultiMap) o).map);
}
@Override
public int hashCode() {
return map.hashCode();
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright 2018 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.util.Arrays;
public class Passphrase {
private final char[] chars;
public Passphrase(char[] chars) {
if (chars == null) {
throw new NullPointerException("chars MUST NOT be null.");
}
this.chars = chars;
}
public void clear() {
Arrays.fill(chars, ' ');
}
@Override
protected void finalize() throws Throwable {
clear();
super.finalize();
}
public char[] getChars() {
char[] copy = new char[chars.length];
System.arraycopy(chars, 0, copy, 0, chars.length);
return copy;
}
}

View file

@ -0,0 +1,19 @@
/*
* Copyright 2018 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 classes.
*/
package org.pgpainless.util;

View file

@ -0,0 +1,29 @@
/*
* Copyright 2018 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;
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.BeforeClass;
public abstract class AbstractPGPainlessTest {
@BeforeClass
public static void registerProvider() {
Security.addProvider(new BouncyCastleProvider());
}
}

View file

@ -0,0 +1,151 @@
/*
* Copyright 2018 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;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.assertTrue;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import junit.framework.TestCase;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.junit.Test;
import org.pgpainless.key.collection.PGPKeyRing;
import org.pgpainless.key.generation.KeySpec;
import org.pgpainless.key.generation.type.RSA_GENERAL;
import org.pgpainless.key.generation.type.length.RsaLength;
import org.pgpainless.util.BCUtil;
public class BCUtilTest extends AbstractPGPainlessTest {
private static final Logger LOGGER = Logger.getLogger(BCUtil.class.getName());
@Test
public void keyRingToCollectionTest()
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException,
IOException {
PGPKeyRing ring = PGPainless.generateKeyRing()
.withSubKey(KeySpec.getBuilder(RSA_GENERAL.withLength(RsaLength._3072)).withDefaultKeyFlags().withDefaultAlgorithms())
.withMasterKey(KeySpec.getBuilder(RSA_GENERAL.withLength(RsaLength._3072)).withDefaultKeyFlags().withDefaultAlgorithms())
.withPrimaryUserId("donald@duck.tails").withoutPassphrase().build();
PGPSecretKeyRing sec = ring.getSecretKeys();
PGPPublicKeyRing pub = ring.getPublicKeys();
LOGGER.log(Level.INFO, "Main ID: " + sec.getPublicKey().getKeyID() + " " + pub.getPublicKey().getKeyID());
int secSize = 1;
Iterator<PGPPublicKey> secPubIt = sec.getPublicKeys();
while (secPubIt.hasNext()) {
PGPPublicKey k = secPubIt.next();
LOGGER.log(Level.INFO, secSize + " " + k.getKeyID() + " " + k.isEncryptionKey() + " " + k.isMasterKey());
secSize++;
}
LOGGER.log(Level.INFO, "After BCUtil.publicKeyRingFromSecretKeyRing()");
int pubSize = 1;
Iterator<PGPPublicKey> pubPubIt = pub.getPublicKeys();
while (pubPubIt.hasNext()) {
PGPPublicKey k = pubPubIt.next();
LOGGER.log(Level.INFO, pubSize + " " + k.getKeyID() + " " + k.isEncryptionKey() + " " + k.isMasterKey());
pubSize++;
}
assertEquals(secSize, pubSize);
PGPSecretKeyRingCollection secCol = BCUtil.keyRingsToKeyRingCollection(sec);
int secColSize = 0;
Iterator<PGPSecretKeyRing> secColIt = secCol.getKeyRings();
while (secColIt.hasNext()) {
PGPSecretKeyRing r = secColIt.next();
LOGGER.log(Level.INFO, "" + r.getPublicKey().getKeyID());
secColSize++;
}
LOGGER.log(Level.INFO, "SecCol: " + secColSize);
PGPPublicKeyRingCollection pubCol = BCUtil.keyRingsToKeyRingCollection(pub);
int pubColSize = 0;
Iterator<PGPPublicKeyRing> pubColIt = pubCol.getKeyRings();
while (pubColIt.hasNext()) {
PGPPublicKeyRing r = pubColIt.next();
LOGGER.log(Level.INFO, "" + r.getPublicKey().getKeyID());
pubColSize++;
}
LOGGER.log(Level.INFO, "PubCol: " + pubColSize);
}
@Test
public void removeUnsignedKeysTest()
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
PGPKeyRing alice = PGPainless.generateKeyRing().simpleRsaKeyRing("alice@wonderland.lit", RsaLength._1024);
PGPKeyRing mallory = PGPainless.generateKeyRing().simpleEcKeyRing("mallory@mall.ory");
PGPSecretKey subKey = null;
Iterator<PGPSecretKey> sit = mallory.getSecretKeys().getSecretKeys();
while (sit.hasNext()) {
PGPSecretKey s = sit.next();
if (!s.isMasterKey()) {
subKey = s;
break;
}
}
TestCase.assertNotNull(subKey);
PGPSecretKeyRing alice_mallory = PGPSecretKeyRing.insertSecretKey(alice.getSecretKeys(), subKey);
// Check, if alice_mallory contains mallory's key
TestCase.assertNotNull(alice_mallory.getSecretKey(subKey.getKeyID()));
PGPSecretKeyRing cleaned = BCUtil.removeUnassociatedKeysFromKeyRing(alice_mallory, alice.getPublicKeys().getPublicKey());
TestCase.assertNull(cleaned.getSecretKey(subKey.getKeyID()));
}
@Test
public void removeUnsignedKeysECTest()
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException,
IOException {
PGPKeyRing ring = PGPainless.generateKeyRing().simpleEcKeyRing("alice@wonderland.lit");
PGPPublicKeyRing publicKeys = ring.getPublicKeys();
PGPSecretKeyRing secretKeys = ring.getSecretKeys();
PGPSecretKeyRing secCleaned = ring.getSecretKeys();
assertTrue(Arrays.equals(secretKeys.getEncoded(), secCleaned.getEncoded()));
PGPPublicKeyRing pubCleaned = BCUtil.removeUnassociatedKeysFromKeyRing(publicKeys, publicKeys.getPublicKey());
assertTrue(Arrays.equals(publicKeys.getEncoded(), pubCleaned.getEncoded()));
}
}

View file

@ -0,0 +1,132 @@
/*
* Copyright 2018 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;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.util.Date;
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.bcpg.sig.Features;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPKeyPair;
import org.bouncycastle.openpgp.PGPKeyRingGenerator;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
import org.junit.Test;
public class BouncycastleExportSubkeys {
@Test
public void testExportImport() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, PGPException, IOException {
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator generator;
KeyPair pair;
// Generate master key
generator = KeyPairGenerator.getInstance("ECDSA", BouncyCastleProvider.PROVIDER_NAME);
generator.initialize(new ECNamedCurveGenParameterSpec("P-256"));
pair = generator.generateKeyPair();
PGPKeyPair pgpMasterKey = new JcaPGPKeyPair(PublicKeyAlgorithmTags.ECDSA, pair, new Date());
PGPSignatureSubpacketGenerator subPackets = new PGPSignatureSubpacketGenerator();
subPackets.setKeyFlags(false, KeyFlags.AUTHENTICATION & KeyFlags.CERTIFY_OTHER & KeyFlags.SIGN_DATA);
subPackets.setPreferredCompressionAlgorithms(false, new int[] {
SymmetricKeyAlgorithmTags.AES_256,
SymmetricKeyAlgorithmTags.AES_128,
SymmetricKeyAlgorithmTags.AES_128});
subPackets.setPreferredHashAlgorithms(false, new int[] {
HashAlgorithmTags.SHA512,
HashAlgorithmTags.SHA384,
HashAlgorithmTags.SHA256,
HashAlgorithmTags.SHA224});
subPackets.setPreferredCompressionAlgorithms(false, new int[] {
CompressionAlgorithmTags.ZLIB,
CompressionAlgorithmTags.BZIP2,
CompressionAlgorithmTags.ZIP,
CompressionAlgorithmTags.UNCOMPRESSED});
subPackets.setFeature(false, Features.FEATURE_MODIFICATION_DETECTION);
// Generate sub key
generator = KeyPairGenerator.getInstance("ECDH", BouncyCastleProvider.PROVIDER_NAME);
generator.initialize(new ECNamedCurveGenParameterSpec("P-256"));
pair = generator.generateKeyPair();
PGPKeyPair pgpSubKey = new JcaPGPKeyPair(PublicKeyAlgorithmTags.ECDH, pair, new Date());
// Assemble key
PGPDigestCalculator calculator = new JcaPGPDigestCalculatorProviderBuilder()
.setProvider(BouncyCastleProvider.PROVIDER_NAME)
.build()
.get(HashAlgorithmTags.SHA1);
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
pgpMasterKey.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA512)
.setProvider(BouncyCastleProvider.PROVIDER_NAME);
PGPKeyRingGenerator pgpGenerator = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION,
pgpMasterKey, "alice@wonderland.lit", calculator, subPackets.generate(), null,
signerBuilder, null);
// Add sub key
subPackets.setKeyFlags(false, KeyFlags.ENCRYPT_STORAGE & KeyFlags.ENCRYPT_COMMS);
pgpGenerator.addSubKey(pgpSubKey, subPackets.generate(), null);
// Generate SecretKeyRing
PGPSecretKeyRing secretKeys = pgpGenerator.generateSecretKeyRing();
PGPPublicKeyRing publicKeys = pgpGenerator.generatePublicKeyRing();
// Test
/*
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(2048);
outputStream.write(secretKeys.getEncoded());
PGPPublicKeyRing publicKeys = new PGPPublicKeyRing(outputStream.toByteArray(), new BcKeyFingerprintCalculator());
Iterator<PGPPublicKey> iterator = secretKeys.getPublicKeys();
while (iterator.hasNext()) {
assertNotNull(publicKeys.getPublicKey(iterator.next().getKeyID()));
}
*/
}
}

View file

@ -0,0 +1,190 @@
/*
* Copyright 2018 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;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.util.io.Streams;
import org.junit.Ignore;
import org.junit.Test;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.algorithm.PublicKeyAlgorithm;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.decryption_verification.DecryptionStream;
import org.pgpainless.decryption_verification.PainlessResult;
import org.pgpainless.encryption_signing.EncryptionStream;
import org.pgpainless.key.collection.PGPKeyRing;
import org.pgpainless.key.generation.KeySpec;
import org.pgpainless.key.generation.type.ElGamal_GENERAL;
import org.pgpainless.key.generation.type.RSA_GENERAL;
import org.pgpainless.key.generation.type.length.ElGamalLength;
import org.pgpainless.key.generation.type.length.RsaLength;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.key.protection.UnprotectedKeysProtector;
import org.pgpainless.util.BCUtil;
public class EncryptDecryptTest extends AbstractPGPainlessTest {
private static final Logger LOGGER = Logger.getLogger(EncryptDecryptTest.class.getName());
private static final Charset UTF8 = Charset.forName("UTF-8");
private static final String testMessage = "Ah, Juliet, if the measure of thy joy\n" +
"Be heaped like mine, and that thy skill be more\n" +
"To blazon it, then sweeten with thy breath\n" +
"This neighbor air, and let rich musics tongue\n" +
"Unfold the imagined happiness that both\n" +
"Receive in either by this dear encounter.";
public EncryptDecryptTest() {
LOGGER.log(Level.INFO, "Plain Length: " + testMessage.getBytes(UTF8).length);
}
@Test
public void freshKeysRsaToElGamalTest()
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException,
IOException {
PGPKeyRing sender = PGPainless.generateKeyRing().simpleRsaKeyRing("romeo@montague.lit", RsaLength._3072);
PGPKeyRing recipient = PGPainless.generateKeyRing()
.withSubKey(KeySpec.getBuilder(ElGamal_GENERAL.withLength(ElGamalLength._3072)).withKeyFlags(KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS).withDefaultAlgorithms())
.withMasterKey(KeySpec.getBuilder(RSA_GENERAL.withLength(RsaLength._4096)).withKeyFlags(KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER).withDefaultAlgorithms())
.withPrimaryUserId("juliet@capulet.lit").withoutPassphrase().build();
encryptDecryptForSecretKeyRings(sender, recipient);
}
@Test
public void freshKeysRsaToRsaTest()
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException,
IOException {
PGPKeyRing sender = PGPainless.generateKeyRing().simpleRsaKeyRing("romeo@montague.lit", RsaLength._4096);
PGPKeyRing recipient = PGPainless.generateKeyRing().simpleRsaKeyRing("juliet@capulet.lit", RsaLength._4096);
encryptDecryptForSecretKeyRings(sender, recipient);
}
@Test
public void freshKeysEcToEcTest() throws IOException, PGPException, NoSuchAlgorithmException, NoSuchProviderException,
InvalidAlgorithmParameterException {
PGPKeyRing sender = PGPainless.generateKeyRing().simpleEcKeyRing("romeo@montague.lit");
PGPKeyRing recipient = PGPainless.generateKeyRing().simpleEcKeyRing("juliet@capulet.lit");
encryptDecryptForSecretKeyRings(sender, recipient);
}
@Test
public void freshKeysEcToRsaTest()
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException,
IOException {
PGPKeyRing sender = PGPainless.generateKeyRing().simpleEcKeyRing("romeo@montague.lit");
PGPKeyRing recipient = PGPainless.generateKeyRing().simpleRsaKeyRing("juliet@capulet.lit", RsaLength._4096);
encryptDecryptForSecretKeyRings(sender, recipient);
}
@Test
public void freshKeysRsaToEcTest()
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException,
IOException {
PGPKeyRing sender = PGPainless.generateKeyRing().simpleRsaKeyRing("romeo@montague.lit", RsaLength._4096);
PGPKeyRing recipient = PGPainless.generateKeyRing().simpleEcKeyRing("juliet@capulet.lit");
encryptDecryptForSecretKeyRings(sender, recipient);
}
@Ignore
private void encryptDecryptForSecretKeyRings(PGPKeyRing sender, PGPKeyRing recipient)
throws PGPException,
IOException {
PGPSecretKeyRing recipientSec = recipient.getSecretKeys();
PGPSecretKeyRing senderSec = sender.getSecretKeys();
PGPPublicKeyRing recipientPub = recipient.getPublicKeys();
PGPPublicKeyRing senderPub = sender.getPublicKeys();
SecretKeyRingProtector keyDecryptor = new UnprotectedKeysProtector();
byte[] secretMessage = testMessage.getBytes(UTF8);
ByteArrayOutputStream envelope = new ByteArrayOutputStream();
EncryptionStream encryptor = PGPainless.createEncryptor()
.onOutputStream(envelope)
.toRecipients(recipientPub)
.usingSecureAlgorithms()
.signWith(keyDecryptor, senderSec)
.noArmor();
PainlessResult encryptionResult = encryptor.getResult();
assertFalse(encryptionResult.getAllSignatureKeyFingerprints().isEmpty());
for (long keyId : encryptionResult.getAllSignatureKeyFingerprints()) {
assertTrue(BCUtil.keyRingContainsKeyWithId(senderPub, keyId));
}
assertFalse(encryptionResult.getRecipientKeyIds().isEmpty());
for (long keyId : encryptionResult.getRecipientKeyIds()) {
assertTrue(BCUtil.keyRingContainsKeyWithId(recipientPub, keyId));
}
assertEquals(SymmetricKeyAlgorithm.AES_256, encryptionResult.getSymmetricKeyAlgorithm());
Streams.pipeAll(new ByteArrayInputStream(secretMessage), encryptor);
encryptor.close();
byte[] encryptedSecretMessage = envelope.toByteArray();
LOGGER.log(Level.INFO, "Sender: " + PublicKeyAlgorithm.fromId(senderPub.getPublicKey().getAlgorithm()) +
" Receiver: " + PublicKeyAlgorithm.fromId(recipientPub.getPublicKey().getAlgorithm()) +
" Encrypted Length: " + encryptedSecretMessage.length);
// Juliet trieth to comprehend Romeos words
ByteArrayInputStream envelopeIn = new ByteArrayInputStream(encryptedSecretMessage);
DecryptionStream decryptor = PGPainless.createDecryptor()
.onInputStream(envelopeIn)
.decryptWith(keyDecryptor, BCUtil.keyRingsToKeyRingCollection(recipientSec))
.verifyWith(BCUtil.keyRingsToKeyRingCollection(senderPub))
.ignoreMissingPublicKeys()
.build();
ByteArrayOutputStream decryptedSecretMessage = new ByteArrayOutputStream();
Streams.pipeAll(decryptor, decryptedSecretMessage);
decryptor.close();
assertTrue(Arrays.equals(secretMessage, decryptedSecretMessage.toByteArray()));
PainlessResult result = decryptor.getResult();
assertTrue(result.containsVerifiedSignatureFrom(senderPub));
assertTrue(result.isIntegrityProtected());
assertTrue(result.isSigned());
assertTrue(result.isEncrypted());
assertTrue(result.isVerified());
}
}

View file

@ -0,0 +1,69 @@
/*
* Copyright 2018 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;
import static junit.framework.TestCase.assertTrue;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.util.Arrays;
import java.util.Iterator;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
import org.junit.Test;
import org.pgpainless.key.collection.PGPKeyRing;
public class ImportExportKeyTest {
static {
Security.addProvider(new BouncyCastleProvider());
}
/**
* This test is failing. Not sure if a bug in BC or in my code...
* @throws PGPException very
* @throws NoSuchAlgorithmException much
* @throws NoSuchProviderException some
* @throws InvalidAlgorithmParameterException annoying
* @throws IOException exceptions
*/
@Test
public void test()
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException,
IOException {
PGPKeyRing keyRing = PGPainless.generateKeyRing().simpleEcKeyRing("alice@bla.blub");
PGPSecretKeyRing secretKeys = keyRing.getSecretKeys();
PGPPublicKeyRing publicKeys = keyRing.getPublicKeys();
BcKeyFingerprintCalculator calc = new BcKeyFingerprintCalculator();
byte[] bytes = publicKeys.getEncoded();
PGPPublicKeyRing parsed = new PGPPublicKeyRing(bytes, calc);
assertTrue(Arrays.equals(publicKeys.getEncoded(), parsed.getEncoded()));
Iterator<PGPPublicKey> it = secretKeys.getPublicKeys();
assertTrue(it.hasNext());
it.next();
assertTrue(it.hasNext());
}
}

View file

@ -0,0 +1,58 @@
/*
* Copyright 2018 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;
import static junit.framework.TestCase.assertTrue;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.util.Arrays;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.junit.Test;
import org.pgpainless.key.collection.PGPKeyRing;
import org.pgpainless.util.BCUtil;
import org.pgpainless.util.KeyRingSubKeyFix;
public class KeyRingSubKeyFixTest {
static {
Security.addProvider(new BouncyCastleProvider());
}
@Test
public void test()
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException,
IOException {
PGPKeyRing ring = PGPainless.generateKeyRing().simpleEcKeyRing("hallo@welt.de");
PGPSecretKeyRing secretKeys = ring.getSecretKeys();
PGPPublicKeyRing publicKeys = ring.getPublicKeys();
PGPSecretKeyRing fixed = KeyRingSubKeyFix.repairSubkeyPackets(secretKeys, null, null);
assertTrue(Arrays.equals(secretKeys.getEncoded(), fixed.getEncoded()));
PGPPublicKeyRing fixedPub = BCUtil.publicKeyRingFromSecretKeyRing(fixed);
assertTrue(Arrays.equals(publicKeys.getEncoded(), fixedPub.getEncoded()));
}
}

View file

@ -0,0 +1,128 @@
/*
* Copyright 2018 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;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.util.io.Streams;
import org.junit.Ignore;
import org.pgpainless.key.collection.PGPKeyRing;
import org.pgpainless.key.generation.type.length.RsaLength;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.key.protection.UnprotectedKeysProtector;
/**
* Class used to determine the length of cipher-text depending on used algorithms.
*/
public class LengthTest extends AbstractPGPainlessTest {
private static final Logger LOGGER = Logger.getLogger(LengthTest.class.getName());
// @Test
public void ecEc()
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException,
IOException {
LOGGER.log(Level.INFO, "\nEC -> EC");
PGPKeyRing sender = PGPainless.generateKeyRing().simpleEcKeyRing("simplejid@server.tld");
PGPKeyRing recipient = PGPainless.generateKeyRing().simpleEcKeyRing("otherjid@other.srv");
encryptDecryptForSecretKeyRings(sender, recipient);
}
// @Test
public void RsaRsa()
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException,
IOException {
LOGGER.log(Level.INFO, "\nRSA-2048 -> RSA-2048");
PGPKeyRing sender = PGPainless.generateKeyRing().simpleRsaKeyRing("simplejid@server.tld", RsaLength._2048);
PGPKeyRing recipient = PGPainless.generateKeyRing().simpleRsaKeyRing("otherjid@other.srv", RsaLength._2048);
encryptDecryptForSecretKeyRings(sender, recipient);
}
// @Test
public void RsaRsa4096()
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException,
IOException {
LOGGER.log(Level.INFO, "\nRSA-4096 -> RSA-4096");
PGPKeyRing sender = PGPainless.generateKeyRing().simpleRsaKeyRing("simplejid@server.tld", RsaLength._4096);
PGPKeyRing recipient = PGPainless.generateKeyRing().simpleRsaKeyRing("otherjid@other.srv", RsaLength._4096);
encryptDecryptForSecretKeyRings(sender, recipient);
}
// @Test
public void rsaEc() throws PGPException, IOException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
NoSuchProviderException {
LOGGER.log(Level.INFO, "\nRSA-2048 -> EC");
PGPKeyRing sender = PGPainless.generateKeyRing().simpleRsaKeyRing("simplejid@server.tld", RsaLength._2048);
PGPKeyRing recipient = PGPainless.generateKeyRing().simpleEcKeyRing("otherjid@other.srv");
encryptDecryptForSecretKeyRings(sender, recipient);
}
// @Test
public void ecRsa()
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException,
IOException {
LOGGER.log(Level.INFO, "\nEC -> RSA-2048");
PGPKeyRing sender = PGPainless.generateKeyRing().simpleEcKeyRing("simplejid@server.tld");
PGPKeyRing recipient = PGPainless.generateKeyRing().simpleRsaKeyRing("otherjid@other.srv", RsaLength._2048);
encryptDecryptForSecretKeyRings(sender, recipient);
}
@Ignore
private void encryptDecryptForSecretKeyRings(PGPKeyRing sender, PGPKeyRing recipient)
throws PGPException,
IOException {
PGPSecretKeyRing recipientSec = recipient.getSecretKeys();
PGPSecretKeyRing senderSec = sender.getSecretKeys();
PGPPublicKeyRing recipientPub = recipient.getPublicKeys();
PGPPublicKeyRing senderPub = sender.getPublicKeys();
SecretKeyRingProtector keyDecryptor = new UnprotectedKeysProtector();
for (int i = 1; i <= 100; i++) {
byte[] secretMessage = new byte[i * 20];
new Random().nextBytes(secretMessage);
ByteArrayOutputStream envelope = new ByteArrayOutputStream();
OutputStream encryptor = PGPainless.createEncryptor()
.onOutputStream(envelope)
.toRecipients(recipientPub)
// .doNotEncrypt()
.usingSecureAlgorithms()
.signWith(keyDecryptor, senderSec)
.noArmor();
Streams.pipeAll(new ByteArrayInputStream(secretMessage), encryptor);
encryptor.close();
byte[] encryptedSecretMessage = envelope.toByteArray();
LOGGER.log(Level.INFO,"\n" + encryptedSecretMessage.length);
}
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright 2018 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;
import static junit.framework.TestCase.assertEquals;
import java.io.IOException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.junit.Test;
import org.pgpainless.key.OpenPgpV4Fingerprint;
public class OpenPgpV4FingerprintTest {
@Test(expected = IllegalArgumentException.class)
public void fpTooShort() {
String fp = "484f57414c495645"; // Asking Mark
new OpenPgpV4Fingerprint(fp);
}
@Test(expected = IllegalArgumentException.class)
public void invalidHexTest() {
String fp = "UNFORTUNATELYTHISISNOVALIDHEXADECIMALDOH";
new OpenPgpV4Fingerprint(fp);
}
@Test
public void validFingerprintTest() {
String fp = "4A4F48414E4E53454E2049532041204E45524421";
OpenPgpV4Fingerprint finger = new OpenPgpV4Fingerprint(fp);
assertEquals(fp, finger.toString());
}
@Test
public void convertsToUpperCaseTest() {
String fp = "444f4e5420552048415645204120484f4242593f";
OpenPgpV4Fingerprint finger = new OpenPgpV4Fingerprint(fp);
assertEquals("444F4E5420552048415645204120484F4242593F", finger.toString());
}
@Test
public void equalsOtherFingerprintTest() {
OpenPgpV4Fingerprint finger = new OpenPgpV4Fingerprint("5448452043414b452049532041204c4945212121");
assertEquals(finger, new OpenPgpV4Fingerprint("5448452043414B452049532041204C4945212121"));
}
@Test
public void keyIdTest() throws IOException {
PGPPublicKey key = TestKeys.getJulietPublicKeyRing().getPublicKey();
long keyId = key.getKeyID();
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(key);
assertEquals(keyId, fingerprint.getKeyId());
}
}

View file

@ -0,0 +1,63 @@
/*
* Copyright 2018 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;
import static junit.framework.TestCase.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.openpgp.PGPException;
import org.junit.Test;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.util.Passphrase;
public class SymmetricTest extends AbstractPGPainlessTest {
private static final Logger LOGGER = Logger.getLogger(SymmetricTest.class.getName());
private static final String message = "I grew up with the understanding that the world " +
"I lived in was one where people enjoyed a sort of freedom " +
"to communicate with each other in privacy, without it " +
"being monitored, without it being measured or analyzed " +
"or sort of judged by these shadowy figures or systems, " +
"any time they mention anything that travels across " +
"public lines.\n" +
"\n" +
"- Edward Snowden -";
@Test
public void testSymmetricEncryptionDecryption() throws IOException, PGPException {
byte[] plain = message.getBytes();
Passphrase passphrase = new Passphrase("choose_a_better_password_please".toCharArray());
byte[] enc = PGPainless.encryptWithPassword(plain, passphrase, SymmetricKeyAlgorithm.AES_128);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ArmoredOutputStream armor = new ArmoredOutputStream(out);
armor.write(enc);
armor.flush();
armor.close();
// Print cipher text for validation with GnuPG.
LOGGER.log(Level.INFO, new String(out.toByteArray()));
byte[] plain2 = PGPainless.decryptWithPassword(enc, passphrase);
assertTrue(Arrays.equals(plain, plain2));
}
}

View file

@ -0,0 +1,267 @@
/*
* Copyright 2018 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;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
public class TestKeys {
private static final KeyFingerPrintCalculator calc = new BcKeyFingerprintCalculator();
private static PGPSecretKeyRing julietSecretKeyRing = null;
private static PGPPublicKeyRing julietPublicKeyRing = null;
private static PGPSecretKeyRing romeoSecretKeyRing = null;
private static PGPPublicKeyRing romeoPublicKeyRing = null;
private static PGPSecretKeyRingCollection julietSecretKeyRingCollection = null;
private static PGPPublicKeyRingCollection julietPublicKeyRingCollection = null;
private static PGPSecretKeyRingCollection romeoSecretKeyRingCollection = null;
private static PGPPublicKeyRingCollection romeoPublicKeyRingCollection = null;
public static final String JULIET_UID = "xmpp:juliet@capulet.lit";
public static final long JULIET_KEY_ID = -5425419407118114754L;
/**
* Public key of xmpp:juliet@capulet.lit.
*/
public static final String JULIET_PUB = "" +
"-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
"mQENBFrxov4BCAChZwPrBxxIlwzpieR5T2pnaOZLWH0WqSON6rVjvfbJHWdDi3Th\n" +
"remHW4gg4IBSTXkVFDIeQNVcOvGNgMg3Oe/x0I6FK12jrw9prycmjFxQ7A0ix7ZG\n" +
"UkTF5jITgzJbkH100gYfXtZsfTyvgISSAT//6vvvQPZ3zCr09XvAG0CyQ1BhULsv\n" +
"mVRe4Oh5b0VK4kLdv+GiA/T+49UKZj6lne9Vdti16ZIj7teVCbicfdhpTzsjur42\n" +
"r8ptouKAuyFPw9KnGNwVlIiv5jt/Kit/LoOBenh74sitsCXq8IQ9kKp/eNt8TF4u\n" +
"D4IGpxnJfB8XCiixYHoFEajmQBVJXNYtvoPvABEBAAG0F3htcHA6anVsaWV0QGNh\n" +
"cHVsZXQubGl0iQFOBBMBCAA4FiEEHQGMdy34xe+GodzJtLUJy1k24D4FAlrxov4C\n" +
"Gy8FCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQtLUJy1k24D6H7AgAoTjx4ezc\n" +
"A83NeOY3tMHVQTM7hKuy0wMcSzQgVgJmhLYRZS8r+FocPZua/eke49GPhe2yozvl\n" +
"ByWHtotklQeJiwOKxuPKMzneVA1ZK3/9LdGvtZlHMcAkEKDhit8HIaEcsFd4Z1re\n" +
"EhF2lyvY/E+rrx9YxV0QjisSWV2dSptv6FeGSztr9e5E+Head6hEQhsugiTVRF+1\n" +
"6mG90te0WGQ9YNiJ2FJovx5kBLTTuhwUz8Oacqihd2+RDDI5p3wJoogVL31aNb4n\n" +
"c7dGo8ieJPHGlkBsOfmreSxijTodZz9MXsgcx7b//u0uQryViJoZHWbtnXOFjjNc\n" +
"GWBtS084NKWl9w==\n" +
"=ecwX\n" +
"-----END PGP PUBLIC KEY BLOCK-----";
/**
* Private key of xmpp:juliet@capulet.lit.
*/
public static final String JULIET_SEC = "" +
"-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
"\n" +
"lQOYBFrxov4BCAChZwPrBxxIlwzpieR5T2pnaOZLWH0WqSON6rVjvfbJHWdDi3Th\n" +
"remHW4gg4IBSTXkVFDIeQNVcOvGNgMg3Oe/x0I6FK12jrw9prycmjFxQ7A0ix7ZG\n" +
"UkTF5jITgzJbkH100gYfXtZsfTyvgISSAT//6vvvQPZ3zCr09XvAG0CyQ1BhULsv\n" +
"mVRe4Oh5b0VK4kLdv+GiA/T+49UKZj6lne9Vdti16ZIj7teVCbicfdhpTzsjur42\n" +
"r8ptouKAuyFPw9KnGNwVlIiv5jt/Kit/LoOBenh74sitsCXq8IQ9kKp/eNt8TF4u\n" +
"D4IGpxnJfB8XCiixYHoFEajmQBVJXNYtvoPvABEBAAEAB/4jMbXagW3q7DkOEZnm\n" +
"0+jVTLvu0QhRsScGEphj+++8sfMq+NVPQp9p+w0Hcjy49ZjB/mnhS+zaVCYI33yJ\n" +
"AlKubXYuVqLwBsO7HUzRrIiSwq4ol9jIo7bIWmYv+As6iRq6JvPb0k+6T2K0uDbw\n" +
"KWKduM0fwhAcVkJFsOO/o5GrbQaJc3oioFk8uFWTnO+FPBRTJ9oTlVG2M/tEatZK\n" +
"gl7I8Ukl0YYruCNUFKZ0tvO8HqulxBgUbGPBer1uOlfUD4RXdc8/PUiFKNo48XSu\n" +
"ZUEAZKGbFBjuX5Z8ha7+sUMEYEt70qlbkiLQxgHKAmpyridAk3q/SB3y2VB8Ik7I\n" +
"gpExBADInzLROYuUcXqmty+znVwm6nRIB75JBAy778zgIxx1v0O3QlVnR+YI8gJM\n" +
"mQ/9pD6LyP9hktWDmJxG8tX+kSuIp3wNJc5EMeXtCCmkUW0CP1gUhAbNW3MezKa5\n" +
"II5IhE9RgIsYqSU8ZgeIh72ON8XTp8i/wGipCXvJPggSAMXukQQAzfRmtLW+JHEK\n" +
"B8ETIYh8IUjXJ6TVlmuBwZ0eXjCpqy9arJi6tacesDJwnL3sqOMQWUmqGsCGSKA5\n" +
"cLITkVsxX/htIq8GFyludjg8t4Nr+fOGfChEq8QE0PHE2CgskQMHpfHvfIdnwKve\n" +
"Fg2Q8twoMw849O6PF3k/848Z65lDin8EAMDbuPWL7KU2sWeqvDEuoulS5K1gsq8X\n" +
"p3Od3+f0OG8YViMjKcVlSKHVvdlK4dlsccJrJJx6VzotV47LsmvVbzDwUE//MYq7\n" +
"QwwQetZbpdQZDysSGVqHMTuAg/1pr2u5rqh4cFqCYatgZwinEI2TQMXEqnSc+mj8\n" +
"xp/LNq5BZZQuO4y0F3htcHA6anVsaWV0QGNhcHVsZXQubGl0iQFOBBMBCAA4FiEE\n" +
"HQGMdy34xe+GodzJtLUJy1k24D4FAlrxov4CGy8FCwkIBwIGFQoJCAsCBBYCAwEC\n" +
"HgECF4AACgkQtLUJy1k24D6H7AgAoTjx4ezcA83NeOY3tMHVQTM7hKuy0wMcSzQg\n" +
"VgJmhLYRZS8r+FocPZua/eke49GPhe2yozvlByWHtotklQeJiwOKxuPKMzneVA1Z\n" +
"K3/9LdGvtZlHMcAkEKDhit8HIaEcsFd4Z1reEhF2lyvY/E+rrx9YxV0QjisSWV2d\n" +
"Sptv6FeGSztr9e5E+Head6hEQhsugiTVRF+16mG90te0WGQ9YNiJ2FJovx5kBLTT\n" +
"uhwUz8Oacqihd2+RDDI5p3wJoogVL31aNb4nc7dGo8ieJPHGlkBsOfmreSxijTod\n" +
"Zz9MXsgcx7b//u0uQryViJoZHWbtnXOFjjNcGWBtS084NKWl9w==\n" +
"=yPPE\n" +
"-----END PGP PRIVATE KEY BLOCK-----";
public static final String ROMEO_UID = "xmpp:romeo@montague.lit";
public static final long ROMEO_KEY_ID = 334147643349279223L;
/**
* Public key of xmpp:romeo@montague.lit.
*/
public static final String ROMEO_PUB = "" +
"-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
"mQENBFrxopkBCADiYg/+mEObXgxuMW6/LFKpEyaJK9pBMgutuxnYZ9PXWZmOhDIT\n" +
"Ugm9X9YJ3Qh94KaHge9F4uCeFASmM1vvUTRFTEb1W5RR9ZE/sy/cdAttnZ5JloPi\n" +
"CT3HDMIJAxIXhRJkeUR9GUb51ql27bMXl6lFh865VdNSXN/B8FzRQHENxv1Bq/6Z\n" +
"iQOViIETeRRgO+u6u2iZkYlHgYMaoMK7+YiNlHXanU9Atcuaz0ZCJS/XFNH89iqB\n" +
"Kvnv7KCQh4FhrNMLJRzNPXV8MY05nn0zF72qeEsniB16Xde18lMro8fQehg2mLwc\n" +
"XGtCwCKI6QbZVxYQt77r3ZACiwl66soFWijVABEBAAG0F3htcHA6cm9tZW9AbW9u\n" +
"dGFndWUubGl0iQFOBBMBCAA4FiEENdKZ0IovfYAjCwldBKMhguBeIfcFAlrxopkC\n" +
"Gy8FCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQBKMhguBeIfcj8AgAu1wubUwr\n" +
"2aQmDN3OqRM4M4yRL3oyYMkCKIjqD6KEeFsIXSSkXOuREJKEo8Mb1+ewV0SYmHCC\n" +
"K3bKKq3m71AQ7evDhKGshacPYesiDvMdHWQdQnjfaoHhyn9qIKl7H0Xv1yf/wyuG\n" +
"ANy1jYgtCEuYw7D+EsqNDdn8Xh+k/9s4aMI/6mfC0yGZgG8EyLTfbZkGPoS4aZfV\n" +
"AGFbuqryg48dXtnuzAPKcdgMTTMSnmR729YlfkjCffcFaldyXoe1VMbudUO7nkO9\n" +
"g65i5EXenkbc2h0TRDQ4lDFQyModqFTwYFYxAf/RA6tuhIQEoCnpCytFMvrRKMb3\n" +
"Bx5vYRDVmE3jeg==\n" +
"=2jSg\n" +
"-----END PGP PUBLIC KEY BLOCK-----";
/**
* Private key of xmpp:romeo@montague.lit.
*/
public static final String ROMEO_SEC = "" +
"-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
"\n" +
"lQOYBFrxopkBCADiYg/+mEObXgxuMW6/LFKpEyaJK9pBMgutuxnYZ9PXWZmOhDIT\n" +
"Ugm9X9YJ3Qh94KaHge9F4uCeFASmM1vvUTRFTEb1W5RR9ZE/sy/cdAttnZ5JloPi\n" +
"CT3HDMIJAxIXhRJkeUR9GUb51ql27bMXl6lFh865VdNSXN/B8FzRQHENxv1Bq/6Z\n" +
"iQOViIETeRRgO+u6u2iZkYlHgYMaoMK7+YiNlHXanU9Atcuaz0ZCJS/XFNH89iqB\n" +
"Kvnv7KCQh4FhrNMLJRzNPXV8MY05nn0zF72qeEsniB16Xde18lMro8fQehg2mLwc\n" +
"XGtCwCKI6QbZVxYQt77r3ZACiwl66soFWijVABEBAAEAB/4mu5p69/hRQ+UikWie\n" +
"Yun9rZ4hSBR+pR5kaifA4/rV1Km2PZ4HujiaYyRO6beDOgWkF7IlpezCfzBQc2ce\n" +
"ailkVemqHzIgV8CzQmhE8sHlzlr/wjXsXaJpRSCJxDG7PnRoJmt2b/W512WFSKQk\n" +
"vDklAVh4U1vlsqhCGWr4DmuJbJkRyDhcX01tplRwim283F7bGqRcMBmKMZHiMgVc\n" +
"0u84EYKKVizJ3YAaaVqZyHb4qdeKK2ak3fPNuGT/oGd2sxnkL+BZGjJpu3RGpTA1\n" +
"tbOvOQnJGHQtABFxE8n6H9dHPJGtgyz2+udjUhL/P/E3PDoXazZkXRq2oHZKgg0f\n" +
"AwOBBADsWncHgvz15rXPF7O6AivbGTJ5ctkgVy4U3Fu2sk9rf0fx0sryBSqtTBw1\n" +
"Uvn/p9RwTsKw6fng6Nf78xpZFlUDB00YCcuWkGodxvjTAyB0dtBmkhopeKi0dmHh\n" +
"ndnR6Pv0CsXu8nG7lUi+q6s3oc4h2OfDBhrqsyYY5M2gGit3dQQA9TNuinJD9XXv\n" +
"QRyauMnSJ5xRcfOu8QCxZlllCvffZjSGCPoVjUpJEe9qsVbXVj2GYCxjLCSXV0V+\n" +
"vlJfdPrl1BhZ3fmEpg0u7SyGDDOe8fe1ehk5sAeL8O0eFWlPSEaEccsjlpJ2FO0n\n" +
"P04SZdOeM6wmhDTEDzpFnjbPndQTH+ED/R1zNzr55DvxQodmrW/BvTmhGQ22rHtk\n" +
"IUfbeMaVfUvNLJA/JksrUIx3Gga9QCDZgfm1RsRhLUlHiqTQe23sPWgKOsbf5O1j\n" +
"XJZaCNZ7LloVQbkG7xFcnb/n1+JjBr4FxXjAA6cY/iRGlznjIIaasyklKm1/4LuQ\n" +
"hnH3QqTvCN3dOFS0F3htcHA6cm9tZW9AbW9udGFndWUubGl0iQFOBBMBCAA4FiEE\n" +
"NdKZ0IovfYAjCwldBKMhguBeIfcFAlrxopkCGy8FCwkIBwIGFQoJCAsCBBYCAwEC\n" +
"HgECF4AACgkQBKMhguBeIfcj8AgAu1wubUwr2aQmDN3OqRM4M4yRL3oyYMkCKIjq\n" +
"D6KEeFsIXSSkXOuREJKEo8Mb1+ewV0SYmHCCK3bKKq3m71AQ7evDhKGshacPYesi\n" +
"DvMdHWQdQnjfaoHhyn9qIKl7H0Xv1yf/wyuGANy1jYgtCEuYw7D+EsqNDdn8Xh+k\n" +
"/9s4aMI/6mfC0yGZgG8EyLTfbZkGPoS4aZfVAGFbuqryg48dXtnuzAPKcdgMTTMS\n" +
"nmR729YlfkjCffcFaldyXoe1VMbudUO7nkO9g65i5EXenkbc2h0TRDQ4lDFQyMod\n" +
"qFTwYFYxAf/RA6tuhIQEoCnpCytFMvrRKMb3Bx5vYRDVmE3jeg==\n" +
"=LZ1b\n" +
"-----END PGP PRIVATE KEY BLOCK-----";
public static PGPSecretKeyRing getJulietSecretKeyRing() throws IOException, PGPException {
if (julietSecretKeyRing == null) {
julietSecretKeyRing = new PGPSecretKeyRing(
PGPUtil.getDecoderStream(new ByteArrayInputStream(JULIET_SEC.getBytes())), calc);
}
return julietSecretKeyRing;
}
public static PGPSecretKeyRingCollection getJulietSecretKeyRingCollection() throws IOException, PGPException {
if (julietSecretKeyRingCollection == null) {
julietSecretKeyRingCollection = new PGPSecretKeyRingCollection(
PGPUtil.getDecoderStream(new ByteArrayInputStream(JULIET_SEC.getBytes())), calc);
}
return julietSecretKeyRingCollection;
}
public static PGPPublicKeyRing getJulietPublicKeyRing() throws IOException {
if (julietPublicKeyRing == null) {
julietPublicKeyRing = new PGPPublicKeyRing(
PGPUtil.getDecoderStream(new ByteArrayInputStream(JULIET_PUB.getBytes())), calc);
}
return julietPublicKeyRing;
}
public static PGPPublicKeyRingCollection getJulietPublicKeyRingCollection() throws IOException, PGPException {
if (julietPublicKeyRingCollection == null) {
julietPublicKeyRingCollection = new PGPPublicKeyRingCollection(
PGPUtil.getDecoderStream(new ByteArrayInputStream(JULIET_PUB.getBytes())), calc);
}
return julietPublicKeyRingCollection;
}
public static PGPSecretKeyRing getRomeoSecretKeyRing() throws IOException, PGPException {
if (romeoSecretKeyRing == null) {
romeoSecretKeyRing = new PGPSecretKeyRing(
PGPUtil.getDecoderStream(new ByteArrayInputStream(ROMEO_SEC.getBytes())), calc);
}
return romeoSecretKeyRing;
}
public static PGPSecretKeyRingCollection getRomeoSecretKeyRingCollection() throws IOException, PGPException {
if (romeoSecretKeyRingCollection == null) {
romeoSecretKeyRingCollection = new PGPSecretKeyRingCollection(
PGPUtil.getDecoderStream(new ByteArrayInputStream(ROMEO_SEC.getBytes())), calc);
}
return romeoSecretKeyRingCollection;
}
public static PGPPublicKeyRing getRomeoPublicKeyRing() throws IOException {
if (romeoPublicKeyRing == null) {
romeoPublicKeyRing = new PGPPublicKeyRing(
PGPUtil.getDecoderStream(new ByteArrayInputStream(ROMEO_PUB.getBytes())), calc);
}
return romeoPublicKeyRing;
}
public static PGPPublicKeyRingCollection getRomeoPublicKeyRingCollection() throws IOException, PGPException {
if (romeoPublicKeyRingCollection == null) {
romeoPublicKeyRingCollection = new PGPPublicKeyRingCollection(
PGPUtil.getDecoderStream(new ByteArrayInputStream(ROMEO_PUB.getBytes())), calc);
}
return romeoPublicKeyRingCollection;
}
public static final String TEST_MESSAGE_01_PLAIN = "This message is encrypted\n";
/**
* Test Message signed with {@link #JULIET_SEC} and encrypted for {@link #JULIET_PUB}.
*/
public static final String TEST_MESSAGE_01 = "-----BEGIN PGP MESSAGE-----\n" +
"\n" +
"hQGMAwAAAAAAAAAAAQwAoJtfpcBPCwhUzzHuVIcBzBLyfIWT/EJ527neb46lN56S\n" +
"B05BTIRudIeCsPYz81jwiFi/k0MBecRfozZ1xCPByo8ohSvRgzEHEkCNgObQ1bz0\n" +
"iB+Xb76OEzFOCPUebTaVscLNf8ak/GSzaW7jDc+5vnvDf7cV0x26pe4odpS/U5Tr\n" +
"cO3wb/47K+sJ1cxJmPtcD41O02xu3QisQKPrimM0Kue6ziGeKyw1RkSowv9U47TK\n" +
"wppPCHOTli2Nf+gZizF1oyQZzPGst4fjujygcIoajplfW9nZvxsbmYRSLSdmV9m6\n" +
"k1jQbPDUhVs0gstH92C6hPpoBWxoxkHcwz8gy36nCyB6cYGyq3oN1UnGU4afPyD5\n" +
"SmmEjELBd2i2Ll/DYk2x06SnKZMQuWrSCZzWgl/9HsPo5ydVb97OjuEpWtW9xDMA\n" +
"KlYPNWEq+b+akOEstNraC3pfVKvypz6ZzaMAS1gWWNYg8dlwBJOUVMSo7iLaUQkK\n" +
"yp4uH1DlsyVu1atCUc8thQIMAwAAAAAAAAAAAQ/5AdiZ/sG859Y/rGR7U/8MzGg0\n" +
"j3f2vrgDF/0NRRk5aqd1lb4CaZvrztcYqW3cEK7iF9rKwImZZiWIptjJ9Mz6f1Zl\n" +
"FbODObSVRZAcZqYGswEEfsQvpQFlwG6Qx48OaQaDPr147raFI3C3kEU9Nb2VBg8+\n" +
"MevJaXJft5PXwUTG2Qvfxqr/3hfGAwB4/zHwA8vFd1np3spryfrC9Dq8UXUoRXIS\n" +
"xaFPiLEYt8rLef8f11OypEpmknIibu9jjJtuVZo+SjP6jgLHDwM7rqCZFITM2Qra\n" +
"2iBCt8YVcIiTK137t+EfsdVN/KHiRbc++e9zUbGMEextbtNbdoFOU4dnKBm6Su8l\n" +
"Z5UerNbR8D7+xJKfAEabdi0qI7QFmhTZ/4H/22yrvoD9jMFSBXUTE9ENIX9Hfqom\n" +
"UdsHfuE+5PC0JjkZkhchDO1M7XBX++lBCFsq2abfdpmaX+roVX0iTGboxr5Ag1Cf\n" +
"T2zWyRX/XKnvmdeGICV5qjy/ThuSWvAclazyFxWLamMztJq5BRpfAzKNQRDqlmKw\n" +
"eePtKW2EWUIjFQ5/UAM6Edu/K34ksFxb0w6YGLzQSskGr7gGAipLmpek6vcUSUA1\n" +
"oc9XJGdpx93GDRcqDjKDt/ej06VxG33/pW65ntf5QM/+LScGqaLhAHyEOsBzVIXY\n" +
"BONcadSgzkTrlbSMGAmFAQwDtLUJy1k24D4BB/0brqR0UN1LtO+Lc/vN6X/Um2CZ\n" +
"CM6MRhPnXP63Q9HHkGJ2S8zGWvQLwWL9Y14CFCgm6rACLBSIyPbihhC2OC8afhSy\n" +
"apGkdHtdghS2egs2U8qlJ2Y32IAG9CcUtNkRjxp+/RWSrmZeuL4l7DXCyH5lUadx\n" +
"5bPZhAHqW9408q2rQd9dBg2o7ciGXTJSKVahjuiB/O0gchOnbqnlYJbKbCkntXUo\n" +
"c7h4w1e8MutisSJorh7kbxgxUJSboZzEkiUfnoacPTz6bL+re9tmnpvlee70sIyM\n" +
"BiYRCyPw7Ice4R3XyWtsMTjT/wjZ//whMpWdy2drcJSyhh+GQMbekTVsNWod0lQB\n" +
"JTPUfti2VU7PMB3LjJA+l/T9iWPPx8lirnLhXOOerWKH9I5Wo4Kqv/47aJhfMO6+\n" +
"jmLekAOylq+9DizrslW/EUgQyjIbcWfmyMiV6E2RwbI93tE=\n" +
"=GAhR\n" +
"-----END PGP MESSAGE-----";
}

View file

@ -0,0 +1,74 @@
/*
* Copyright 2018 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;
import static junit.framework.TestCase.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Collections;
import junit.framework.TestCase;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.util.io.Streams;
import org.junit.Test;
import org.pgpainless.decryption_verification.DecryptionStream;
import org.pgpainless.key.protection.UnprotectedKeysProtector;
public class TestKeysTest extends AbstractPGPainlessTest {
private final PGPSecretKeyRing juliet;
private final PGPSecretKeyRing romeo;
public TestKeysTest() throws IOException, PGPException {
this.juliet = TestKeys.getJulietSecretKeyRing();
this.romeo = TestKeys.getRomeoSecretKeyRing();
}
@Test
public void keyIdTest() {
TestCase.assertEquals(TestKeys.JULIET_KEY_ID, juliet.getSecretKey().getKeyID());
TestCase.assertEquals(TestKeys.ROMEO_KEY_ID, romeo.getSecretKey().getKeyID());
}
@Test
public void decryptVerifyTest() throws Exception {
String encryptedMessage = TestKeys.TEST_MESSAGE_01;
DecryptionStream decryptor = PGPainless.createDecryptor()
.onInputStream(new ByteArrayInputStream(encryptedMessage.getBytes()))
.decryptWith(new UnprotectedKeysProtector(), new PGPSecretKeyRingCollection(Collections.singleton(juliet)))
.verifyWith(Collections.singleton(new PGPPublicKeyRing(Collections.singletonList(juliet.getPublicKey()))))
.ignoreMissingPublicKeys()
.build();
ByteArrayOutputStream toPlain = new ByteArrayOutputStream();
Streams.pipeAll(decryptor, toPlain);
decryptor.close();
toPlain.close();
byte[] expected = TestKeys.TEST_MESSAGE_01_PLAIN.getBytes(Charset.forName("UTF-8"));
byte[] actual = toPlain.toByteArray();
assertTrue(Arrays.equals(expected, actual));
}
}