Skip to content

Commit

Permalink
Allow maven encryption for keyPass
Browse files Browse the repository at this point in the history
fix: #33
  • Loading branch information
slawekjaranowski authored and mkarg committed Jan 22, 2021
1 parent 58fb1a7 commit 7822e3f
Show file tree
Hide file tree
Showing 15 changed files with 182 additions and 9 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@
Creates OpenPGP signatures for all of the project's artifacts
without any external software.

This plugin can replace **maven-gpg-plugin** in easy way and give you new feature.

# Feature

- all the signing operations are done using `Bouncy Castle`
- support Maven `3.6` and is ready for next version `3.7/4.0` of Maven with `Consumer POM`
- support `subkey` for signing
- easy to use on CI system, configuration can be provided by environment variables
- easy to use on CI system, configuration can be provided by environment variables
- key passphrase can be encrypted by standard Maven [Password Encryption](https://maven.apache.org/guides/mini/guide-encryption.html)

# Key prepare

Expand Down
20 changes: 20 additions & 0 deletions src/it/invalid-encrypted-password/invoker.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#
# Copyright 2021 Slawomir Jaranowski
#
# 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.
#
invoker.goals = install --no-transfer-progress -Drevision=1.1.1 -Dsettings.security=${project.basedir}/src/it/settings-security.xml
invoker.mavenOpts = ${argLine}
invoker.buildResult = failure

invoker.ordinal = 100
52 changes: 52 additions & 0 deletions src/it/invalid-encrypted-password/pom-test.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?xml version='1.0' encoding='UTF-8'?>

<!--
~ Copyright 2021 Slawomir Jaranowski
~
~ 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.
-->

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>test-group</groupId>
<artifactId>invalid-encrypted-password</artifactId>
<version>${revision}</version>
<packaging>pom</packaging>

<build>
<plugins>

<plugin>
<groupId>org.simplify4u.plugins</groupId>
<artifactId>sign-maven-plugin</artifactId>
<version>@project.version@</version>
<executions>

<execution>
<goals>
<goal>sign</goal>
</goals>
<configuration>
<keyId>AC71B3E31C0C0D38</keyId>
<keyPass>{Kii9qpeY09YHqthhLmmtWA7q2KWCABsqXMrPrWVsY6w=}</keyPass>
<keyFile>@project.basedir@/src/test/resources/priv-key.asc</keyFile>
</configuration>
</execution>

</executions>
</plugin>
</plugins>
</build>
</project>
22 changes: 22 additions & 0 deletions src/it/invalid-encrypted-password/postbuild.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2021 Slawomir Jaranowski
*
* 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.
*/

def buildLog = new File( basedir, 'build.log' ).text

buildLog.contains('SignMojoException: Invalid encrypted password')

def target = new File( basedir, "target" )
assert !target.exists()
2 changes: 1 addition & 1 deletion src/it/pom-packaging/invoker.properties
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
invoker.goals = install --no-transfer-progress -Drevision=1.1.1
invoker.goals = install --no-transfer-progress -Drevision=1.1.1 -Dsettings.security=${project.basedir}/src/it/settings-security.xml
invoker.mavenOpts = ${argLine}

invoker.ordinal = 100
2 changes: 1 addition & 1 deletion src/it/pom-packaging/pom-test.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
</goals>
<configuration>
<keyId>AC71B3E31C0C0D38</keyId>
<keyPass>testPass</keyPass>
<keyPass>{p01Vm25gEoIH/wDlR3hWNhOH5mn4PBEnSFEtH96k1Yw=}</keyPass>
<keyFile>@project.basedir@/src/test/resources/priv-key.asc</keyFile>
</configuration>
</execution>
Expand Down
18 changes: 18 additions & 0 deletions src/it/settings-security.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!--
~ Copyright 2021 Slawomir Jaranowski
~
~ 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.
-->
<settingsSecurity>
<master>{C2S/FkUJ51EHeGSVvXXp+thgf/SzyfoN5naopubT/ZU=}</master>
</settingsSecurity>
3 changes: 3 additions & 0 deletions src/main/java/org/simplify4u/plugins/sign/ArtifactSigner.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public abstract class ArtifactSigner {

/**
* Check if artifact has correct data.
*
* @param artifact an artifact to sign
*/
protected static void verifyArtifact(Artifact artifact) {
Expand All @@ -67,6 +68,7 @@ protected static void verifyArtifact(Artifact artifact) {
* @param inputStream data to sign
* @param artifactId used for build filename
* @param classifier used for build filename
* @param version used for build filename
* @param extension used for build filename
*
* @return result of signing
Expand All @@ -93,6 +95,7 @@ protected SignResult makeSignature(InputStream inputStream,

/**
* Setup signarer with key configuration
*
* @param pgpKeyInfo a private key configuration
*/
public void setKeyInfo(PGPKeyInfo pgpKeyInfo) {
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/org/simplify4u/plugins/sign/SignMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.Set;
import javax.inject.Inject;

import io.vavr.control.Try;
import lombok.AccessLevel;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -34,6 +35,7 @@
import org.apache.maven.project.artifact.ProjectArtifact;
import org.simplify4u.plugins.sign.openpgp.PGPKeyInfo;
import org.simplify4u.plugins.sign.openpgp.PGPSignerKeyNotFoundException;
import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;

/**
* Creates OpenPGP signatures for all of the project's artifacts.
Expand All @@ -54,6 +56,9 @@ public class SignMojo extends AbstractMojo {
@Inject
private ArtifactSignerFactory artifactSignerFactory;

@Inject
private SecDispatcher secDispatcher;

/**
* <p><code>keyId</code> used for signing. If not provided first key from <code>keyFile</code> will be taken.</p>
*
Expand All @@ -67,12 +72,16 @@ public class SignMojo extends AbstractMojo {
/**
* <p><code>passphrase</code> to decrypt private signing key.</p>
*
* <p>Can be encrypted by standard Maven
* <a href="https://maven.apache.org/guides/mini/guide-encryption.html">Password Encryption</a></p>
*
* <p>Provided key can be stored in plain text, in this case <code>keyPass</code> can be empty.</p>
*
* <p>This value can be delivered by environment variable <code>SIGN_KEY_PASS</code>.</p>
*
* @since 0.1.0
*/
@Setter(AccessLevel.PACKAGE)
@Parameter(property = "sign.keyPass")
private String keyPass;

Expand Down Expand Up @@ -125,6 +134,7 @@ public void execute() {
PGPKeyInfo keyInfo;
try {
keyInfo = PGPKeyInfo.builder()
.passDecryptor(this::decryptPass)
.keyId(keyId)
.keyPass(keyPass)
.keyFile(keyFile)
Expand Down Expand Up @@ -154,6 +164,11 @@ public void execute() {
.forEach(this::attachSignResult);
}

private String decryptPass(String pass) {
return Try.of(() -> secDispatcher.decrypt(pass))
.getOrElseThrow(e -> new SignMojoException("Invalid encrypted password", e));
}

/**
* Attache sign result to project.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,8 @@ public class SignMojoException extends RuntimeException {
SignMojoException(String message) {
super(message);
}

SignMojoException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Optional;
import java.util.function.UnaryOperator;

import io.vavr.control.Try;
import lombok.AccessLevel;
Expand All @@ -48,13 +49,14 @@ public class PGPKeyInfo {
InputStream key;

@Builder
private PGPKeyInfo(String keyId, String keyPass, File keyFile) {
private PGPKeyInfo(String keyId, String keyPass, File keyFile, UnaryOperator<String> passDecryptor) {

id = Optional.ofNullable(stringFromEnv(SIGN_KEY_ID_ENV).orElse(keyId))
.map(PGPKeyInfo::parseKeyId)
.orElse(null);

pass = Optional.ofNullable(stringFromEnv(SIGN_KEY_PASS_ENV).orElse(keyPass))
.map(Optional.ofNullable(passDecryptor).orElseGet(UnaryOperator::identity))
.map(String::toCharArray)
.orElse(null);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;

/**
* Signig data by PGP.
* Signing data by PGP.
*
* @author Slawomir Jaranowski
*/
Expand Down
6 changes: 5 additions & 1 deletion src/site/markdown/index.md.vm
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ Introduction
`Sign Maven Plugin` allows you to creates OpenPGP signatures for all of the project's artifacts
without any external software.

# Feature
This plugin can replace **maven-gpg-plugin** in easy way and give you new feature.

Feature
-------

- all the signing operations are done using `Bouncy Castle`
- support Maven `3.6` and is ready for next version `3.7/4.0` of Maven with `Consumer POM`
- support `subkey` for signing
- easy to use on CI system, configuration can be provided by environment variables
- key passphrase can be encrypted by standard Maven [Password Encryption](https://maven.apache.org/guides/mini/guide-encryption.html)

22 changes: 19 additions & 3 deletions src/test/java/org/simplify4u/plugins/sign/SignMojoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.simplify4u.plugins.sign.openpgp.PGPSignerKeyNotFoundException;
import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;

@ExtendWith(MockitoExtension.class)
class SignMojoTest {
Expand All @@ -47,6 +48,9 @@ class SignMojoTest {
@Mock
private ArtifactSignerFactory artifactSignerFactory;

@Mock
private SecDispatcher secDispatcher;

@InjectMocks
private SignMojo mojo;

Expand All @@ -60,7 +64,7 @@ void skipExecution() {
mojo.execute();

// then
verifyNoInteractions(artifactSignerFactory, artifactSigner, project);
verifyNoInteractions(artifactSignerFactory, artifactSigner, secDispatcher, project);
}

@Test
Expand All @@ -73,7 +77,7 @@ void noExistingKeyShouldSkipExecution() {
mojo.execute();

//then
verifyNoInteractions(artifactSignerFactory, artifactSigner, project);
verifyNoInteractions(artifactSignerFactory, artifactSigner, secDispatcher, project);
}

@Test
Expand All @@ -86,7 +90,8 @@ void noExistingKeyShouldBreakExecution() {
assertThatThrownBy(() -> mojo.execute())
.isExactlyInstanceOf(PGPSignerKeyNotFoundException.class)
.hasMessage("key file: no-existing-key.asc not found");
verifyNoInteractions(artifactSignerFactory, artifactSigner, project);

verifyNoInteractions(artifactSignerFactory, artifactSigner, secDispatcher, project);
}

@Nested
Expand All @@ -113,6 +118,17 @@ void executeWithOutParams() {
mojo.execute();

verify(artifactSigner).signArtifact(any());
verifyNoInteractions(secDispatcher);
}

@Test
void executeWithPassword() throws Exception {

mojo.setKeyPass("keyPass");
mojo.execute();

verify(artifactSigner).signArtifact(any());
verify(secDispatcher).decrypt("keyPass");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,18 @@ void nullStringInEnvironmentValueShouldBeFiltered() {
assertThat(keyInfo.getPass()).isNull();
assertThat(keyInfo.getKey()).hasContent("signKey from environment");
}

@Test
void passDecryptorShouldBeCalled() {

// when
PGPKeyInfo keyInfo = PGPKeyInfo.builder()
.passDecryptor(String::toUpperCase)
.keyPass(KEY_PASS_STR)
.keyFile(KEY_FILE)
.build();

// then
assertThat(keyInfo.getPass()).isEqualTo(KEY_PASS_STR.toUpperCase().toCharArray());
}
}

0 comments on commit 7822e3f

Please sign in to comment.