Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,8 @@

<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk18on</artifactId>
<groupId>org.pgpainless</groupId>
<artifactId>pgpainless-core</artifactId>
</dependency>
</dependencies>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
import org.bouncycastle.openpgp.bc.BcPGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
import org.pgpainless.PGPainless;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.key.util.KeyIdUtil;
import org.pgpainless.util.Passphrase;

public final class PgpHelper {
private PgpHelper() {
Expand Down Expand Up @@ -133,4 +137,23 @@ public static PGPSecretKey loadSecretKey(final InputStream input, final String k

return null;
}

public static PGPSecretKeyRing loadSecretKeyRing(InputStream inputStream)
throws IOException {
return PGPainless.readKeyRing().secretKeyRing(inputStream);
}

public static long parseKeyId(String keyId) {
if (keyId == null) {
return 0L;
}
return KeyIdUtil.fromLongKeyId(keyId);
}

public static SecretKeyRingProtector protectorFromPassword(String password) {
if (password == null || password.isEmpty()) {
return SecretKeyRingProtector.unprotectedKeys();
}
return SecretKeyRingProtector.unlockAnyKeyWith(Passphrase.fromPassword(password));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@
import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;

/**
* Signing Stream.
*
* @deprecated use {@link SigningStream2} instead.
*/
@Deprecated
public class SigningStream extends OutputStream {
private final OutputStream stream;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/*
* Copyright (c) 2015, 2019 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/

package org.eclipse.packager.security.pgp;

import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Objects;

import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.DocumentSignatureType;
import org.pgpainless.encryption_signing.EncryptionResult;
import org.pgpainless.encryption_signing.EncryptionStream;
import org.pgpainless.encryption_signing.ProducerOptions;
import org.pgpainless.encryption_signing.SigningOptions;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.util.ArmoredOutputStreamFactory;

public class SigningStream2 extends OutputStream {
private final OutputStream stream;

private final PGPSecretKeyRing secretKeys;

private final SecretKeyRingProtector protector;

private final long keyId;

private final boolean inline;

private boolean initialized;

private EncryptionStream signingStream;

/**
* Create a new signing stream
*
* @param stream the actual output stream
* @param secretKeys the signing key ring
* @param protector protector to unlock the signing key
* @param inline whether to sign inline or just write the signature
* @param version the optional version which will be in the signature comment
*/
public SigningStream2(final OutputStream stream,
final PGPSecretKeyRing secretKeys,
final SecretKeyRingProtector protector,
final long keyId,
final boolean inline,
final String version) {
this.stream = stream;
this.secretKeys = secretKeys;
this.protector = protector;
this.keyId = keyId;
this.inline = inline;

ArmoredOutputStreamFactory.setVersionInfo(version);
}

/**
* Create a new signing stream
*
* @param stream the actual output stream
* @param secretKeys the signing key ring
* @param protector protector to unlock the signing key
* @param inline whether to sign inline or just write the signature
*/
public SigningStream2(final OutputStream stream,
final PGPSecretKeyRing secretKeys,
final SecretKeyRingProtector protector,
final long keyId,
final boolean inline) {
this(stream, secretKeys, protector, keyId, inline, null);
}

protected void testInit() throws IOException {
if (this.initialized) {
return;
}

this.initialized = true;

try {
long signingKeyId = keyId;
if (signingKeyId == 0) {
List<PGPPublicKey> signingKeys = PGPainless.inspectKeyRing(secretKeys).getSigningSubkeys();
if (signingKeys.isEmpty()) {
throw new PGPException("No available signing subkey found.");
}
// Sign with first signing subkey found
signingKeyId = signingKeys.get(0).getKeyID();
}

if (inline) {

SigningOptions signingOptions = SigningOptions.get();
if (keyId != 0) {
signingOptions.addInlineSignature(protector, secretKeys, signingKeyId);
} else {
signingOptions.addInlineSignature(protector, secretKeys, DocumentSignatureType.BINARY_DOCUMENT);
}
ProducerOptions producerOptions = ProducerOptions.sign(signingOptions)
.setCleartextSigned();

signingStream = PGPainless.encryptAndOrSign()
.onOutputStream(stream) // write data and sig to the output stream
.withOptions(producerOptions);

} else {

SigningOptions signingOptions = SigningOptions.get();
if (keyId != 0) {
signingOptions.addDetachedSignature(protector, secretKeys, keyId);
} else {
signingOptions.addDetachedSignature(protector, secretKeys, DocumentSignatureType.BINARY_DOCUMENT);
}
ProducerOptions producerOptions = ProducerOptions.sign(signingOptions);

signingStream = PGPainless.encryptAndOrSign()
.onOutputStream(
// do not output the plaintext data, just emit the signature in close()
new OutputStream() {
@Override
public void write(int i) throws IOException {
// Ignore data
}
})
.withOptions(producerOptions);
}
} catch (final PGPException e) {
throw new IOException(e);
}
}

@Override
public void write(final int b) throws IOException {
write(new byte[] { (byte) b });
}

@Override
public void write(final byte[] b, final int off, final int len) throws IOException {
Objects.requireNonNull(b);

testInit();

signingStream.write(b, off, len);
}

@Override
public void close() throws IOException {
testInit();

signingStream.close();

if (this.inline) {
return;
}

EncryptionResult result = signingStream.getResult();
final PGPSignature signature = result.getDetachedSignatures().flatten().iterator().next();
ArmoredOutputStream armoredOutput = ArmoredOutputStreamFactory.get(stream);
signature.encode(new BCPGOutputStream(armoredOutput));
armoredOutput.close();

super.close();
}
}
12 changes: 3 additions & 9 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,10 @@

<dependencyManagement>
<dependencies>

<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
<groupId>org.pgpainless</groupId>
<artifactId>pgpainless-core</artifactId>
<version>1.6.7</version>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be provided as a variable.

</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright (c) 2023 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/

package org.eclipse.packager.rpm.signature;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.Objects;

import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.eclipse.packager.rpm.RpmSignatureTag;
import org.eclipse.packager.rpm.header.Header;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.DocumentSignatureType;
import org.pgpainless.encryption_signing.EncryptionResult;
import org.pgpainless.encryption_signing.EncryptionStream;
import org.pgpainless.encryption_signing.ProducerOptions;
import org.pgpainless.encryption_signing.SigningOptions;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* An RSA signature processor for the header section only.
*/
public class PgpHeaderSignatureProcessor implements SignatureProcessor {
private final static Logger logger = LoggerFactory.getLogger(PgpHeaderSignatureProcessor.class);

private final PGPSecretKeyRing secretKeys;

private final SecretKeyRingProtector protector;

private final long keyId;

private PGPSignature signature;

private byte[] value;

public PgpHeaderSignatureProcessor(final PGPSecretKeyRing secretKeys,
final SecretKeyRingProtector protector,
final long keyId) {
this.secretKeys = Objects.requireNonNull(secretKeys);
this.protector = Objects.requireNonNull(protector);
this.keyId = keyId;
}

public PgpHeaderSignatureProcessor(final PGPSecretKeyRing secretKeys, SecretKeyRingProtector protector) {
this(secretKeys, protector, 0);
}

public PgpHeaderSignatureProcessor(final PGPSecretKeyRing secretKeys) {
this(secretKeys, SecretKeyRingProtector.unprotectedKeys());
}

@Override
public void feedHeader(final ByteBuffer header) {
try {
OutputStream sink = new OutputStream() {
@Override
public void write(int i) throws IOException {
// ignore "ciphertext"
}
};
SigningOptions signingOptions = SigningOptions.get();
if (keyId == 0) {
signingOptions.addDetachedSignature(protector, secretKeys, DocumentSignatureType.BINARY_DOCUMENT);
} else {
signingOptions.addDetachedSignature(protector, secretKeys, keyId);
}
EncryptionStream signingStream = PGPainless.encryptAndOrSign()
.onOutputStream(sink)
.withOptions(ProducerOptions.sign(signingOptions));

if (header.hasArray()) {
signingStream.write(header.array(), header.position(), header.remaining());
} else {
final byte[] buffer = new byte[header.remaining()];
header.get(buffer);
signingStream.write(buffer);
}

signingStream.close();
EncryptionResult result = signingStream.getResult();

this.signature = result.getDetachedSignatures().flatten().iterator().next();
this.value = signature.getEncoded();
} catch (final Exception e) {
throw new RuntimeException(e);
}
}

@Override
public void feedPayloadData(final ByteBuffer data) {
// we only work on the header data
}

@Override
public void finish(final Header<RpmSignatureTag> signature) {
switch (this.signature.getKeyAlgorithm()) {
// RSA
case PublicKeyAlgorithmTags.RSA_GENERAL: // 1
logger.info("RSA HEADER: {}", this.value);
signature.putBlob(RpmSignatureTag.RSAHEADER, this.value);
break;

// DSA
case PublicKeyAlgorithmTags.DSA: // 17
case PublicKeyAlgorithmTags.EDDSA_LEGACY: // 22
logger.info("DSA HEADER: {}", this.value);
signature.putBlob(RpmSignatureTag.DSAHEADER, this.value);
break;

default:
throw new RuntimeException("Unsupported public key algorithm id: " + this.signature.getKeyAlgorithm());
}

}
}
Loading