Initial commit

This commit is contained in:
Paul Schaub 2018-06-02 21:21:35 +02:00
commit 6dd2e83acf
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
27 changed files with 1325 additions and 0 deletions

14
.gitignore vendored Normal file
View File

@ -0,0 +1,14 @@
.idea
.gradle
out/
*.iws
*.iml
*.ipr
*.class
*.log
*.jar
!gradle-wrapper.jar

18
build.gradle Normal file
View File

@ -0,0 +1,18 @@
plugins {
id 'java'
}
group 'de.vanitasvitae.crypto'
version '0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile 'org.bouncycastle:bcprov-jdk15on:1.59'
compile 'org.bouncycastle:bcpg-jdk15on:1.59'
}

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-bin.zip

172
gradlew vendored Executable file
View File

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
gradlew.bat vendored Normal file
View File

@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

2
settings.gradle Normal file
View File

@ -0,0 +1,2 @@
rootProject.name = 'pgpainless'

View File

@ -0,0 +1,5 @@
package de.vanitasvitae.crypto.pgpainless;
public class EncryptionBuilder {
}

View File

@ -0,0 +1,44 @@
package de.vanitasvitae.crypto.pgpainless;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.util.Base64;
import de.vanitasvitae.crypto.pgpainless.key.algorithm.KeyFlag;
import de.vanitasvitae.crypto.pgpainless.key.generation.KeySpec;
import de.vanitasvitae.crypto.pgpainless.key.generation.type.DSA;
import de.vanitasvitae.crypto.pgpainless.key.generation.type.RSA_GENERAL;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
public class Main {
public static void main(String[] args)
throws NoSuchAlgorithmException, PGPException, NoSuchProviderException, IOException {
Security.addProvider(new BouncyCastleProvider());
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.generateCompositeKeyRing()
.withSubKey(
KeySpec.getBuilder()
.ofType(RSA_GENERAL._4096)
.withKeyFlags(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)
.withStandardConfiguration())
.done()
.withCertificationKeyType(
KeySpec.getBuilder()
.ofType(DSA._3072)
.withKeyFlags(KeyFlag.SIGN_DATA)
.withStandardConfiguration())
.withPrimaryUserId("Test123")
.done()
.withoutPassphrase()
.build();
byte[] base64 = Base64.getEncoder().encode(secretKeys.getEncoded());
System.out.println(new String(base64));
}
}

View File

@ -0,0 +1,10 @@
package de.vanitasvitae.crypto.pgpainless;
import de.vanitasvitae.crypto.pgpainless.key.generation.KeyRingBuilder;
public class PGPainless {
public static KeyRingBuilder generateKeyRing() {
return new KeyRingBuilder();
}
}

View File

@ -0,0 +1,8 @@
package de.vanitasvitae.crypto.pgpainless;
import java.util.Set;
public class PainlessResult {
Set<Long> signingKeys;
Long decryptingKey;
}

View File

@ -0,0 +1,93 @@
package de.vanitasvitae.crypto.pgpainless.key.algorithm;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import com.sun.istack.internal.NotNull;
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,
HashAlgorithm.SHA1),
Arrays.asList(
CompressionAlgorithm.ZLIB,
CompressionAlgorithm.BZIP2,
CompressionAlgorithm.ZIP,
CompressionAlgorithm.UNCOMPRESSED)
);
private List<SymmetricKeyAlgorithm> symmetricKeyAlgorithms;
private List<HashAlgorithm> hashAlgorithms;
private List<CompressionAlgorithm> compressionAlgorithms;
public AlgorithmSuite(@NotNull List<SymmetricKeyAlgorithm> symmetricKeyAlgorithms,
@NotNull List<HashAlgorithm> hashAlgorithms,
@NotNull List<CompressionAlgorithm> compressionAlgorithms) {
this.symmetricKeyAlgorithms = Collections.unmodifiableList(symmetricKeyAlgorithms);
this.hashAlgorithms = Collections.unmodifiableList(hashAlgorithms);
this.compressionAlgorithms = Collections.unmodifiableList(compressionAlgorithms);
}
public void setSymmetricKeyAlgorithms(@NotNull 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(@NotNull 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(@NotNull 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,37 @@
package de.vanitasvitae.crypto.pgpainless.key.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,33 @@
package de.vanitasvitae.crypto.pgpainless.key.algorithm;
import java.util.HashMap;
import java.util.Map;
import org.bouncycastle.bcpg.sig.Features;
public enum Feature {
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,44 @@
package de.vanitasvitae.crypto.pgpainless.key.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,25 @@
package de.vanitasvitae.crypto.pgpainless.key.algorithm;
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;
}
}

View File

@ -0,0 +1,46 @@
package de.vanitasvitae.crypto.pgpainless.key.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),
/**
* @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,47 @@
package de.vanitasvitae.crypto.pgpainless.key.algorithm;
import java.util.HashMap;
import java.util.Map;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
public enum SymmetricKeyAlgorithm {
NULL( SymmetricKeyAlgorithmTags.NULL),
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 forId(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,213 @@
package de.vanitasvitae.crypto.pgpainless.key.generation;
import java.nio.charset.Charset;
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 de.vanitasvitae.crypto.pgpainless.key.algorithm.KeyFlag;
import de.vanitasvitae.crypto.pgpainless.key.generation.type.KeyType;
import org.bouncycastle.bcpg.HashAlgorithmTags;
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.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
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;
public class KeyRingBuilder implements KeyRingBuilderInterface {
private final Charset UTF8 = Charset.forName("UTF-8");
private List<KeySpec> keySpecs = new ArrayList<>();
private List<String> userIds = new ArrayList<>();
private char[] passphrase;
@Override
public WithSubKeyType generateCompositeKeyRing() {
return new WithSubKeyTypeImpl();
}
@Override
public WithCertificationKeyType generateSingleKeyKeyRing() {
return new WithCertificationKeyTypeImpl();
}
class WithSubKeyTypeImpl implements WithSubKeyType {
@Override
public WithSubKeyType withSubKey(KeySpec type) {
KeyRingBuilder.this.keySpecs.add(type);
return this;
}
@Override
public WithCertificationKeyType done() {
return new WithCertificationKeyTypeImpl();
}
}
class WithCertificationKeyTypeImpl implements WithCertificationKeyType {
@Override
public WithPrimaryUserId withCertificationKeyType(KeySpec spec) {
if ((spec.getKeyFlags() & KeyFlag.CERTIFY_OTHER.getFlag()) == 0) {
throw new IllegalArgumentException("Certification Key MUST have KeyFlag CERTIFY_OTHER");
}
KeyRingBuilder.this.keySpecs.add(spec);
return new WithPrimaryUserIdImpl();
}
}
class WithPrimaryUserIdImpl implements WithPrimaryUserId {
@Override
public WithAdditionalUserIds withPrimaryUserId(String userId) {
KeyRingBuilder.this.userIds.add(userId);
return new WithAdditionalUserIdsImpl();
}
@Override
public WithAdditionalUserIds withPrimaryUserId(byte[] userId) {
return withPrimaryUserId(new String(userId, UTF8));
}
}
class WithAdditionalUserIdsImpl implements WithAdditionalUserIds {
@Deprecated
@Override
public WithAdditionalUserIds withAdditionalUserId(String userId) {
KeyRingBuilder.this.userIds.add(userId);
return this;
}
@Deprecated
@Override
public WithAdditionalUserIds withAdditionalUserId(byte[] userId) {
return withAdditionalUserId(new String(userId, UTF8));
}
@Override
public WithPassphrase done() {
return new WithPassphraseImpl();
}
}
class WithPassphraseImpl implements WithPassphrase {
@Override
public Build withPassphrase(String passphrase) {
return withPassphrase(passphrase.toCharArray());
}
@Override
public Build withPassphrase(char[] 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 PGPSecretKeyRing build() throws NoSuchAlgorithmException, PGPException, NoSuchProviderException {
// Hash Calculator
PGPDigestCalculator calculator = new JcaPGPDigestCalculatorProviderBuilder()
.setProvider(BouncyCastleProvider.PROVIDER_NAME)
.build()
.get(HashAlgorithmTags.SHA1);
// 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);
// First key is the Master Key
KeySpec certKeySpec = keySpecs.get(0);
KeyType certKeyType = certKeySpec.getKeyType();
keySpecs.remove(0); // Remove master key, so that we later only add sub keys.
// Generate Master Key
PGPKeyPair certKey = generateKeyPair(certKeySpec);
// Signer for creating self-signature
PGPContentSignerBuilder signer = new JcaPGPContentSignerBuilder(
certKey.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA256);
// Mimic GnuPGs signature sub packets
PGPSignatureSubpacketGenerator hashedSubPackets = new PGPSignatureSubpacketGenerator();
// Key flags
hashedSubPackets.setKeyFlags(true, certKeySpec.getKeyFlags());
// Encryption Algorithms
hashedSubPackets.setPreferredSymmetricAlgorithms(true,
certKeySpec.getPreferredAlgorithms().getSymmetricKeyAlgorithmIds());
// Hash Algorithms
hashedSubPackets.setPreferredHashAlgorithms(true,
certKeySpec.getPreferredAlgorithms().getHashAlgorithmIds());
// Compression Algorithms
hashedSubPackets.setPreferredCompressionAlgorithms(true,
certKeySpec.getPreferredAlgorithms().getCompressionAlgorithmIds());
// Modification Detection
hashedSubPackets.setFeature(true, certKeySpec.getFeatures());
// Generator which the user can get the key pair from
PGPKeyRingGenerator ringGenerator = new PGPKeyRingGenerator(
PGPSignature.POSITIVE_CERTIFICATION, certKey,
userIds.get(0), calculator,
hashedSubPackets.generate(), null, signer, encryptor);
for (KeySpec subKeySpec : keySpecs) {
PGPKeyPair subKey = generateKeyPair(subKeySpec);
ringGenerator.addSubKey(subKey);
}
return ringGenerator.generateSecretKeyRing();
}
private PGPKeyPair generateKeyPair(KeySpec spec)
throws NoSuchProviderException, NoSuchAlgorithmException, PGPException {
KeyType type = spec.getKeyType();
KeyPairGenerator certKeyGenerator = KeyPairGenerator.getInstance(
type.getName(), BouncyCastleProvider.PROVIDER_NAME);
certKeyGenerator.initialize(type.getLength());
// Create raw Key Pair
KeyPair rawKeyPair = certKeyGenerator.generateKeyPair();
// Form PGP key pair
PGPKeyPair pgpKeyPair = new JcaPGPKeyPair(type.getAlgorithm().getAlgorithmId(),
rawKeyPair, new Date());
return pgpKeyPair;
}
}
}
}

View File

@ -0,0 +1,58 @@
package de.vanitasvitae.crypto.pgpainless.key.generation;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
public interface KeyRingBuilderInterface {
WithSubKeyType generateCompositeKeyRing();
WithCertificationKeyType generateSingleKeyKeyRing();
interface WithSubKeyType {
WithSubKeyType withSubKey(KeySpec keySpec);
WithCertificationKeyType done();
}
interface WithCertificationKeyType {
WithPrimaryUserId withCertificationKeyType(KeySpec keySpec);
}
interface WithPrimaryUserId {
WithAdditionalUserIds withPrimaryUserId(String userId);
WithAdditionalUserIds withPrimaryUserId(byte[] userId);
}
interface WithAdditionalUserIds {
WithAdditionalUserIds withAdditionalUserId(String userId);
WithAdditionalUserIds withAdditionalUserId(byte[] userId);
WithPassphrase done();
}
interface WithPassphrase {
Build withPassphrase(String passphrase);
Build withPassphrase(char[] passphrase);
Build withoutPassphrase();
}
interface Build {
PGPSecretKeyRing build() throws NoSuchAlgorithmException, PGPException, NoSuchProviderException;
}
}

View File

@ -0,0 +1,49 @@
package de.vanitasvitae.crypto.pgpainless.key.generation;
import java.util.Set;
import de.vanitasvitae.crypto.pgpainless.key.algorithm.AlgorithmSuite;
import de.vanitasvitae.crypto.pgpainless.key.algorithm.Feature;
import de.vanitasvitae.crypto.pgpainless.key.generation.type.KeyType;
public class KeySpec {
private final KeyType keyType;
private final int keyFlags;
private final AlgorithmSuite algorithmSuite;
private final Set<Feature> features;
KeySpec(KeyType type,
int keyFlags,
AlgorithmSuite preferredAlgorithms,
Set<Feature> features) {
this.keyType = type;
this.keyFlags = keyFlags;
this.algorithmSuite = preferredAlgorithms;
this.features = features;
}
KeyType getKeyType() {
return keyType;
}
int getKeyFlags() {
return keyFlags;
}
AlgorithmSuite getPreferredAlgorithms() {
return algorithmSuite;
}
byte getFeatures() {
byte val = 0;
for (Feature f : features) {
val |= f.getFeatureId();
}
return val;
}
public static KeySpecBuilder getBuilder() {
return new KeySpecBuilder();
}
}

View File

@ -0,0 +1,140 @@
package de.vanitasvitae.crypto.pgpainless.key.generation;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import de.vanitasvitae.crypto.pgpainless.key.algorithm.AlgorithmSuite;
import de.vanitasvitae.crypto.pgpainless.key.algorithm.CompressionAlgorithm;
import de.vanitasvitae.crypto.pgpainless.key.algorithm.Feature;
import de.vanitasvitae.crypto.pgpainless.key.algorithm.HashAlgorithm;
import de.vanitasvitae.crypto.pgpainless.key.algorithm.KeyFlag;
import de.vanitasvitae.crypto.pgpainless.key.algorithm.SymmetricKeyAlgorithm;
import de.vanitasvitae.crypto.pgpainless.key.generation.type.KeyType;
public class KeySpecBuilder implements KeySpecBuilderInterface {
private KeyType type;
private int keyFlags;
private AlgorithmSuite algorithmSuite = AlgorithmSuite.getDefaultAlgorithmSuite();
private Set<Feature> features = new HashSet<>();
@Override
public WithKeyFlags ofType(KeyType type) {
KeySpecBuilder.this.type = type;
return new WithKeyFlagsImpl();
}
class WithKeyFlagsImpl implements WithKeyFlags {
@Override
public WithDetailedConfiguration withKeyFlags(KeyFlag... flags) {
int val = 0;
for (KeyFlag f : flags) {
val |= f.getFlag();
}
KeySpecBuilder.this.keyFlags = val;
return new WithDetailedConfigurationImpl();
}
@Override
public WithDetailedConfiguration withDefaultKeyFlags() {
return withKeyFlags(
KeyFlag.CERTIFY_OTHER,
KeyFlag.SIGN_DATA,
KeyFlag.ENCRYPT_COMMS,
KeyFlag.ENCRYPT_STORAGE,
KeyFlag.AUTHENTICATION);
}
}
class WithDetailedConfigurationImpl implements WithDetailedConfiguration {
@Deprecated
@Override
public WithPreferredSymmetricAlgorithms withDetailedConfiguration() {
return new WithPreferredSymmetricAlgorithmsImpl();
}
@Override
public KeySpec withStandardConfiguration() {
return new KeySpec(
KeySpecBuilder.this.type,
KeySpecBuilder.this.keyFlags,
KeySpecBuilder.this.algorithmSuite,
KeySpecBuilder.this.features);
}
}
class WithPreferredSymmetricAlgorithmsImpl implements WithPreferredSymmetricAlgorithms {
@Override
public WithPreferredHashAlgorithms withPreferredSymmetricAlgorithms(SymmetricKeyAlgorithm... algorithms) {
KeySpecBuilder.this.algorithmSuite.setSymmetricKeyAlgorithms(Arrays.asList(algorithms));
return new WithPreferredHashAlgorithmsImpl();
}
@Override
public WithPreferredHashAlgorithms withDefaultSymmetricAlgorithms() {
KeySpecBuilder.this.algorithmSuite.setSymmetricKeyAlgorithms(
AlgorithmSuite.getDefaultAlgorithmSuite().getSymmetricKeyAlgorithms());
return new WithPreferredHashAlgorithmsImpl();
}
@Override
public WithFeatures withDefaultAlgorithms() {
KeySpecBuilder.this.algorithmSuite = AlgorithmSuite.getDefaultAlgorithmSuite();
return new WithFeaturesImpl();
}
}
class WithPreferredHashAlgorithmsImpl implements WithPreferredHashAlgorithms {
@Override
public WithPreferredCompressionAlgorithms withPreferredHashAlgorithms(HashAlgorithm... algorithms) {
KeySpecBuilder.this.algorithmSuite.setHashAlgorithms(Arrays.asList(algorithms));
return new WithPreferredCompressionAlgorithmsImpl();
}
@Override
public WithPreferredCompressionAlgorithms withDefaultHashAlgorithms() {
KeySpecBuilder.this.algorithmSuite.setHashAlgorithms(
AlgorithmSuite.getDefaultAlgorithmSuite().getHashAlgorithms());
return new WithPreferredCompressionAlgorithmsImpl();
}
}
class WithPreferredCompressionAlgorithmsImpl implements WithPreferredCompressionAlgorithms {
@Override
public WithFeatures withPreferredCompressionAlgorithms(CompressionAlgorithm... algorithms) {
KeySpecBuilder.this.algorithmSuite.setCompressionAlgorithms(Arrays.asList(algorithms));
return new WithFeaturesImpl();
}
@Override
public WithFeatures withDefaultCompressionAlgorithms() {
KeySpecBuilder.this.algorithmSuite.setCompressionAlgorithms(
AlgorithmSuite.getDefaultAlgorithmSuite().getCompressionAlgorithms());
return new WithFeaturesImpl();
}
}
class WithFeaturesImpl implements WithFeatures {
@Override
public WithFeatures withFeature(Feature feature) {
KeySpecBuilder.this.features.add(feature);
return this;
}
@Override
public KeySpec done() {
return new KeySpec(
KeySpecBuilder.this.type,
KeySpecBuilder.this.keyFlags,
KeySpecBuilder.this.algorithmSuite,
KeySpecBuilder.this.features);
}
}
}

View File

@ -0,0 +1,62 @@
package de.vanitasvitae.crypto.pgpainless.key.generation;
import de.vanitasvitae.crypto.pgpainless.key.algorithm.AlgorithmSuite;
import de.vanitasvitae.crypto.pgpainless.key.algorithm.CompressionAlgorithm;
import de.vanitasvitae.crypto.pgpainless.key.algorithm.Feature;
import de.vanitasvitae.crypto.pgpainless.key.algorithm.HashAlgorithm;
import de.vanitasvitae.crypto.pgpainless.key.algorithm.KeyFlag;
import de.vanitasvitae.crypto.pgpainless.key.algorithm.SymmetricKeyAlgorithm;
import de.vanitasvitae.crypto.pgpainless.key.generation.type.KeyType;
public interface KeySpecBuilderInterface {
WithKeyFlags ofType(KeyType type);
interface WithKeyFlags {
WithDetailedConfiguration withKeyFlags(KeyFlag... flags);
WithDetailedConfiguration withDefaultKeyFlags();
}
interface WithDetailedConfiguration {
WithPreferredSymmetricAlgorithms withDetailedConfiguration();
KeySpec withStandardConfiguration();
}
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,36 @@
package de.vanitasvitae.crypto.pgpainless.key.generation.type;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import de.vanitasvitae.crypto.pgpainless.key.algorithm.PublicKeyAlgorithm;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public enum DSA implements KeyType {
_1024(1024),
_2048(2048),
_3072(3072),
;
private final int length;
DSA(int length) {
this.length = length;
}
@Override
public int getLength() {
return length;
}
@Override
public String getName() {
return "DSA";
}
@Override
public PublicKeyAlgorithm getAlgorithm() {
return PublicKeyAlgorithm.DSA;
}
}

View File

@ -0,0 +1,32 @@
package de.vanitasvitae.crypto.pgpainless.key.generation.type;
import de.vanitasvitae.crypto.pgpainless.key.algorithm.PublicKeyAlgorithm;
public enum ElGamal_ENCRYPT implements KeyType {
_1024(1024),
_2048(2048),
_3072(3072),
;
private final int length;
ElGamal_ENCRYPT(int length) {
this.length = length;
}
@Override
public int getLength() {
return length;
}
@Override
public String getName() {
return "ElGamal";
}
@Override
public PublicKeyAlgorithm getAlgorithm() {
return PublicKeyAlgorithm.ELGAMAL_ENCRYPT;
}
}

View File

@ -0,0 +1,12 @@
package de.vanitasvitae.crypto.pgpainless.key.generation.type;
import de.vanitasvitae.crypto.pgpainless.key.algorithm.PublicKeyAlgorithm;
public interface KeyType {
int getLength();
String getName();
PublicKeyAlgorithm getAlgorithm();
}

View File

@ -0,0 +1,36 @@
package de.vanitasvitae.crypto.pgpainless.key.generation.type;
import de.vanitasvitae.crypto.pgpainless.key.algorithm.PublicKeyAlgorithm;
public enum RSA_GENERAL implements KeyType {
@Deprecated
_1024(1024),
@Deprecated
_2048(2048),
_3072(3072),
_4096(4096),
_8192(8192),
;
private final int length;
RSA_GENERAL(int length) {
this.length = length;
}
@Override
public int getLength() {
return length;
}
@Override
public String getName() {
return "RSA";
}
@Override
public PublicKeyAlgorithm getAlgorithm() {
return PublicKeyAlgorithm.RSA_GENERAL;
}
}