2020-10-25 19:54:03 +01:00
/ *
* Copyright 2020 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 .
* /
2020-11-19 17:51:57 +01:00
package org.pgpainless.key.modification.secretkeyring ;
2020-10-22 01:20:43 +02:00
2021-02-03 16:26:15 +01:00
import static org.pgpainless.util.CollectionUtils.iteratorToList ;
2020-11-13 15:08:37 +01:00
2020-11-10 17:25:35 +01:00
import java.security.InvalidAlgorithmParameterException ;
import java.security.NoSuchAlgorithmException ;
2020-10-23 16:44:21 +02:00
import java.util.ArrayList ;
2020-10-25 20:43:09 +01:00
import java.util.Collections ;
2020-11-27 13:00:06 +01:00
import java.util.Date ;
2020-10-23 16:44:21 +02:00
import java.util.Iterator ;
import java.util.List ;
2020-10-25 20:43:09 +01:00
import java.util.Map ;
2020-11-03 19:29:15 +01:00
import java.util.NoSuchElementException ;
2020-10-23 16:44:21 +02:00
import javax.annotation.Nonnull ;
2020-10-22 01:20:43 +02:00
import javax.annotation.Nullable ;
2021-07-19 14:52:59 +02:00
import org.bouncycastle.bcpg.S2K ;
2021-09-10 20:14:12 +02:00
import org.bouncycastle.bcpg.SecretKeyPacket ;
2020-10-23 16:44:21 +02:00
import org.bouncycastle.openpgp.PGPException ;
2020-11-10 17:25:35 +01:00
import org.bouncycastle.openpgp.PGPKeyPair ;
import org.bouncycastle.openpgp.PGPKeyRingGenerator ;
2020-10-23 16:44:21 +02:00
import org.bouncycastle.openpgp.PGPPrivateKey ;
import org.bouncycastle.openpgp.PGPPublicKey ;
2020-11-03 19:29:15 +01:00
import org.bouncycastle.openpgp.PGPPublicKeyRing ;
2020-10-23 16:44:21 +02:00
import org.bouncycastle.openpgp.PGPSecretKey ;
2020-10-22 01:20:43 +02:00
import org.bouncycastle.openpgp.PGPSecretKeyRing ;
2020-10-23 16:44:21 +02:00
import org.bouncycastle.openpgp.PGPSignature ;
2020-10-25 19:54:03 +01:00
import org.bouncycastle.openpgp.PGPSignatureGenerator ;
2020-11-20 12:01:39 +01:00
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator ;
import org.bouncycastle.openpgp.PGPSignatureSubpacketVector ;
2020-10-23 16:44:21 +02:00
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor ;
2020-10-25 20:43:09 +01:00
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor ;
2020-11-10 17:25:35 +01:00
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder ;
2020-10-25 19:54:03 +01:00
import org.bouncycastle.openpgp.operator.PGPDigestCalculator ;
import org.pgpainless.algorithm.HashAlgorithm ;
2020-10-23 16:44:21 +02:00
import org.pgpainless.algorithm.SignatureType ;
2020-11-10 17:25:35 +01:00
import org.pgpainless.algorithm.SymmetricKeyAlgorithm ;
2020-12-27 01:56:18 +01:00
import org.pgpainless.implementation.ImplementationFactory ;
2020-10-22 01:20:43 +02:00
import org.pgpainless.key.OpenPgpV4Fingerprint ;
2020-11-10 17:25:35 +01:00
import org.pgpainless.key.generation.KeyRingBuilder ;
2020-10-22 01:20:43 +02:00
import org.pgpainless.key.generation.KeySpec ;
2021-05-14 18:55:26 +02:00
import org.pgpainless.key.protection.CachingSecretKeyRingProtector ;
2021-05-28 23:14:20 +02:00
import org.pgpainless.key.protection.KeyRingProtectionSettings ;
2020-10-25 20:43:09 +01:00
import org.pgpainless.key.protection.PasswordBasedSecretKeyRingProtector ;
2020-10-23 16:44:21 +02:00
import org.pgpainless.key.protection.SecretKeyRingProtector ;
2021-05-14 13:18:34 +02:00
import org.pgpainless.key.protection.UnlockSecretKey ;
2020-10-25 20:43:09 +01:00
import org.pgpainless.key.protection.UnprotectedKeysProtector ;
2021-09-10 20:14:12 +02:00
import org.pgpainless.key.protection.fixes.S2KUsageFix ;
2020-10-25 20:43:09 +01:00
import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvider ;
2020-11-13 14:32:29 +01:00
import org.pgpainless.key.util.KeyRingUtils ;
2020-11-20 12:01:39 +01:00
import org.pgpainless.key.util.RevocationAttributes ;
2021-04-26 13:38:12 +02:00
import org.pgpainless.signature.SignatureUtils ;
import org.pgpainless.signature.subpackets.SignatureSubpacketGeneratorUtil ;
2021-05-28 23:14:20 +02:00
import org.pgpainless.util.Passphrase ;
2021-02-03 16:38:28 +01:00
import org.pgpainless.util.selection.userid.SelectUserId ;
2020-10-22 01:20:43 +02:00
2020-11-19 17:51:57 +01:00
public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
2020-10-22 01:20:43 +02:00
2020-10-30 12:28:11 +01:00
// Default algorithm for calculating private key checksums
// While I'd like to use something else, eg. SHA256, BC seems to lack support for
// calculating secret key checksums with algorithms other than SHA1.
2021-08-15 15:43:52 +02:00
private static final HashAlgorithm defaultDigestHashAlgorithm = HashAlgorithm . SHA1 ;
2020-10-30 12:28:11 +01:00
2020-10-22 01:20:43 +02:00
private PGPSecretKeyRing secretKeyRing ;
2020-11-19 17:51:57 +01:00
public SecretKeyRingEditor ( PGPSecretKeyRing secretKeyRing ) {
2020-10-22 01:20:43 +02:00
if ( secretKeyRing = = null ) {
throw new NullPointerException ( " SecretKeyRing MUST NOT be null. " ) ;
}
this . secretKeyRing = secretKeyRing ;
}
@Override
2020-11-19 17:51:57 +01:00
public SecretKeyRingEditorInterface addUserId ( String userId , SecretKeyRingProtector secretKeyRingProtector ) throws PGPException {
2020-10-23 16:44:21 +02:00
userId = sanitizeUserId ( userId ) ;
2020-11-03 19:29:15 +01:00
List < PGPSecretKey > secretKeyList = new ArrayList < > ( ) ;
Iterator < PGPSecretKey > secretKeyIterator = secretKeyRing . getSecretKeys ( ) ;
2021-05-28 23:14:20 +02:00
// add user-id certificate to primary key
PGPSecretKey primaryKey = secretKeyIterator . next ( ) ;
PGPPublicKey publicKey = primaryKey . getPublicKey ( ) ;
PGPPrivateKey privateKey = UnlockSecretKey . unlockSecretKey ( primaryKey , secretKeyRingProtector ) ;
publicKey = addUserIdToPubKey ( userId , privateKey , publicKey ) ;
primaryKey = PGPSecretKey . replacePublicKey ( primaryKey , publicKey ) ;
2020-11-03 19:29:15 +01:00
2021-05-28 23:14:20 +02:00
secretKeyList . add ( primaryKey ) ;
while ( secretKeyIterator . hasNext ( ) ) {
secretKeyList . add ( secretKeyIterator . next ( ) ) ;
2020-11-03 19:29:15 +01:00
}
2020-10-29 15:15:13 +01:00
2020-11-03 19:29:15 +01:00
secretKeyRing = new PGPSecretKeyRing ( secretKeyList ) ;
2020-10-25 19:54:03 +01:00
2020-11-03 19:29:15 +01:00
return this ;
}
private static PGPPublicKey addUserIdToPubKey ( String userId , PGPPrivateKey privateKey , PGPPublicKey publicKey ) throws PGPException {
if ( privateKey . getKeyID ( ) ! = publicKey . getKeyID ( ) ) {
throw new IllegalArgumentException ( " Key-ID mismatch! " ) ;
}
2020-10-29 15:15:13 +01:00
// Create signature with new user-id and add it to the public key
2020-11-13 15:08:37 +01:00
PGPSignatureGenerator signatureGenerator = SignatureUtils . getSignatureGeneratorFor ( publicKey ) ;
2020-10-23 16:44:21 +02:00
signatureGenerator . init ( SignatureType . POSITIVE_CERTIFICATION . getCode ( ) , privateKey ) ;
2020-11-03 19:29:15 +01:00
PGPSignature userIdSignature = signatureGenerator . generateCertification ( userId , publicKey ) ;
publicKey = PGPPublicKey . addCertification ( publicKey ,
userId , userIdSignature ) ;
2020-10-23 16:44:21 +02:00
2020-11-03 19:29:15 +01:00
return publicKey ;
2020-10-22 01:20:43 +02:00
}
2020-10-25 20:43:09 +01:00
// TODO: Move to utility class?
2020-10-23 16:44:21 +02:00
private String sanitizeUserId ( String userId ) {
userId = userId . trim ( ) ;
// TODO: Further research how to sanitize user IDs.
// eg. what about newlines?
return userId ;
}
2020-10-22 01:20:43 +02:00
@Override
2020-11-19 17:51:57 +01:00
public SecretKeyRingEditorInterface deleteUserId ( String userId , SecretKeyRingProtector protector ) {
2021-05-28 23:14:20 +02:00
return deleteUserIds ( SelectUserId . exactMatch ( userId ) , protector ) ;
2020-11-03 19:56:35 +01:00
}
2021-02-03 16:31:45 +01:00
@Override
2021-02-03 16:38:28 +01:00
public SecretKeyRingEditorInterface deleteUserIds ( SelectUserId selectionStrategy , SecretKeyRingProtector secretKeyRingProtector ) {
2020-11-03 19:56:35 +01:00
List < PGPPublicKey > publicKeys = new ArrayList < > ( ) ;
Iterator < PGPPublicKey > publicKeyIterator = secretKeyRing . getPublicKeys ( ) ;
2021-05-28 23:14:20 +02:00
PGPPublicKey primaryKey = publicKeyIterator . next ( ) ;
List < String > matchingUserIds = selectionStrategy . selectUserIds ( iteratorToList ( primaryKey . getUserIDs ( ) ) ) ;
if ( matchingUserIds . isEmpty ( ) ) {
throw new NoSuchElementException ( " Key does not have a matching user-id attribute. " ) ;
2020-11-03 19:56:35 +01:00
}
2021-05-28 23:14:20 +02:00
for ( String userId : matchingUserIds ) {
primaryKey = PGPPublicKey . removeCertification ( primaryKey , userId ) ;
2020-11-03 19:56:35 +01:00
}
2021-05-28 23:14:20 +02:00
publicKeys . add ( primaryKey ) ;
while ( publicKeyIterator . hasNext ( ) ) {
publicKeys . add ( publicKeyIterator . next ( ) ) ;
}
2020-11-03 19:56:35 +01:00
PGPPublicKeyRing publicKeyRing = new PGPPublicKeyRing ( publicKeys ) ;
secretKeyRing = PGPSecretKeyRing . replacePublicKeys ( secretKeyRing , publicKeyRing ) ;
return this ;
}
2020-10-22 01:20:43 +02:00
@Override
2020-11-19 17:51:57 +01:00
public SecretKeyRingEditorInterface addSubKey ( @Nonnull KeySpec keySpec ,
@Nonnull Passphrase subKeyPassphrase ,
SecretKeyRingProtector secretKeyRingProtector )
2020-11-10 17:25:35 +01:00
throws InvalidAlgorithmParameterException , NoSuchAlgorithmException , PGPException {
PGPSecretKey secretSubKey = generateSubKey ( keySpec , subKeyPassphrase ) ;
SecretKeyRingProtector subKeyProtector = PasswordBasedSecretKeyRingProtector
. forKey ( secretSubKey , subKeyPassphrase ) ;
2021-06-24 14:11:18 +02:00
PGPSignatureSubpacketVector hashedSubpackets = keySpec . getSubpackets ( ) ;
PGPSignatureSubpacketVector unhashedSubpackets = null ;
2020-11-10 17:25:35 +01:00
2021-06-24 14:11:18 +02:00
return addSubKey ( secretSubKey , hashedSubpackets , unhashedSubpackets , subKeyProtector , secretKeyRingProtector ) ;
2020-10-22 01:20:43 +02:00
}
@Override
2020-11-19 17:51:57 +01:00
public SecretKeyRingEditorInterface addSubKey ( PGPSecretKey secretSubKey ,
2021-06-24 14:11:18 +02:00
PGPSignatureSubpacketVector hashedSubpackets ,
PGPSignatureSubpacketVector unhashedSubpackets ,
2020-11-19 17:51:57 +01:00
SecretKeyRingProtector subKeyProtector ,
SecretKeyRingProtector keyRingProtector )
2020-11-10 17:25:35 +01:00
throws PGPException {
PGPPublicKey primaryKey = secretKeyRing . getSecretKey ( ) . getPublicKey ( ) ;
PBESecretKeyDecryptor ringDecryptor = keyRingProtector . getDecryptor ( primaryKey . getKeyID ( ) ) ;
PBESecretKeyEncryptor subKeyEncryptor = subKeyProtector . getEncryptor ( secretSubKey . getKeyID ( ) ) ;
2020-12-27 01:56:18 +01:00
PGPDigestCalculator digestCalculator =
ImplementationFactory . getInstance ( ) . getPGPDigestCalculator ( defaultDigestHashAlgorithm ) ;
PGPContentSignerBuilder contentSignerBuilder = ImplementationFactory . getInstance ( )
. getPGPContentSignerBuilder (
primaryKey . getAlgorithm ( ) ,
HashAlgorithm . SHA256 . getAlgorithmId ( ) // TODO: Why SHA256?
) ;
2020-11-10 17:25:35 +01:00
2021-05-14 13:18:34 +02:00
PGPPrivateKey privateSubKey = UnlockSecretKey . unlockSecretKey ( secretSubKey , subKeyProtector ) ;
2020-11-10 17:25:35 +01:00
PGPKeyPair subKeyPair = new PGPKeyPair ( secretSubKey . getPublicKey ( ) , privateSubKey ) ;
PGPKeyRingGenerator keyRingGenerator = new PGPKeyRingGenerator (
secretKeyRing , ringDecryptor , digestCalculator , contentSignerBuilder , subKeyEncryptor ) ;
2021-06-24 14:11:18 +02:00
keyRingGenerator . addSubKey ( subKeyPair , hashedSubpackets , unhashedSubpackets ) ;
2020-11-10 17:25:35 +01:00
secretKeyRing = keyRingGenerator . generateSecretKeyRing ( ) ;
return this ;
}
private PGPSecretKey generateSubKey ( @Nonnull KeySpec keySpec ,
@Nonnull Passphrase subKeyPassphrase )
throws PGPException , InvalidAlgorithmParameterException , NoSuchAlgorithmException {
2020-12-27 01:56:18 +01:00
PGPDigestCalculator checksumCalculator = ImplementationFactory . getInstance ( )
. getPGPDigestCalculator ( defaultDigestHashAlgorithm ) ;
2020-11-10 17:25:35 +01:00
PBESecretKeyEncryptor subKeyEncryptor = subKeyPassphrase . isEmpty ( ) ? null :
2020-12-27 01:56:18 +01:00
ImplementationFactory . getInstance ( ) . getPBESecretKeyEncryptor ( SymmetricKeyAlgorithm . AES_256 , subKeyPassphrase ) ;
2020-11-10 17:25:35 +01:00
PGPKeyPair keyPair = KeyRingBuilder . generateKeyPair ( keySpec ) ;
PGPSecretKey secretKey = new PGPSecretKey ( keyPair . getPrivateKey ( ) , keyPair . getPublicKey ( ) ,
checksumCalculator , false , subKeyEncryptor ) ;
return secretKey ;
}
@Override
2020-11-19 17:51:57 +01:00
public SecretKeyRingEditorInterface deleteSubKey ( OpenPgpV4Fingerprint fingerprint ,
SecretKeyRingProtector protector ) {
2020-11-03 19:32:01 +01:00
return deleteSubKey ( fingerprint . getKeyId ( ) , protector ) ;
2020-10-22 01:20:43 +02:00
}
@Override
2020-11-19 17:51:57 +01:00
public SecretKeyRingEditorInterface deleteSubKey ( long subKeyId ,
SecretKeyRingProtector protector ) {
2020-11-03 19:32:01 +01:00
if ( secretKeyRing . getSecretKey ( ) . getKeyID ( ) = = subKeyId ) {
throw new IllegalArgumentException ( " You cannot delete the primary key of this key ring. " ) ;
}
PGPSecretKey deleteMe = secretKeyRing . getSecretKey ( subKeyId ) ;
if ( deleteMe = = null ) {
2020-11-13 15:08:37 +01:00
throw new NoSuchElementException ( " KeyRing does not contain a key with keyId " + Long . toHexString ( subKeyId ) ) ;
2020-11-03 19:32:01 +01:00
}
PGPSecretKeyRing newKeyRing = PGPSecretKeyRing . removeSecretKey ( secretKeyRing , deleteMe ) ;
secretKeyRing = newKeyRing ;
return this ;
2020-10-22 01:20:43 +02:00
}
2020-11-22 21:25:52 +01:00
@Override
public SecretKeyRingEditorInterface revoke ( SecretKeyRingProtector secretKeyRingProtector ,
RevocationAttributes revocationAttributes )
throws PGPException {
return revokeSubKey ( secretKeyRing . getSecretKey ( ) . getKeyID ( ) , secretKeyRingProtector , revocationAttributes ) ;
}
2020-10-22 01:20:43 +02:00
@Override
2020-11-20 12:19:45 +01:00
public SecretKeyRingEditorInterface revokeSubKey ( OpenPgpV4Fingerprint fingerprint ,
SecretKeyRingProtector protector ,
RevocationAttributes revocationAttributes )
2020-11-13 14:32:29 +01:00
throws PGPException {
2020-11-20 12:19:45 +01:00
return revokeSubKey ( fingerprint . getKeyId ( ) , protector , revocationAttributes ) ;
2020-11-13 16:59:55 +01:00
}
2020-11-13 14:32:29 +01:00
2020-11-13 16:59:55 +01:00
@Override
2020-11-20 12:19:45 +01:00
public SecretKeyRingEditorInterface revokeSubKey ( long subKeyId ,
SecretKeyRingProtector protector ,
RevocationAttributes revocationAttributes )
throws PGPException {
2020-11-13 16:59:55 +01:00
PGPPublicKey revokeeSubKey = secretKeyRing . getPublicKey ( subKeyId ) ;
2020-11-13 14:32:29 +01:00
if ( revokeeSubKey = = null ) {
2020-11-13 16:59:55 +01:00
throw new NoSuchElementException ( " No subkey with id " + Long . toHexString ( subKeyId ) + " found. " ) ;
2020-11-13 14:32:29 +01:00
}
2020-11-20 12:19:45 +01:00
secretKeyRing = revokeSubKey ( protector , revokeeSubKey , revocationAttributes ) ;
2020-11-13 16:59:55 +01:00
return this ;
}
2021-01-22 18:28:48 +01:00
@Override
public SecretKeyRingEditorInterface revokeUserId ( String userId ,
SecretKeyRingProtector secretKeyRingProtector ,
RevocationAttributes revocationAttributes )
throws PGPException {
2021-05-28 23:14:20 +02:00
Iterator < String > userIds = secretKeyRing . getPublicKey ( ) . getUserIDs ( ) ;
boolean found = false ;
while ( userIds . hasNext ( ) ) {
if ( userId . equals ( userIds . next ( ) ) ) {
found = true ;
break ;
}
2021-01-22 18:28:48 +01:00
}
2021-05-28 23:14:20 +02:00
if ( ! found ) {
throw new NoSuchElementException ( " No user-id ' " + userId + " ' found on the key. " ) ;
2021-01-22 18:28:48 +01:00
}
2021-05-28 23:14:20 +02:00
return doRevokeUserId ( userId , secretKeyRingProtector , revocationAttributes ) ;
2021-01-22 18:28:48 +01:00
}
private SecretKeyRingEditorInterface doRevokeUserId ( String userId ,
SecretKeyRingProtector protector ,
RevocationAttributes revocationAttributes ) throws PGPException {
2021-05-28 23:14:20 +02:00
PGPSecretKey primarySecretKey = secretKeyRing . getSecretKey ( ) ;
PGPPublicKey primaryPublicKey = primarySecretKey . getPublicKey ( ) ;
PGPPrivateKey privateKey = UnlockSecretKey . unlockSecretKey ( primarySecretKey , protector ) ;
2021-01-22 18:28:48 +01:00
PGPSignatureSubpacketGenerator subpacketGenerator = new PGPSignatureSubpacketGenerator ( ) ;
subpacketGenerator . setSignatureCreationTime ( false , new Date ( ) ) ;
subpacketGenerator . setRevocable ( false , false ) ;
2021-05-28 23:14:20 +02:00
subpacketGenerator . setIssuerFingerprint ( false , primarySecretKey ) ;
2021-01-22 18:28:48 +01:00
if ( revocationAttributes ! = null ) {
RevocationAttributes . Reason reason = revocationAttributes . getReason ( ) ;
if ( reason ! = RevocationAttributes . Reason . NO_REASON
& & reason ! = RevocationAttributes . Reason . USER_ID_NO_LONGER_VALID ) {
throw new IllegalArgumentException ( " Revocation reason must either be NO_REASON or USER_ID_NO_LONGER_VALID " ) ;
}
subpacketGenerator . setRevocationReason ( false , revocationAttributes . getReason ( ) . code ( ) , revocationAttributes . getDescription ( ) ) ;
}
2021-05-28 23:14:20 +02:00
PGPSignatureGenerator signatureGenerator = SignatureUtils . getSignatureGeneratorFor ( primarySecretKey ) ;
2021-01-22 18:28:48 +01:00
signatureGenerator . setHashedSubpackets ( subpacketGenerator . generate ( ) ) ;
signatureGenerator . init ( SignatureType . CERTIFICATION_REVOCATION . getCode ( ) , privateKey ) ;
2021-05-28 23:14:20 +02:00
PGPSignature revocationSignature = signatureGenerator . generateCertification ( userId , primaryPublicKey ) ;
primaryPublicKey = PGPPublicKey . addCertification ( primaryPublicKey , userId , revocationSignature ) ;
2021-01-22 18:28:48 +01:00
PGPPublicKeyRing publicKeyRing = KeyRingUtils . publicKeyRingFrom ( secretKeyRing ) ;
2021-05-28 23:14:20 +02:00
publicKeyRing = PGPPublicKeyRing . insertPublicKey ( publicKeyRing , primaryPublicKey ) ;
2021-01-22 18:28:48 +01:00
secretKeyRing = PGPSecretKeyRing . replacePublicKeys ( secretKeyRing , publicKeyRing ) ;
return this ;
}
2021-01-18 17:09:57 +01:00
@Override
public SecretKeyRingEditorInterface setExpirationDate ( Date expiration ,
SecretKeyRingProtector secretKeyRingProtector )
throws PGPException {
return setExpirationDate ( new OpenPgpV4Fingerprint ( secretKeyRing ) , expiration , secretKeyRingProtector ) ;
}
2020-11-27 13:00:06 +01:00
@Override
public SecretKeyRingEditorInterface setExpirationDate ( OpenPgpV4Fingerprint fingerprint ,
Date expiration ,
SecretKeyRingProtector secretKeyRingProtector )
throws PGPException {
2021-01-18 17:09:57 +01:00
List < PGPSecretKey > secretKeyList = new ArrayList < > ( ) ;
PGPSecretKey primaryKey = secretKeyRing . getSecretKey ( ) ;
if ( ! primaryKey . isMasterKey ( ) ) {
throw new IllegalArgumentException ( " Key Ring does not appear to contain a primary secret key. " ) ;
2020-11-27 13:00:06 +01:00
}
2021-01-18 17:09:57 +01:00
boolean found = false ;
2021-06-10 12:42:48 +02:00
for ( PGPSecretKey secretKey : secretKeyRing ) {
2021-01-18 17:09:57 +01:00
// Skip over unaffected subkeys
if ( secretKey . getKeyID ( ) ! = fingerprint . getKeyId ( ) ) {
secretKeyList . add ( secretKey ) ;
continue ;
}
// We found the target subkey
found = true ;
secretKey = setExpirationDate ( primaryKey , secretKey , expiration , secretKeyRingProtector ) ;
secretKeyList . add ( secretKey ) ;
}
2020-11-27 13:00:06 +01:00
2021-01-18 17:09:57 +01:00
if ( ! found ) {
throw new IllegalArgumentException ( " Key Ring does not contain secret key with fingerprint " + fingerprint ) ;
2020-11-27 13:00:06 +01:00
}
2021-01-18 17:09:57 +01:00
secretKeyRing = new PGPSecretKeyRing ( secretKeyList ) ;
2020-11-27 13:00:06 +01:00
2021-01-18 17:09:57 +01:00
return this ;
}
2020-11-27 13:00:06 +01:00
2021-01-18 17:09:57 +01:00
private PGPSecretKey setExpirationDate ( PGPSecretKey primaryKey ,
PGPSecretKey subjectKey ,
Date expiration ,
SecretKeyRingProtector secretKeyRingProtector )
throws PGPException {
if ( expiration ! = null & & expiration . before ( subjectKey . getPublicKey ( ) . getCreationTime ( ) ) ) {
throw new IllegalArgumentException ( " Expiration date cannot be before creation date. " ) ;
2020-11-27 13:00:06 +01:00
}
2021-05-14 13:18:34 +02:00
PGPPrivateKey privateKey = UnlockSecretKey . unlockSecretKey ( primaryKey , secretKeyRingProtector ) ;
2021-01-18 17:09:57 +01:00
PGPPublicKey subjectPubKey = subjectKey . getPublicKey ( ) ;
PGPSignature oldSignature = getPreviousSignature ( primaryKey , subjectPubKey ) ;
PGPSignatureSubpacketVector oldSubpackets = oldSignature . getHashedSubPackets ( ) ;
PGPSignatureSubpacketGenerator subpacketGenerator = new PGPSignatureSubpacketGenerator ( oldSubpackets ) ;
SignatureSubpacketGeneratorUtil . setSignatureCreationTimeInSubpacketGenerator ( new Date ( ) , subpacketGenerator ) ;
SignatureSubpacketGeneratorUtil . setExpirationDateInSubpacketGenerator ( expiration , subjectPubKey . getCreationTime ( ) , subpacketGenerator ) ;
PGPSignatureGenerator signatureGenerator = SignatureUtils . getSignatureGeneratorFor ( primaryKey ) ;
signatureGenerator . setHashedSubpackets ( subpacketGenerator . generate ( ) ) ;
if ( primaryKey . getKeyID ( ) = = subjectKey . getKeyID ( ) ) {
signatureGenerator . init ( PGPSignature . POSITIVE_CERTIFICATION , privateKey ) ;
2020-11-27 13:00:06 +01:00
2021-01-18 17:09:57 +01:00
for ( Iterator < String > it = subjectKey . getUserIDs ( ) ; it . hasNext ( ) ; ) {
String userId = it . next ( ) ;
PGPSignature signature = signatureGenerator . generateCertification ( userId , subjectPubKey ) ;
subjectPubKey = PGPPublicKey . addCertification ( subjectPubKey , userId , signature ) ;
}
} else {
signatureGenerator . init ( PGPSignature . SUBKEY_BINDING , privateKey ) ;
2020-11-27 13:00:06 +01:00
2021-01-18 17:09:57 +01:00
PGPSignature signature = signatureGenerator . generateCertification ( primaryKey . getPublicKey ( ) , subjectPubKey ) ;
subjectPubKey = PGPPublicKey . addCertification ( subjectPubKey , signature ) ;
2020-11-27 13:00:06 +01:00
}
2021-01-18 17:09:57 +01:00
subjectKey = PGPSecretKey . replacePublicKey ( subjectKey , subjectPubKey ) ;
return subjectKey ;
}
2020-11-27 13:00:06 +01:00
2021-01-18 17:09:57 +01:00
private PGPSignature getPreviousSignature ( PGPSecretKey primaryKey , PGPPublicKey subjectPubKey ) {
PGPSignature oldSignature = null ;
if ( primaryKey . getKeyID ( ) = = subjectPubKey . getKeyID ( ) ) {
Iterator < PGPSignature > keySignatures = subjectPubKey . getSignaturesForKeyID ( primaryKey . getKeyID ( ) ) ;
while ( keySignatures . hasNext ( ) ) {
PGPSignature next = keySignatures . next ( ) ;
2021-09-08 11:59:28 +02:00
SignatureType type = SignatureType . valueOf ( next . getSignatureType ( ) ) ;
if ( type = = SignatureType . POSITIVE_CERTIFICATION | |
type = = SignatureType . CASUAL_CERTIFICATION | |
type = = SignatureType . GENERIC_CERTIFICATION ) {
2021-01-18 17:09:57 +01:00
oldSignature = next ;
}
}
if ( oldSignature = = null ) {
2021-09-08 11:59:28 +02:00
throw new IllegalStateException ( " Key " + new OpenPgpV4Fingerprint ( subjectPubKey ) + " does not have a previous positive/casual/generic certification signature. " ) ;
2021-01-18 17:09:57 +01:00
}
} else {
2021-06-10 12:41:12 +02:00
Iterator < PGPSignature > bindingSignatures = subjectPubKey . getSignaturesOfType ( SignatureType . SUBKEY_BINDING . getCode ( ) ) ;
2021-01-18 17:09:57 +01:00
while ( bindingSignatures . hasNext ( ) ) {
2021-06-10 12:41:12 +02:00
oldSignature = bindingSignatures . next ( ) ;
2021-01-18 17:09:57 +01:00
}
}
2020-11-27 13:00:06 +01:00
2021-01-18 17:09:57 +01:00
if ( oldSignature = = null ) {
throw new IllegalStateException ( " Key " + new OpenPgpV4Fingerprint ( subjectPubKey ) + " does not have a previous subkey binding signature. " ) ;
}
return oldSignature ;
2020-11-27 13:00:06 +01:00
}
2020-11-20 12:01:39 +01:00
@Override
2021-05-28 23:14:20 +02:00
public PGPSignature createRevocationCertificate ( SecretKeyRingProtector secretKeyRingProtector ,
2020-11-20 12:01:39 +01:00
RevocationAttributes revocationAttributes )
throws PGPException {
2021-05-28 23:14:20 +02:00
PGPPublicKey revokeeSubKey = secretKeyRing . getPublicKey ( ) ;
2020-11-20 12:01:39 +01:00
PGPSignature revocationCertificate = generateRevocation ( secretKeyRingProtector , revokeeSubKey , revocationAttributes ) ;
return revocationCertificate ;
}
@Override
2021-05-28 23:14:20 +02:00
public PGPSignature createRevocationCertificate ( long subkeyId , SecretKeyRingProtector secretKeyRingProtector , RevocationAttributes revocationAttributes ) throws PGPException {
PGPPublicKey revokeeSubKey = KeyRingUtils . requirePublicKeyFrom ( secretKeyRing , subkeyId ) ;
2020-11-20 12:01:39 +01:00
PGPSignature revocationCertificate = generateRevocation ( secretKeyRingProtector , revokeeSubKey , revocationAttributes ) ;
return revocationCertificate ;
}
2020-11-20 12:19:45 +01:00
private PGPSecretKeyRing revokeSubKey ( SecretKeyRingProtector protector ,
PGPPublicKey revokeeSubKey ,
RevocationAttributes revocationAttributes )
2020-11-20 12:01:39 +01:00
throws PGPException {
2020-11-20 12:19:45 +01:00
PGPSignature subKeyRevocation = generateRevocation ( protector , revokeeSubKey , revocationAttributes ) ;
2020-11-13 14:32:29 +01:00
revokeeSubKey = PGPPublicKey . addCertification ( revokeeSubKey , subKeyRevocation ) ;
// Inject revoked public key into key ring
PGPPublicKeyRing publicKeyRing = KeyRingUtils . publicKeyRingFrom ( secretKeyRing ) ;
publicKeyRing = PGPPublicKeyRing . insertPublicKey ( publicKeyRing , revokeeSubKey ) ;
2020-11-13 16:59:55 +01:00
return PGPSecretKeyRing . replacePublicKeys ( secretKeyRing , publicKeyRing ) ;
2020-10-22 01:20:43 +02:00
}
2020-11-20 12:01:39 +01:00
private PGPSignature generateRevocation ( SecretKeyRingProtector protector ,
PGPPublicKey revokeeSubKey ,
RevocationAttributes revocationAttributes )
throws PGPException {
PGPSecretKey primaryKey = secretKeyRing . getSecretKey ( ) ;
PGPSignatureGenerator signatureGenerator = SignatureUtils . getSignatureGeneratorFor ( primaryKey ) ;
PGPSignatureSubpacketGenerator subpacketGenerator = new PGPSignatureSubpacketGenerator ( ) ;
subpacketGenerator . setIssuerFingerprint ( false , primaryKey ) ;
if ( revocationAttributes ! = null ) {
subpacketGenerator . setRevocationReason ( false , revocationAttributes . getReason ( ) . code ( ) , revocationAttributes . getDescription ( ) ) ;
}
PGPSignatureSubpacketVector subPackets = subpacketGenerator . generate ( ) ;
signatureGenerator . setHashedSubpackets ( subPackets ) ;
2021-05-14 13:18:34 +02:00
PGPPrivateKey privateKey = UnlockSecretKey . unlockSecretKey ( primaryKey , protector ) ;
2020-11-20 12:01:39 +01:00
2021-04-26 13:38:12 +02:00
PGPSignature revocation ;
if ( revokeeSubKey . isMasterKey ( ) ) {
signatureGenerator . init ( SignatureType . KEY_REVOCATION . getCode ( ) , privateKey ) ;
revocation = signatureGenerator . generateCertification ( revokeeSubKey ) ;
} else {
signatureGenerator . init ( SignatureType . SUBKEY_REVOCATION . getCode ( ) , privateKey ) ;
revocation = signatureGenerator . generateCertification ( primaryKey . getPublicKey ( ) , revokeeSubKey ) ;
}
return revocation ;
2020-11-20 12:01:39 +01:00
}
2020-10-23 16:44:21 +02:00
@Override
public WithKeyRingEncryptionSettings changePassphraseFromOldPassphrase ( @Nullable Passphrase oldPassphrase ,
@Nonnull KeyRingProtectionSettings oldProtectionSettings ) {
2020-10-25 20:43:09 +01:00
SecretKeyRingProtector protector = new PasswordBasedSecretKeyRingProtector (
oldProtectionSettings ,
new SolitaryPassphraseProvider ( oldPassphrase ) ) ;
return new WithKeyRingEncryptionSettingsImpl ( null , protector ) ;
2020-10-23 16:44:21 +02:00
}
@Override
public WithKeyRingEncryptionSettings changeSubKeyPassphraseFromOldPassphrase ( @Nonnull Long keyId ,
@Nullable Passphrase oldPassphrase ,
@Nonnull KeyRingProtectionSettings oldProtectionSettings ) {
2020-10-25 20:43:09 +01:00
Map < Long , Passphrase > passphraseMap = Collections . singletonMap ( keyId , oldPassphrase ) ;
2021-05-14 18:55:26 +02:00
SecretKeyRingProtector protector = new CachingSecretKeyRingProtector (
2020-10-25 20:43:09 +01:00
passphraseMap , oldProtectionSettings , null ) ;
return new WithKeyRingEncryptionSettingsImpl ( keyId , protector ) ;
2020-10-23 16:44:21 +02:00
}
2020-10-22 01:20:43 +02:00
@Override
public PGPSecretKeyRing done ( ) {
return secretKeyRing ;
}
2020-10-23 16:44:21 +02:00
2020-10-25 20:43:09 +01:00
private final class WithKeyRingEncryptionSettingsImpl implements WithKeyRingEncryptionSettings {
private final Long keyId ;
// Protector to unlock the key with the old passphrase
private final SecretKeyRingProtector oldProtector ;
2020-10-29 15:15:13 +01:00
/ * *
* Builder for selecting protection settings .
*
* If the keyId is null , the whole keyRing will get the same new passphrase .
*
* @param keyId id of the subkey whose passphrase will be changed , or null .
* @param oldProtector protector do unlock the key / ring .
* /
2020-10-25 20:43:09 +01:00
private WithKeyRingEncryptionSettingsImpl ( Long keyId , SecretKeyRingProtector oldProtector ) {
this . keyId = keyId ;
this . oldProtector = oldProtector ;
}
2020-10-23 16:44:21 +02:00
@Override
public WithPassphrase withSecureDefaultSettings ( ) {
return withCustomSettings ( KeyRingProtectionSettings . secureDefaultSettings ( ) ) ;
}
@Override
public WithPassphrase withCustomSettings ( KeyRingProtectionSettings settings ) {
2020-10-25 20:43:09 +01:00
return new WithPassphraseImpl ( keyId , oldProtector , settings ) ;
2020-10-23 16:44:21 +02:00
}
}
2020-10-25 20:43:09 +01:00
private final class WithPassphraseImpl implements WithPassphrase {
private final SecretKeyRingProtector oldProtector ;
private final KeyRingProtectionSettings newProtectionSettings ;
private final Long keyId ;
private WithPassphraseImpl ( Long keyId , SecretKeyRingProtector oldProtector , KeyRingProtectionSettings newProtectionSettings ) {
this . keyId = keyId ;
this . oldProtector = oldProtector ;
this . newProtectionSettings = newProtectionSettings ;
}
2020-10-23 16:44:21 +02:00
@Override
2020-11-19 17:51:57 +01:00
public SecretKeyRingEditorInterface toNewPassphrase ( Passphrase passphrase ) throws PGPException {
2020-10-25 20:43:09 +01:00
SecretKeyRingProtector newProtector = new PasswordBasedSecretKeyRingProtector (
newProtectionSettings , new SolitaryPassphraseProvider ( passphrase ) ) ;
2020-11-19 17:51:57 +01:00
PGPSecretKeyRing secretKeys = changePassphrase ( keyId , SecretKeyRingEditor . this . secretKeyRing , oldProtector , newProtector ) ;
SecretKeyRingEditor . this . secretKeyRing = secretKeys ;
2020-10-25 20:43:09 +01:00
2020-11-19 17:51:57 +01:00
return SecretKeyRingEditor . this ;
2020-10-23 16:44:21 +02:00
}
@Override
2020-11-19 17:51:57 +01:00
public SecretKeyRingEditorInterface toNoPassphrase ( ) throws PGPException {
2020-10-25 20:43:09 +01:00
SecretKeyRingProtector newProtector = new UnprotectedKeysProtector ( ) ;
2020-11-19 17:51:57 +01:00
PGPSecretKeyRing secretKeys = changePassphrase ( keyId , SecretKeyRingEditor . this . secretKeyRing , oldProtector , newProtector ) ;
SecretKeyRingEditor . this . secretKeyRing = secretKeys ;
2020-10-25 20:43:09 +01:00
2020-11-19 17:51:57 +01:00
return SecretKeyRingEditor . this ;
2020-10-23 16:44:21 +02:00
}
2020-11-10 17:25:35 +01:00
}
2020-10-25 20:43:09 +01:00
2020-11-10 17:25:35 +01:00
private PGPSecretKeyRing changePassphrase ( Long keyId ,
PGPSecretKeyRing secretKeys ,
SecretKeyRingProtector oldProtector ,
SecretKeyRingProtector newProtector ) throws PGPException {
2021-09-10 20:14:12 +02:00
List < PGPSecretKey > secretKeyList = new ArrayList < > ( ) ;
2020-11-10 17:25:35 +01:00
if ( keyId = = null ) {
// change passphrase of whole key ring
Iterator < PGPSecretKey > secretKeyIterator = secretKeys . getSecretKeys ( ) ;
while ( secretKeyIterator . hasNext ( ) ) {
PGPSecretKey secretKey = secretKeyIterator . next ( ) ;
2021-07-19 14:52:59 +02:00
secretKey = reencryptPrivateKey ( secretKey , oldProtector , newProtector ) ;
2021-09-10 20:14:12 +02:00
secretKeyList . add ( secretKey ) ;
2020-11-10 17:25:35 +01:00
}
} else {
// change passphrase of selected subkey only
Iterator < PGPSecretKey > secretKeyIterator = secretKeys . getSecretKeys ( ) ;
while ( secretKeyIterator . hasNext ( ) ) {
PGPSecretKey secretKey = secretKeyIterator . next ( ) ;
if ( secretKey . getPublicKey ( ) . getKeyID ( ) = = keyId ) {
// Re-encrypt only the selected subkey
2021-07-19 14:52:59 +02:00
secretKey = reencryptPrivateKey ( secretKey , oldProtector , newProtector ) ;
2020-10-25 20:43:09 +01:00
}
2020-11-10 17:25:35 +01:00
secretKeyList . add ( secretKey ) ;
2020-10-25 20:43:09 +01:00
}
}
2021-09-10 20:14:12 +02:00
PGPSecretKeyRing newRing = new PGPSecretKeyRing ( secretKeyList ) ;
newRing = s2kUsageFixIfNecessary ( newRing , newProtector ) ;
return newRing ;
}
private PGPSecretKeyRing s2kUsageFixIfNecessary ( PGPSecretKeyRing secretKeys , SecretKeyRingProtector protector ) throws PGPException {
boolean hasS2KUsageChecksum = false ;
for ( PGPSecretKey secKey : secretKeys ) {
if ( secKey . getS2KUsage ( ) = = SecretKeyPacket . USAGE_CHECKSUM ) {
hasS2KUsageChecksum = true ;
2021-09-10 21:04:36 +02:00
break ;
2021-09-10 20:14:12 +02:00
}
}
if ( hasS2KUsageChecksum ) {
secretKeys = S2KUsageFix . replaceUsageChecksumWithUsageSha1 ( secretKeys , protector , true ) ;
}
return secretKeys ;
2020-11-10 17:25:35 +01:00
}
2020-10-25 20:43:09 +01:00
2021-07-19 14:52:59 +02:00
private static PGPSecretKey reencryptPrivateKey ( PGPSecretKey secretKey , SecretKeyRingProtector oldProtector , SecretKeyRingProtector newProtector ) throws PGPException {
S2K s2k = secretKey . getS2K ( ) ;
// If the key uses GNU_DUMMY_S2K, we leave it as is and skip this block
if ( s2k = = null | | s2k . getType ( ) ! = S2K . GNU_DUMMY_S2K ) {
long secretKeyId = secretKey . getKeyID ( ) ;
PBESecretKeyDecryptor decryptor = oldProtector . getDecryptor ( secretKeyId ) ;
PBESecretKeyEncryptor encryptor = newProtector . getEncryptor ( secretKeyId ) ;
secretKey = PGPSecretKey . copyWithNewPassword ( secretKey , decryptor , encryptor ) ;
}
2020-11-10 17:25:35 +01:00
return secretKey ;
2020-10-23 16:44:21 +02:00
}
2020-10-22 01:20:43 +02:00
}