mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-11-30 08:12:06 +01:00
Fix signature creation using keys without preferred algorithms
This commit is contained in:
parent
831dbb3c5d
commit
74c0c8a32e
4 changed files with 245 additions and 2 deletions
|
@ -16,6 +16,8 @@
|
|||
package org.pgpainless.key.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -42,6 +44,9 @@ public class OpenPgpKeyAttributeUtil {
|
|||
if (signatureType == SignatureType.POSITIVE_CERTIFICATION
|
||||
|| signatureType == SignatureType.GENERIC_CERTIFICATION) {
|
||||
int[] hashAlgos = signature.getHashedSubPackets().getPreferredHashAlgorithms();
|
||||
if (hashAlgos == null) {
|
||||
continue;
|
||||
}
|
||||
for (int h : hashAlgos) {
|
||||
hashAlgorithms.add(HashAlgorithm.fromId(h));
|
||||
}
|
||||
|
@ -53,4 +58,40 @@ public class OpenPgpKeyAttributeUtil {
|
|||
}
|
||||
return hashAlgorithms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hash algorithm that was used in the latest self signature.
|
||||
*
|
||||
* @param publicKey public key
|
||||
* @return list of hash algorithm
|
||||
*/
|
||||
public static List<HashAlgorithm> guessPreferredHashAlgorithms(PGPPublicKey publicKey) {
|
||||
HashAlgorithm hashAlgorithm = null;
|
||||
Date lastCreationDate = null;
|
||||
|
||||
Iterator<?> keySignatures = publicKey.getSignatures();
|
||||
while (keySignatures.hasNext()) {
|
||||
PGPSignature signature = (PGPSignature) keySignatures.next();
|
||||
if (signature.getKeyID() != publicKey.getKeyID()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SignatureType signatureType = SignatureType.valueOf(signature.getSignatureType());
|
||||
if (signatureType != SignatureType.POSITIVE_CERTIFICATION
|
||||
&& signatureType != SignatureType.GENERIC_CERTIFICATION) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Date creationDate = signature.getCreationTime();
|
||||
if (lastCreationDate == null || lastCreationDate.before(creationDate)) {
|
||||
lastCreationDate = creationDate;
|
||||
hashAlgorithm = HashAlgorithm.fromId(signature.getHashAlgorithm());
|
||||
}
|
||||
}
|
||||
|
||||
if (hashAlgorithm == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.singletonList(hashAlgorithm);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,14 +37,18 @@ public class SignatureUtils {
|
|||
|
||||
private static BcPGPContentSignerBuilder getPgpContentSignerBuilderForKey(PGPPublicKey publicKey) {
|
||||
List<HashAlgorithm> preferredHashAlgorithms = OpenPgpKeyAttributeUtil.getPreferredHashAlgorithms(publicKey);
|
||||
if (preferredHashAlgorithms.isEmpty()) {
|
||||
preferredHashAlgorithms = OpenPgpKeyAttributeUtil.guessPreferredHashAlgorithms(publicKey);
|
||||
}
|
||||
HashAlgorithm hashAlgorithm = negotiateHashAlgorithm(preferredHashAlgorithms);
|
||||
|
||||
return new BcPGPContentSignerBuilder(publicKey.getAlgorithm(), hashAlgorithm.getAlgorithmId());
|
||||
}
|
||||
|
||||
private static HashAlgorithm negotiateHashAlgorithm(List<HashAlgorithm> preferredHashAlgorithms) {
|
||||
// TODO: Match our list of supported hash algorithms against the list, to determine the best suitable algo.
|
||||
// For now we just take the first algorithm in the list and hope that BC has support for it.
|
||||
if (preferredHashAlgorithms.isEmpty()) {
|
||||
return HashAlgorithm.SHA512;
|
||||
}
|
||||
return preferredHashAlgorithms.get(0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.key.modification;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
import org.pgpainless.key.info.KeyRingInfo;
|
||||
import org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditorInterface;
|
||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||
import org.pgpainless.key.protection.UnprotectedKeysProtector;
|
||||
|
||||
public class RevokeKeyWithoutPreferredAlgorithmsOnPrimaryKey {
|
||||
|
||||
/**
|
||||
* This key has a primary key which does not carry a signature with preferred algorithms.
|
||||
* We therefore have to guess a suitable algorithm to create a new signature with the new expiration date.
|
||||
*/
|
||||
private static final String KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
|
||||
"Version: FlowCrypt Email Encryption 8.0.1\n" +
|
||||
"Comment: Seamlessly send and receive encrypted email\n" +
|
||||
"\n" +
|
||||
"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\n" +
|
||||
"zqyz7qFrSCvY\n" +
|
||||
"=3Zyp\n" +
|
||||
"-----END PGP PRIVATE KEY BLOCK-----";
|
||||
|
||||
@Test
|
||||
public void testChangingExpirationTimeWithKeyWithoutPrefAlgos() throws IOException, PGPException {
|
||||
Date expirationDate = new Date();
|
||||
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(KEY);
|
||||
List<OpenPgpV4Fingerprint> fingerprintList = new ArrayList<>();
|
||||
for (PGPSecretKey secretKey : secretKeys) {
|
||||
fingerprintList.add(new OpenPgpV4Fingerprint(secretKey));
|
||||
}
|
||||
SecretKeyRingProtector protector = new UnprotectedKeysProtector();
|
||||
|
||||
SecretKeyRingEditorInterface modify = PGPainless.modifyKeyRing(secretKeys)
|
||||
.setExpirationDate(expirationDate, protector);
|
||||
for (int i = 1; i < fingerprintList.size(); i++) {
|
||||
modify.setExpirationDate(fingerprintList.get(i), expirationDate, protector);
|
||||
}
|
||||
secretKeys = modify.done();
|
||||
|
||||
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
||||
assertEquals(expirationDate.getTime(), info.getExpirationDate().getTime(), 1000);
|
||||
for (OpenPgpV4Fingerprint fingerprint : fingerprintList) {
|
||||
assertEquals(expirationDate.getTime(), info.getExpirationDate(fingerprint).getTime(), 1000);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.algorithm.CompressionAlgorithm;
|
||||
import org.pgpainless.algorithm.HashAlgorithm;
|
||||
import org.pgpainless.algorithm.KeyFlag;
|
||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||
import org.pgpainless.key.generation.KeySpec;
|
||||
import org.pgpainless.key.generation.type.KeyType;
|
||||
import org.pgpainless.key.generation.type.eddsa.EdDSACurve;
|
||||
import org.pgpainless.key.util.OpenPgpKeyAttributeUtil;
|
||||
|
||||
public class GuessPreferredHashAlgorithmTest {
|
||||
|
||||
@Test
|
||||
public void test() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException {
|
||||
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||
.withMasterKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||
.withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)
|
||||
.withDetailedConfiguration()
|
||||
// Do not specify preferred algorithms
|
||||
.withPreferredSymmetricAlgorithms(new SymmetricKeyAlgorithm[] {})
|
||||
.withPreferredHashAlgorithms(new HashAlgorithm[] {})
|
||||
.withPreferredCompressionAlgorithms(new CompressionAlgorithm[] {})
|
||||
|
||||
.done())
|
||||
.withPrimaryUserId("test@test.test")
|
||||
.withoutPassphrase()
|
||||
.build();
|
||||
|
||||
PGPPublicKey publicKey = secretKeys.getPublicKey();
|
||||
assertEquals(Collections.emptyList(),
|
||||
OpenPgpKeyAttributeUtil.getPreferredHashAlgorithms(publicKey));
|
||||
assertEquals(Collections.singletonList(HashAlgorithm.SHA512),
|
||||
OpenPgpKeyAttributeUtil.guessPreferredHashAlgorithms(publicKey));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue