Browse Source

Initial commit

tags/0.0.1-alpha1
Paul Schaub 1 year ago
commit
6dd2e83acf
Signed by: Paul Schaub <vanitasvitae@fsfe.org> GPG Key ID: 62BEE9264BF17311
27 changed files with 1325 additions and 0 deletions
  1. 14
    0
      .gitignore
  2. 18
    0
      build.gradle
  3. BIN
      gradle/wrapper/gradle-wrapper.jar
  4. 5
    0
      gradle/wrapper/gradle-wrapper.properties
  5. 172
    0
      gradlew
  6. 84
    0
      gradlew.bat
  7. 2
    0
      settings.gradle
  8. 5
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/EncryptionBuilder.java
  9. 44
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/Main.java
  10. 10
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/PGPainless.java
  11. 8
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/PainlessResult.java
  12. 93
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/key/algorithm/AlgorithmSuite.java
  13. 37
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/key/algorithm/CompressionAlgorithm.java
  14. 33
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/key/algorithm/Feature.java
  15. 44
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/key/algorithm/HashAlgorithm.java
  16. 25
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/key/algorithm/KeyFlag.java
  17. 46
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/key/algorithm/PublicKeyAlgorithm.java
  18. 47
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/key/algorithm/SymmetricKeyAlgorithm.java
  19. 213
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/KeyRingBuilder.java
  20. 58
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/KeyRingBuilderInterface.java
  21. 49
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/KeySpec.java
  22. 140
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/KeySpecBuilder.java
  23. 62
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/KeySpecBuilderInterface.java
  24. 36
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/type/DSA.java
  25. 32
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/type/ElGamal_ENCRYPT.java
  26. 12
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/type/KeyType.java
  27. 36
    0
      src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/type/RSA_GENERAL.java

+ 14
- 0
.gitignore View File

@@ -0,0 +1,14 @@
.idea
.gradle

out/

*.iws
*.iml
*.ipr
*.class
*.log
*.jar


!gradle-wrapper.jar

+ 18
- 0
build.gradle 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 View File


+ 5
- 0
gradle/wrapper/gradle-wrapper.properties 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
- 0
gradlew 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
- 0
gradlew.bat 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
- 0
settings.gradle View File

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


+ 5
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/EncryptionBuilder.java View File

@@ -0,0 +1,5 @@
package de.vanitasvitae.crypto.pgpainless;

public class EncryptionBuilder {

}

+ 44
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/Main.java 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));
}
}

+ 10
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/PGPainless.java 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();
}
}

+ 8
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/PainlessResult.java View File

@@ -0,0 +1,8 @@
package de.vanitasvitae.crypto.pgpainless;

import java.util.Set;

public class PainlessResult {
Set<Long> signingKeys;
Long decryptingKey;
}

+ 93
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/key/algorithm/AlgorithmSuite.java 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;
}
}

+ 37
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/key/algorithm/CompressionAlgorithm.java 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;
}
}

+ 33
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/key/algorithm/Feature.java 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;
}
}

+ 44
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/key/algorithm/HashAlgorithm.java 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;
}
}

+ 25
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/key/algorithm/KeyFlag.java 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;
}
}

+ 46
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/key/algorithm/PublicKeyAlgorithm.java 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;
}
}

+ 47
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/key/algorithm/SymmetricKeyAlgorithm.java 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;
}
}

+ 213
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/KeyRingBuilder.java 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;
}
}
}
}

+ 58
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/KeyRingBuilderInterface.java 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;

}
}

+ 49
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/KeySpec.java 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();
}
}

+ 140
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/KeySpecBuilder.java 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);
}
}
}

+ 62
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/KeySpecBuilderInterface.java 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();
}

}

+ 36
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/type/DSA.java 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;
}
}

+ 32
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/type/ElGamal_ENCRYPT.java 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;
}
}

+ 12
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/type/KeyType.java 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();
}

+ 36
- 0
src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/type/RSA_GENERAL.java 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;
}
}

Loading…
Cancel
Save