1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-09-09 18:29:39 +02:00

Port SecretKeyRingEditor, replace Singleton usage with API instance calls

This commit is contained in:
Paul Schaub 2025-03-19 17:24:04 +01:00
parent 2a71a98bba
commit 57540d8028
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
43 changed files with 811 additions and 611 deletions

View file

@ -67,9 +67,14 @@ class PGPainless(
* @param referenceTime reference time for evaluation
* @return [KeyRingInfo] wrapper
*/
@JvmOverloads
fun inspect(keyOrCertificate: OpenPGPCertificate, referenceTime: Date = Date()): KeyRingInfo =
KeyRingInfo(keyOrCertificate, this, referenceTime)
@JvmOverloads
fun modify(key: OpenPGPKey, referenceTime: Date = Date()): SecretKeyRingEditor =
SecretKeyRingEditor(key, this, referenceTime)
fun readKey(): OpenPGPKeyReader = api.readKeyOrCertificate()
fun toKey(secretKeyRing: PGPSecretKeyRing): OpenPGPKey =

View file

@ -694,7 +694,7 @@ class OpenPgpMessageInputStream(
private fun getDecryptionKey(pkesk: PGPPublicKeyEncryptedData): OpenPGPKey? =
options.getDecryptionKeys().firstOrNull {
it.pgpSecretKeyRing.getSecretKeyFor(pkesk) != null &&
PGPainless.inspectKeyRing(it).decryptionSubkeys.any { subkey ->
api.inspect(it).decryptionSubkeys.any { subkey ->
pkesk.keyIdentifier.matches(subkey.keyIdentifier)
}
}
@ -702,7 +702,7 @@ class OpenPgpMessageInputStream(
private fun getDecryptionKeys(pkesk: PGPPublicKeyEncryptedData): List<OpenPGPKey> =
options.getDecryptionKeys().filter {
it.pgpSecretKeyRing.getSecretKeyFor(pkesk) != null &&
PGPainless.inspectKeyRing(it).decryptionSubkeys.any { subkey ->
api.inspect(it).decryptionSubkeys.any { subkey ->
pkesk.keyIdentifier.matches(subkey.keyIdentifier)
}
}
@ -713,7 +713,7 @@ class OpenPgpMessageInputStream(
val algorithm = pkesk.algorithm
val candidates = mutableListOf<OpenPGPSecretKey>()
options.getDecryptionKeys().forEach {
val info = PGPainless.inspectKeyRing(it)
val info = api.inspect(it)
for (key in info.decryptionSubkeys) {
if (key.pgpPublicKey.algorithm == algorithm &&
info.isSecretKeyAvailable(key.keyIdentifier)) {

View file

@ -10,7 +10,6 @@ import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator
import org.pgpainless.PGPainless
import org.pgpainless.PGPainless.Companion.inspectKeyRing
import org.pgpainless.algorithm.EncryptionPurpose
import org.pgpainless.algorithm.SymmetricKeyAlgorithm
import org.pgpainless.algorithm.negotiation.SymmetricKeyAlgorithmNegotiator.Companion.byPopularity
@ -191,7 +190,7 @@ class EncryptionOptions(private val purpose: EncryptionPurpose, private val api:
userId: CharSequence,
encryptionKeySelector: EncryptionKeySelector
) = apply {
val info = inspectKeyRing(cert, evaluationDate)
val info = api.inspect(cert, evaluationDate)
val subkeys =
encryptionKeySelector.selectEncryptionSubkeys(
info.getEncryptionSubkeys(userId, purpose))
@ -289,7 +288,7 @@ class EncryptionOptions(private val purpose: EncryptionPurpose, private val api:
selector: EncryptionKeySelector,
wildcardKeyId: Boolean
) = apply {
val info = inspectKeyRing(cert, evaluationDate)
val info = api.inspect(cert, evaluationDate)
val primaryKeyExpiration =
try {
info.primaryKeyExpirationDate

View file

@ -8,6 +8,7 @@ import java.util.*
import org.bouncycastle.openpgp.PGPLiteralData
import org.bouncycastle.openpgp.PGPPublicKeyRing
import org.bouncycastle.openpgp.PGPSignature
import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.pgpainless.algorithm.CompressionAlgorithm
import org.pgpainless.algorithm.StreamEncoding
import org.pgpainless.algorithm.SymmetricKeyAlgorithm
@ -34,6 +35,9 @@ data class EncryptionResult(
val isForYourEyesOnly: Boolean
get() = PGPLiteralData.CONSOLE == fileName
fun isEncryptedFor(certificate: OpenPGPCertificate) =
recipients.any { certificate.getKey(it.keyIdentifier) != null }
/**
* Returns true, if the message was encrypted for at least one subkey of the given certificate.
*

View file

@ -12,7 +12,6 @@ import org.bouncycastle.openpgp.api.OpenPGPKey
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPPrivateKey
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPSecretKey
import org.pgpainless.PGPainless
import org.pgpainless.PGPainless.Companion.inspectKeyRing
import org.pgpainless.algorithm.DocumentSignatureType
import org.pgpainless.algorithm.HashAlgorithm
import org.pgpainless.algorithm.PublicKeyAlgorithm.Companion.requireFromId
@ -138,6 +137,7 @@ class SigningOptions(private val api: PGPainless) {
signatureType: DocumentSignatureType
) = addInlineSignature(signingKeyProtector, api.toKey(signingKey), signatureType)
@JvmOverloads
fun addInlineSignature(
signingKeyProtector: SecretKeyRingProtector,
signingKey: OpenPGPKey,
@ -145,7 +145,7 @@ class SigningOptions(private val api: PGPainless) {
signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT,
subpacketsCallback: Callback? = null
) = apply {
val keyRingInfo = inspectKeyRing(signingKey, evaluationDate)
val keyRingInfo = api.inspect(signingKey, evaluationDate)
if (userId != null && !keyRingInfo.isUserIdValid(userId)) {
throw UnboundUserIdException(
of(signingKey),
@ -212,7 +212,7 @@ class SigningOptions(private val api: PGPainless) {
subpacketsCallback: Callback? = null
): SigningOptions = apply {
val openPGPKey = signingKey.openPGPKey
val keyRingInfo = inspectKeyRing(openPGPKey, evaluationDate)
val keyRingInfo = api.inspect(openPGPKey, evaluationDate)
val signingPubKeys = keyRingInfo.signingSubkeys
if (signingPubKeys.isEmpty()) {
throw UnacceptableSigningKeyException(openPGPKey)
@ -319,7 +319,7 @@ class SigningOptions(private val api: PGPainless) {
signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT,
subpacketCallback: Callback? = null
): SigningOptions = apply {
val keyRingInfo = inspectKeyRing(signingKey, evaluationDate)
val keyRingInfo = api.inspect(signingKey, evaluationDate)
if (userId != null && !keyRingInfo.isUserIdValid(userId)) {
throw UnboundUserIdException(
of(signingKey),
@ -380,7 +380,7 @@ class SigningOptions(private val api: PGPainless) {
signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT,
subpacketCallback: Callback? = null
): SigningOptions = apply {
val keyRingInfo = inspectKeyRing(signingKey.openPGPKey, evaluationDate)
val keyRingInfo = api.inspect(signingKey.openPGPKey, evaluationDate)
val signingPrivKey: OpenPGPPrivateKey = signingKey.unlock(signingKeyProtector)
val hashAlgorithms =
if (userId != null) keyRingInfo.getPreferredHashAlgorithms(userId)

View file

@ -134,7 +134,7 @@ class CertifyCertificate(private val api: PGPainless) {
key: OpenPGPKey,
protector: SecretKeyRingProtector
): CertificationOnUserIdWithSubpackets {
val secretKey = getCertifyingSecretKey(key)
val secretKey = getCertifyingSecretKey(key, api)
val sigBuilder =
ThirdPartyCertificationSignatureBuilder(
certificationType.asSignatureType(), secretKey, protector, api)
@ -220,7 +220,7 @@ class CertifyCertificate(private val api: PGPainless) {
key: OpenPGPKey,
protector: SecretKeyRingProtector
): DelegationOnCertificateWithSubpackets {
val secretKey = getCertifyingSecretKey(key)
val secretKey = getCertifyingSecretKey(key, api)
val sigBuilder = ThirdPartyDirectKeySignatureBuilder(secretKey, protector, api)
if (trustworthiness != null) {
sigBuilder.hashedSubpackets.setTrust(
@ -306,10 +306,11 @@ class CertifyCertificate(private val api: PGPainless) {
companion object {
@JvmStatic
private fun getCertifyingSecretKey(
certificationKey: OpenPGPKey
certificationKey: OpenPGPKey,
api: PGPainless
): OpenPGPKey.OpenPGPSecretKey {
val now = Date()
val info = PGPainless.inspectKeyRing(certificationKey, now)
val info = api.inspect(certificationKey, now)
val fingerprint = info.fingerprint
val certificationPubKey = info.getPublicKey(fingerprint)

View file

@ -8,10 +8,11 @@ import java.util.*
import java.util.function.Predicate
import javax.annotation.Nonnull
import kotlin.NoSuchElementException
import openpgp.openPgpKeyId
import org.bouncycastle.bcpg.KeyIdentifier
import org.bouncycastle.bcpg.sig.KeyExpirationTime
import org.bouncycastle.openpgp.*
import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPSubkey
import org.bouncycastle.openpgp.api.OpenPGPImplementation
import org.bouncycastle.openpgp.api.OpenPGPKey
@ -20,7 +21,6 @@ import org.bouncycastle.openpgp.api.OpenPGPKeyEditor
import org.bouncycastle.openpgp.api.OpenPGPSignature
import org.bouncycastle.openpgp.api.SignatureParameters
import org.pgpainless.PGPainless
import org.pgpainless.PGPainless.Companion.inspectKeyRing
import org.pgpainless.algorithm.AlgorithmSuite
import org.pgpainless.algorithm.KeyFlag
import org.pgpainless.algorithm.OpenPGPKeyVersion
@ -29,7 +29,6 @@ import org.pgpainless.algorithm.negotiation.HashAlgorithmNegotiator
import org.pgpainless.bouncycastle.extensions.checksumCalculator
import org.pgpainless.bouncycastle.extensions.getKeyExpirationDate
import org.pgpainless.bouncycastle.extensions.publicKeyAlgorithm
import org.pgpainless.bouncycastle.extensions.requirePublicKey
import org.pgpainless.key.OpenPgpFingerprint
import org.pgpainless.key.generation.KeyRingBuilder
import org.pgpainless.key.generation.KeySpec
@ -50,8 +49,6 @@ class SecretKeyRingEditor(
override val referenceTime: Date = Date()
) : SecretKeyRingEditorInterface {
private var secretKeyRing: PGPSecretKeyRing = key.pgpSecretKeyRing
@JvmOverloads
constructor(
secretKeyRing: PGPSecretKeyRing,
@ -64,8 +61,7 @@ class SecretKeyRingEditor(
callback: SelfSignatureSubpackets.Callback?,
protector: SecretKeyRingProtector
): SecretKeyRingEditorInterface {
key = PGPainless.getInstance().toKey(secretKeyRing)
val info = inspectKeyRing(key, referenceTime)
val info = api.inspect(key, referenceTime)
require(!info.isHardRevoked(userId)) {
"User-ID $userId is hard revoked and cannot be re-certified."
}
@ -89,7 +85,7 @@ class SecretKeyRingEditor(
.setSignatureCreationTime(referenceTime)
.setHashedSubpacketsFunction { subpacketGenerator ->
val subpackets = SignatureSubpackets(subpacketGenerator)
subpackets.setAppropriateIssuerInfo(secretKeyRing.publicKey)
subpackets.setAppropriateIssuerInfo(key.primaryKey.pgpPublicKey)
subpackets.setKeyFlags(info.getKeyFlagsOf(key.keyIdentifier))
subpackets.setPreferredHashAlgorithms(hashAlgorithmPreferences)
@ -111,7 +107,6 @@ class SecretKeyRingEditor(
}
})
.done()
secretKeyRing = key.pgpSecretKeyRing
return this
}
@ -120,8 +115,8 @@ class SecretKeyRingEditor(
protector: SecretKeyRingProtector
): SecretKeyRingEditorInterface {
val uid = sanitizeUserId(userId)
val primaryKey = secretKeyRing.publicKey
var info = inspectKeyRing(secretKeyRing, referenceTime)
val primaryKey = key.primaryKey.pgpPublicKey
var info = api.inspect(key, referenceTime)
val primaryUserId = info.primaryUserId
val signature =
if (primaryUserId == null) info.latestDirectKeySelfSignature
@ -144,7 +139,7 @@ class SecretKeyRingEditor(
protector)
// unmark previous primary user-ids to be non-primary
info = inspectKeyRing(secretKeyRing, referenceTime)
info = api.inspect(key, referenceTime)
info.validAndExpiredUserIds
.filterNot { it == uid }
.forEach { otherUserId ->
@ -215,7 +210,7 @@ class SecretKeyRingEditor(
require(oldUID.isNotBlank()) { "Old user-ID cannot be empty." }
require(newUID.isNotBlank()) { "New user-ID cannot be empty." }
val info = inspectKeyRing(secretKeyRing, referenceTime)
val info = api.inspect(key, referenceTime)
if (!info.isUserIdValid(oldUID)) {
throw NoSuchElementException(
"Key does not carry user-ID '$oldUID', or it is not valid.")
@ -244,7 +239,6 @@ class SecretKeyRingEditor(
}
},
protector)
return revokeUserId(oldUID, protector)
}
@ -270,7 +264,7 @@ class SecretKeyRingEditor(
callback: SelfSignatureSubpackets.Callback?,
protector: SecretKeyRingProtector
): SecretKeyRingEditorInterface {
val version = OpenPGPKeyVersion.from(secretKeyRing.publicKey.version)
val version = OpenPGPKeyVersion.from(key.primarySecretKey.version)
val keyPair = KeyRingBuilder.generateKeyPair(keySpec, version, api.implementation)
val subkeyProtector =
PasswordBasedSecretKeyRingProtector.forKeyId(keyPair.keyIdentifier, subkeyPassphrase)
@ -303,8 +297,8 @@ class SecretKeyRingEditor(
"Public key algorithm policy violation: $subkeyAlgorithm with bit strength $bitStrength is not acceptable."
}
val primaryKey = secretKeyRing.secretKey
val info = inspectKeyRing(secretKeyRing, referenceTime)
val primaryKey = key.primarySecretKey.pgpSecretKey
val info = api.inspect(key, referenceTime)
val hashAlgorithm =
HashAlgorithmNegotiator.negotiateSignatureHashAlgorithm(api.algorithmPolicy)
.negotiateHashAlgorithm(info.preferredHashAlgorithms)
@ -341,7 +335,8 @@ class SecretKeyRingEditor(
secretSubkey =
KeyRingUtils.secretKeyPlusSignature(
secretSubkey, skBindingBuilder.build(secretSubkey.publicKey))
secretKeyRing = KeyRingUtils.keysPlusSecretKey(secretKeyRing, secretSubkey)
val secretKeyRing = KeyRingUtils.keysPlusSecretKey(key.pgpSecretKeyRing, secretSubkey)
key = api.toKey(secretKeyRing)
return this
}
@ -356,26 +351,33 @@ class SecretKeyRingEditor(
protector: SecretKeyRingProtector,
callback: RevocationSignatureSubpackets.Callback?
): SecretKeyRingEditorInterface {
return revokeSubKey(secretKeyRing.secretKey.keyID, protector, callback)
return revokeSubKey(key.keyIdentifier, protector, callback)
}
override fun revokeSubKey(
subkeyId: Long,
subkeyIdentifier: KeyIdentifier,
protector: SecretKeyRingProtector,
revocationAttributes: RevocationAttributes?
): SecretKeyRingEditorInterface {
return revokeSubKey(
subkeyId, protector, callbackFromRevocationAttributes(revocationAttributes))
subkeyIdentifier, protector, callbackFromRevocationAttributes(revocationAttributes))
}
override fun revokeSubKey(
subkeyId: Long,
subkeyIdentifier: KeyIdentifier,
protector: SecretKeyRingProtector,
callback: RevocationSignatureSubpackets.Callback?
): SecretKeyRingEditorInterface {
val revokeeSubKey = secretKeyRing.requirePublicKey(subkeyId)
var secretKeyRing = key.pgpSecretKeyRing
val revokeeSubKey =
key.getKey(subkeyIdentifier)
?: throw NoSuchElementException(
"Certificate ${key.keyIdentifier} does not contain subkey $subkeyIdentifier")
val subkeyRevocation = generateRevocation(protector, revokeeSubKey, callback)
secretKeyRing = injectCertification(secretKeyRing, revokeeSubKey, subkeyRevocation)
secretKeyRing =
injectCertification(
secretKeyRing, revokeeSubKey.pgpPublicKey, subkeyRevocation.signature)
key = api.toKey(secretKeyRing)
return this
}
@ -451,6 +453,7 @@ class SecretKeyRingEditor(
expiration: Date?,
protector: SecretKeyRingProtector
): SecretKeyRingEditorInterface {
var secretKeyRing = key.pgpSecretKeyRing
require(secretKeyRing.secretKey.isMasterKey) {
"OpenPGP key does not appear to contain a primary secret key."
}
@ -465,8 +468,9 @@ class SecretKeyRingEditor(
reissueDirectKeySignature(expiration, protector, prevDirectKeySig).signature)
}
val primaryUserId =
inspectKeyRing(secretKeyRing, referenceTime).getPossiblyExpiredPrimaryUserId()
val info = api.inspect(key, referenceTime)
val primaryUserId = info.getPossiblyExpiredPrimaryUserId()
if (primaryUserId != null) {
val prevUserIdSig = getPreviousUserIdSignatures(primaryUserId)
val userIdSig =
@ -474,7 +478,6 @@ class SecretKeyRingEditor(
secretKeyRing = injectCertification(secretKeyRing, primaryUserId, userIdSig)
}
val info = inspectKeyRing(secretKeyRing, referenceTime)
for (userId in info.validUserIds) {
if (userId == primaryUserId) {
continue
@ -493,35 +496,40 @@ class SecretKeyRingEditor(
}
}
key = api.toKey(secretKeyRing)
return this
}
override fun setExpirationDateOfSubkey(
expiration: Date?,
keyId: Long,
keyId: KeyIdentifier,
protector: SecretKeyRingProtector
): SecretKeyRingEditorInterface = apply {
var secretKeyRing = key.pgpSecretKeyRing
// is primary key
if (keyId == secretKeyRing.publicKey.keyID) {
if (keyId.matches(key.keyIdentifier)) {
return setExpirationDate(expiration, protector)
}
// is subkey
val subkey =
secretKeyRing.getPublicKey(keyId)
?: throw NoSuchElementException("No subkey with ID ${keyId.openPgpKeyId()} found.")
key.getKey(keyId) ?: throw NoSuchElementException("No subkey with ID $keyId found.")
val prevBinding =
inspectKeyRing(secretKeyRing).getCurrentSubkeyBindingSignature(keyId)
api.inspect(key).getCurrentSubkeyBindingSignature(keyId)
?: throw NoSuchElementException(
"Previous subkey binding signaure for ${keyId.openPgpKeyId()} MUST NOT be null.")
"Previous subkey binding signaure for $keyId MUST NOT be null.")
val bindingSig = reissueSubkeyBindingSignature(subkey, expiration, protector, prevBinding)
secretKeyRing = injectCertification(secretKeyRing, subkey, bindingSig)
secretKeyRing =
injectCertification(secretKeyRing, subkey.pgpPublicKey, bindingSig.signature)
key = api.toKey(secretKeyRing)
}
override fun createMinimalRevocationCertificate(
protector: SecretKeyRingProtector,
revocationAttributes: RevocationAttributes?
): PGPPublicKeyRing {
): OpenPGPCertificate {
// Check reason
if (revocationAttributes != null) {
require(RevocationAttributes.Reason.isKeyRevocation(revocationAttributes.reason)) {
@ -530,49 +538,47 @@ class SecretKeyRingEditor(
}
val revocation = createRevocation(protector, revocationAttributes)
var primaryKey = secretKeyRing.secretKey.publicKey
var primaryKey = key.primaryKey.pgpPublicKey
primaryKey = KeyRingUtils.getStrippedDownPublicKey(primaryKey)
primaryKey = PGPPublicKey.addCertification(primaryKey, revocation)
return PGPPublicKeyRing(listOf(primaryKey))
primaryKey = PGPPublicKey.addCertification(primaryKey, revocation.signature)
return api.toCertificate(PGPPublicKeyRing(listOf(primaryKey)))
}
override fun createRevocation(
protector: SecretKeyRingProtector,
revocationAttributes: RevocationAttributes?
): PGPSignature {
): OpenPGPSignature {
return generateRevocation(
protector,
secretKeyRing.publicKey,
callbackFromRevocationAttributes(revocationAttributes))
protector, key.primaryKey, callbackFromRevocationAttributes(revocationAttributes))
}
override fun createRevocation(
subkeyId: Long,
subkeyIdentifier: KeyIdentifier,
protector: SecretKeyRingProtector,
revocationAttributes: RevocationAttributes?
): PGPSignature {
): OpenPGPSignature {
return generateRevocation(
protector,
secretKeyRing.requirePublicKey(subkeyId),
key.getKey(subkeyIdentifier),
callbackFromRevocationAttributes(revocationAttributes))
}
override fun createRevocation(
subkeyId: Long,
subkeyIdentifier: KeyIdentifier,
protector: SecretKeyRingProtector,
callback: RevocationSignatureSubpackets.Callback?
): PGPSignature {
return generateRevocation(protector, secretKeyRing.requirePublicKey(subkeyId), callback)
): OpenPGPSignature {
return generateRevocation(protector, key.getKey(subkeyIdentifier), callback)
}
override fun createRevocation(
subkeyFingerprint: OpenPgpFingerprint,
protector: SecretKeyRingProtector,
revocationAttributes: RevocationAttributes?
): PGPSignature {
): OpenPGPSignature {
return generateRevocation(
protector,
secretKeyRing.requirePublicKey(subkeyFingerprint),
key.getKey(subkeyFingerprint.keyIdentifier),
callbackFromRevocationAttributes(revocationAttributes))
}
@ -599,8 +605,8 @@ class SecretKeyRingEditor(
mapOf(keyIdentifier to oldPassphrase), oldProtectionSettings, null))
}
override fun done(): PGPSecretKeyRing {
return secretKeyRing
override fun done(): OpenPGPKey {
return key
}
private fun sanitizeUserId(userId: CharSequence): CharSequence =
@ -619,11 +625,11 @@ class SecretKeyRingEditor(
private fun generateRevocation(
protector: SecretKeyRingProtector,
revokeeSubkey: PGPPublicKey,
revokeeSubkey: OpenPGPComponentKey,
callback: RevocationSignatureSubpackets.Callback?
): PGPSignature {
): OpenPGPSignature {
val signatureType =
if (revokeeSubkey.isMasterKey) SignatureType.KEY_REVOCATION
if (revokeeSubkey.isPrimaryKey) SignatureType.KEY_REVOCATION
else SignatureType.SUBKEY_REVOCATION
return RevocationSignatureBuilder(signatureType, key.primarySecretKey, protector, api)
@ -643,19 +649,20 @@ class SecretKeyRingEditor(
applyCallback(callback)
}
.let {
secretKeyRing =
injectCertification(secretKeyRing, userId, it.build(userId.toString()))
val secretKeyRing =
injectCertification(key.pgpSecretKeyRing, userId, it.build(userId.toString()))
key = api.toKey(secretKeyRing)
}
return this
}
private fun getPreviousDirectKeySignature(): PGPSignature? {
val info = inspectKeyRing(secretKeyRing, referenceTime)
val info = api.inspect(key, referenceTime)
return info.latestDirectKeySelfSignature
}
private fun getPreviousUserIdSignatures(userId: String): PGPSignature? {
val info = inspectKeyRing(secretKeyRing, referenceTime)
val info = api.inspect(key, referenceTime)
return info.getLatestUserIdCertification(userId)
}
@ -696,7 +703,7 @@ class SecretKeyRingEditor(
) {
if (expiration != null) {
hashedSubpackets.setKeyExpirationTime(
true, secretKeyRing.publicKey.creationTime, expiration)
true, key.primaryKey.creationTime, expiration)
} else {
hashedSubpackets.setKeyExpirationTime(KeyExpirationTime(true, 0))
}
@ -713,6 +720,7 @@ class SecretKeyRingEditor(
secretKeyRingProtector: SecretKeyRingProtector,
prevDirectKeySig: PGPSignature
): OpenPGPSignature {
val secretKeyRing = key.pgpSecretKeyRing
return DirectKeySelfSignatureBuilder(
secretKeyRing, secretKeyRingProtector, prevDirectKeySig, api)
.apply {
@ -735,13 +743,13 @@ class SecretKeyRingEditor(
}
private fun reissueSubkeyBindingSignature(
subkey: PGPPublicKey,
subkey: OpenPGPComponentKey,
expiration: Date?,
protector: SecretKeyRingProtector,
prevSubkeyBindingSignature: PGPSignature
): PGPSignature {
val primaryKey = secretKeyRing.publicKey
val secretSubkey: PGPSecretKey? = secretKeyRing.getSecretKey(subkey.keyID)
): OpenPGPSignature {
val primaryKey = key.primaryKey
val secretSubkey: OpenPGPSecretKey? = key.getSecretKey(subkey)
val builder =
SubkeyBindingSignatureBuilder(
@ -749,7 +757,7 @@ class SecretKeyRingEditor(
builder.hashedSubpackets.apply {
// set expiration
setSignatureCreationTime(referenceTime)
setKeyExpirationTime(subkey, expiration)
setKeyExpirationTime(subkey.pgpPublicKey, expiration)
setSignatureExpirationTime(null) // avoid copying sig exp time
// signing-capable subkeys need embedded primary key binding sig
@ -758,7 +766,7 @@ class SecretKeyRingEditor(
if (secretSubkey == null) {
throw NoSuchElementException(
"Secret key does not contain secret-key" +
" component for subkey ${subkey.keyID.openPgpKeyId()}")
" component for subkey ${subkey.keyIdentifier}")
}
// create new embedded back-sig
@ -766,7 +774,8 @@ class SecretKeyRingEditor(
addEmbeddedSignature(
PrimaryKeyBindingSignatureBuilder(
key.getSecretKey(subkey.keyIdentifier), protector, api)
.build(primaryKey))
.build(primaryKey)
.signature)
}
}
}
@ -775,7 +784,7 @@ class SecretKeyRingEditor(
}
private fun selectUserIds(predicate: Predicate<String>): List<String> =
inspectKeyRing(secretKeyRing).validUserIds.filter { predicate.test(it) }
key.validUserIds.map { it.userId }.filter { predicate.test(it) }.toList()
private class WithKeyRingEncryptionSettingsImpl(
private val editor: SecretKeyRingEditor,
@ -805,15 +814,17 @@ class SecretKeyRingEditor(
val protector =
PasswordBasedSecretKeyRingProtector(
newProtectionSettings, SolitaryPassphraseProvider(passphrase))
val secretKeys = changePassphrase(keyId, editor.secretKeyRing, oldProtector, protector)
editor.secretKeyRing = secretKeys
val secretKeys =
changePassphrase(keyId, editor.key.pgpSecretKeyRing, oldProtector, protector)
editor.key = editor.api.toKey(secretKeys)
return editor
}
override fun toNoPassphrase(): SecretKeyRingEditorInterface {
val protector = UnprotectedKeysProtector()
val secretKeys = changePassphrase(keyId, editor.secretKeyRing, oldProtector, protector)
editor.secretKeyRing = secretKeys
val secretKeys =
changePassphrase(keyId, editor.key.pgpSecretKeyRing, oldProtector, protector)
editor.key = editor.api.toKey(secretKeys)
return editor
}
}

View file

@ -10,6 +10,9 @@ import java.security.NoSuchAlgorithmException
import java.util.*
import org.bouncycastle.bcpg.KeyIdentifier
import org.bouncycastle.openpgp.*
import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.bouncycastle.openpgp.api.OpenPGPKey
import org.bouncycastle.openpgp.api.OpenPGPSignature
import org.pgpainless.algorithm.KeyFlag
import org.pgpainless.key.OpenPgpFingerprint
import org.pgpainless.key.generation.KeySpec
@ -262,26 +265,37 @@ interface SecretKeyRingEditorInterface {
protector: SecretKeyRingProtector,
revocationAttributes: RevocationAttributes? = null
): SecretKeyRingEditorInterface =
revokeSubKey(fingerprint.keyId, protector, revocationAttributes)
revokeSubKey(fingerprint.keyIdentifier, protector, revocationAttributes)
@Deprecated("Pass in a KeyIdentifier instead of keyId")
fun revokeSubKey(subkeyId: Long, protector: SecretKeyRingProtector) =
revokeSubKey(KeyIdentifier(subkeyId), protector)
/**
* Revoke the subkey binding signature of a subkey. The subkey with the provided key-id will be
* revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown.
*
* @param subkeyId id of the subkey
* @param subkeyIdentifier id of the subkey
* @param protector protector to unlock the primary key
* @return the builder
* @throws PGPException in case we cannot generate a revocation signature for the subkey
*/
@Throws(PGPException::class)
fun revokeSubKey(subkeyId: Long, protector: SecretKeyRingProtector) =
revokeSubKey(subkeyId, protector, null as RevocationAttributes?)
fun revokeSubKey(subkeyIdentifier: KeyIdentifier, protector: SecretKeyRingProtector) =
revokeSubKey(subkeyIdentifier, protector, null as RevocationAttributes?)
@Deprecated("Pass in a KeyIdentifier instead of keyId")
fun revokeSubKey(
subkeyId: Long,
protector: SecretKeyRingProtector,
revocationAttributes: RevocationAttributes? = null
) = revokeSubKey(KeyIdentifier(subkeyId), protector, revocationAttributes)
/**
* Revoke the subkey binding signature of a subkey. The subkey with the provided key-id will be
* revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown.
*
* @param subkeyId id of the subkey
* @param subkeyIdentifier id of the subkey
* @param protector protector to unlock the primary key
* @param revocationAttributes reason for the revocation
* @return the builder
@ -289,18 +303,25 @@ interface SecretKeyRingEditorInterface {
*/
@Throws(PGPException::class)
fun revokeSubKey(
subkeyId: Long,
subkeyIdentifier: KeyIdentifier,
protector: SecretKeyRingProtector,
revocationAttributes: RevocationAttributes? = null
): SecretKeyRingEditorInterface
@Deprecated("Pass in a KeyIdentifier instead of keyId.")
fun revokeSubKey(
keyId: Long,
protector: SecretKeyRingProtector,
callback: RevocationSignatureSubpackets.Callback?
) = revokeSubKey(KeyIdentifier(keyId), protector, callback)
/**
* Revoke the subkey binding signature of a subkey. The subkey with the provided key-id will be
* revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown.
*
* The provided subpackets callback is used to modify the revocation signatures subpackets.
*
* @param subkeyId id of the subkey
* @param subkeyIdentifier id of the subkey
* @param protector protector to unlock the secret key ring
* @param callback callback which can be used to modify the subpackets of the revocation
* signature
@ -309,7 +330,7 @@ interface SecretKeyRingEditorInterface {
*/
@Throws(PGPException::class)
fun revokeSubKey(
subkeyId: Long,
subkeyIdentifier: KeyIdentifier,
protector: SecretKeyRingProtector,
callback: RevocationSignatureSubpackets.Callback?
): SecretKeyRingEditorInterface
@ -470,6 +491,14 @@ interface SecretKeyRingEditorInterface {
protector: SecretKeyRingProtector
): SecretKeyRingEditorInterface
@Deprecated("Pass in a KeyIdentifier instead of keyId")
@Throws(PGPException::class)
fun setExpirationDateOfSubkey(
expiration: Date?,
keyId: Long,
protector: SecretKeyRingProtector
) = setExpirationDateOfSubkey(expiration, KeyIdentifier(keyId), protector)
/**
* Set the expiration date for the subkey identified by the given keyId to the given expiration
* date. If the key is supposed to never expire, then an expiration date of null is expected.
@ -484,7 +513,7 @@ interface SecretKeyRingEditorInterface {
@Throws(PGPException::class)
fun setExpirationDateOfSubkey(
expiration: Date?,
keyId: Long,
keyId: KeyIdentifier,
protector: SecretKeyRingProtector
): SecretKeyRingEditorInterface
@ -502,7 +531,7 @@ interface SecretKeyRingEditorInterface {
fun createMinimalRevocationCertificate(
protector: SecretKeyRingProtector,
revocationAttributes: RevocationAttributes?
): PGPPublicKeyRing
): OpenPGPCertificate
/**
* Create a detached revocation certificate, which can be used to revoke the whole key. The
@ -517,13 +546,21 @@ interface SecretKeyRingEditorInterface {
fun createRevocation(
protector: SecretKeyRingProtector,
revocationAttributes: RevocationAttributes?
): PGPSignature
): OpenPGPSignature
@Throws(PGPException::class)
@Deprecated("Pass in a KeyIdentifier instead of a keyId")
fun createRevocation(
subkeyId: Long,
protector: SecretKeyRingProtector,
revocationAttributes: RevocationAttributes?
) = createRevocation(KeyIdentifier(subkeyId), protector, revocationAttributes)
/**
* Create a detached revocation certificate, which can be used to revoke the specified subkey.
* The original key will not be modified by this method.
*
* @param subkeyId id of the subkey to be revoked
* @param subkeyIdentifier id of the subkey to be revoked
* @param protector protector to unlock the primary key.
* @param revocationAttributes reason for the revocation
* @return revocation certificate
@ -531,16 +568,24 @@ interface SecretKeyRingEditorInterface {
*/
@Throws(PGPException::class)
fun createRevocation(
subkeyId: Long,
subkeyIdentifier: KeyIdentifier,
protector: SecretKeyRingProtector,
revocationAttributes: RevocationAttributes?
): PGPSignature
): OpenPGPSignature
@Deprecated("Pass in a KeyIdentifier instead of keyId")
@Throws(PGPException::class)
fun createRevocation(
subkeyId: Long,
protector: SecretKeyRingProtector,
callback: RevocationSignatureSubpackets.Callback?
) = createRevocation(KeyIdentifier(subkeyId), protector, callback)
/**
* Create a detached revocation certificate, which can be used to revoke the specified subkey.
* The original key will not be modified by this method.
*
* @param subkeyId id of the subkey to be revoked
* @param subkeyIdentifier id of the subkey to be revoked
* @param protector protector to unlock the primary key.
* @param callback callback to modify the subpackets of the revocation certificate.
* @return revocation certificate
@ -548,10 +593,10 @@ interface SecretKeyRingEditorInterface {
*/
@Throws(PGPException::class)
fun createRevocation(
subkeyId: Long,
subkeyIdentifier: KeyIdentifier,
protector: SecretKeyRingProtector,
callback: RevocationSignatureSubpackets.Callback?
): PGPSignature
): OpenPGPSignature
/**
* Create a detached revocation certificate, which can be used to revoke the specified subkey.
@ -568,7 +613,7 @@ interface SecretKeyRingEditorInterface {
subkeyFingerprint: OpenPgpFingerprint,
protector: SecretKeyRingProtector,
revocationAttributes: RevocationAttributes?
): PGPSignature
): OpenPGPSignature
/**
* Change the passphrase of the whole key ring.
@ -676,7 +721,7 @@ interface SecretKeyRingEditorInterface {
*
* @return the key
*/
fun done(): PGPSecretKeyRing
fun done(): OpenPGPKey
fun addSubKey(
keySpec: KeySpec,

View file

@ -7,6 +7,7 @@ package org.pgpainless.key.protection
import org.bouncycastle.bcpg.KeyIdentifier
import org.bouncycastle.openpgp.PGPKeyRing
import org.bouncycastle.openpgp.PGPSecretKey
import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider
import org.pgpainless.util.Passphrase
@ -32,6 +33,25 @@ class PasswordBasedSecretKeyRingProtector : BaseSecretKeyRingProtector {
) : super(passphraseProvider, settings)
companion object {
@JvmStatic
fun forKey(
cert: OpenPGPCertificate,
passphrase: Passphrase
): PasswordBasedSecretKeyRingProtector {
return object : SecretKeyPassphraseProvider {
override fun getPassphraseFor(keyIdentifier: KeyIdentifier): Passphrase? {
return if (hasPassphrase(keyIdentifier)) passphrase else null
}
override fun hasPassphrase(keyIdentifier: KeyIdentifier): Boolean {
return cert.getKey(keyIdentifier) != null
}
}
.let { PasswordBasedSecretKeyRingProtector(it) }
}
@JvmStatic
fun forKey(
keyRing: PGPKeyRing,

View file

@ -12,6 +12,7 @@ import org.bouncycastle.openpgp.PGPSecretKey
import org.bouncycastle.openpgp.PGPSecretKeyRing
import org.bouncycastle.openpgp.api.KeyPassphraseProvider
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
import org.bouncycastle.openpgp.api.OpenPGPKey
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor
import org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider
@ -105,6 +106,10 @@ interface SecretKeyRingProtector : KeyPassphraseProvider {
KeyRingProtectionSettings.secureDefaultSettings(),
missingPassphraseCallback)
@JvmStatic
fun unlockEachKeyWith(passphrase: Passphrase, keys: OpenPGPKey): SecretKeyRingProtector =
fromPassphraseMap(keys.secretKeys.keys.associateWith { passphrase })
/**
* Use the provided passphrase to lock/unlock all keys in the provided key ring.
*

View file

@ -8,6 +8,8 @@ import java.util.function.Predicate
import org.bouncycastle.openpgp.PGPException
import org.bouncycastle.openpgp.PGPPublicKey
import org.bouncycastle.openpgp.PGPSignature
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentSignature
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPPrimaryKey
import org.bouncycastle.openpgp.api.OpenPGPKey
import org.pgpainless.PGPainless
import org.pgpainless.algorithm.HashAlgorithm
@ -63,4 +65,7 @@ class PrimaryKeyBindingSignatureBuilder :
fun build(primaryKey: PGPPublicKey): PGPSignature =
buildAndInitSignatureGenerator()
.generateCertification(primaryKey, signingKey.publicKey.pgpPublicKey)
fun build(primaryKey: OpenPGPPrimaryKey): OpenPGPComponentSignature =
OpenPGPComponentSignature(build(primaryKey.pgpPublicKey), primaryKey, primaryKey)
}

View file

@ -8,7 +8,11 @@ import java.util.function.Predicate
import org.bouncycastle.openpgp.PGPException
import org.bouncycastle.openpgp.PGPPublicKey
import org.bouncycastle.openpgp.PGPSignature
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentSignature
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPUserId
import org.bouncycastle.openpgp.api.OpenPGPKey
import org.bouncycastle.openpgp.api.OpenPGPSignature
import org.pgpainless.PGPainless
import org.pgpainless.algorithm.SignatureType
import org.pgpainless.key.protection.SecretKeyRingProtector
@ -59,6 +63,11 @@ constructor(
}
}
fun build(revokeeKey: OpenPGPComponentKey): OpenPGPSignature {
return OpenPGPComponentSignature(
build(revokeeKey.pgpPublicKey), signingKey.publicKey, revokeeKey)
}
@Throws(PGPException::class)
fun build(revokeeUserId: CharSequence): PGPSignature =
buildAndInitSignatureGenerator()
@ -69,6 +78,11 @@ constructor(
}
.generateCertification(revokeeUserId.toString(), signingKey.publicKey.pgpPublicKey)
fun build(revokeeUserId: OpenPGPUserId): OpenPGPComponentSignature {
return OpenPGPComponentSignature(
build(revokeeUserId.userId), signingKey.publicKey, revokeeUserId)
}
init {
hashedSubpackets.setRevocable(false)
}

View file

@ -8,6 +8,8 @@ import java.util.function.Predicate
import org.bouncycastle.openpgp.PGPException
import org.bouncycastle.openpgp.PGPPublicKey
import org.bouncycastle.openpgp.PGPSignature
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentSignature
import org.bouncycastle.openpgp.api.OpenPGPKey
import org.pgpainless.PGPainless
import org.pgpainless.algorithm.HashAlgorithm
@ -77,4 +79,7 @@ class SubkeyBindingSignatureBuilder : AbstractSignatureBuilder<SubkeyBindingSign
fun build(subkey: PGPPublicKey): PGPSignature =
buildAndInitSignatureGenerator()
.generateCertification(signingKey.publicKey.pgpPublicKey, subkey)
fun build(subkey: OpenPGPComponentKey): OpenPGPComponentSignature =
OpenPGPComponentSignature(build(subkey.pgpPublicKey), signingKey.publicKey, subkey)
}

View file

@ -6,6 +6,7 @@ package org.pgpainless.util.selection.userid
import java.util.function.Predicate
import org.bouncycastle.openpgp.PGPKeyRing
import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.pgpainless.PGPainless
abstract class SelectUserId : Predicate<String>, (String) -> Boolean {
@ -72,6 +73,14 @@ abstract class SelectUserId : Predicate<String>, (String) -> Boolean {
@JvmStatic
fun byEmail(email: CharSequence) = or(exactMatch(email), containsEmailAddress(email))
@JvmStatic
fun validUserId(key: OpenPGPCertificate) =
object : SelectUserId() {
private val info = PGPainless.getInstance().inspect(key)
override fun invoke(userId: String): Boolean = info.isUserIdValid(userId)
}
@JvmStatic
fun validUserId(keyRing: PGPKeyRing) =
object : SelectUserId() {

View file

@ -23,6 +23,7 @@ import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
@ -130,16 +131,16 @@ public class SigningTest {
@ExtendWith(TestAllImplementations.class)
public void testSignWithRevokedUserIdFails()
throws PGPException {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.modernKeyRing("alice", "password123")
.getPGPSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey()
.modernKeyRing("alice", "password123");
SecretKeyRingProtector protector = SecretKeyRingProtector.unlockAnyKeyWith(
Passphrase.fromPassword("password123"));
secretKeys = PGPainless.modifyKeyRing(secretKeys)
secretKeys = api.modify(secretKeys)
.revokeUserId("alice", protector)
.done();
final PGPSecretKeyRing fSecretKeys = secretKeys;
final OpenPGPKey fSecretKeys = secretKeys;
SigningOptions opts = SigningOptions.get();
// "alice" has been revoked

View file

@ -9,14 +9,14 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import org.bouncycastle.bcpg.KeyIdentifier;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
@ -34,25 +34,25 @@ import org.pgpainless.util.Passphrase;
/**
* PGPainless offers a simple API to modify keys by adding and replacing signatures and/or subkeys.
* The main entry point to this API is {@link PGPainless#modifyKeyRing(PGPSecretKeyRing)}.
* The main entry point to this API is {@link PGPainless#modify(OpenPGPKey)}.
*/
public class ModifyKeys {
private final String userId = "alice@pgpainless.org";
private final String originalPassphrase = "p4ssw0rd";
private PGPSecretKeyRing secretKey;
private long primaryKeyId;
private OpenPGPKey secretKey;
private KeyIdentifier primaryKeyId;
private KeyIdentifier encryptionSubkeyId;
private KeyIdentifier signingSubkeyId;
@BeforeEach
public void generateKey() {
secretKey = PGPainless.generateKeyRing()
.modernKeyRing(userId, originalPassphrase)
.getPGPSecretKeyRing();
PGPainless api = PGPainless.getInstance();
secretKey = api.generateKey()
.modernKeyRing(userId, originalPassphrase);
KeyRingInfo info = PGPainless.inspectKeyRing(secretKey);
primaryKeyId = info.getKeyIdentifier().getKeyId();
KeyRingInfo info = api.inspect(secretKey);
primaryKeyId = info.getKeyIdentifier();
encryptionSubkeyId = info.getEncryptionSubkeys(EncryptionPurpose.ANY).get(0).getKeyIdentifier();
signingSubkeyId = info.getSigningSubkeys().get(0).getKeyIdentifier();
}
@ -63,9 +63,9 @@ public class ModifyKeys {
@Test
public void extractPublicKey() {
// the certificate consists of only the public keys
PGPPublicKeyRing certificate = PGPainless.extractCertificate(secretKey);
OpenPGPCertificate certificate = secretKey.toCertificate();
KeyRingInfo info = PGPainless.inspectKeyRing(certificate);
KeyRingInfo info = PGPainless.getInstance().inspect(certificate);
assertFalse(info.isSecretKey());
}
@ -73,11 +73,11 @@ public class ModifyKeys {
* This example demonstrates how to export a secret key or certificate to an ASCII armored string.
*/
@Test
public void toAsciiArmoredString() {
PGPPublicKeyRing certificate = PGPainless.extractCertificate(secretKey);
public void toAsciiArmoredString() throws IOException {
OpenPGPCertificate certificate = secretKey.toCertificate();
String asciiArmoredSecretKey = PGPainless.asciiArmor(secretKey);
String asciiArmoredCertificate = PGPainless.asciiArmor(certificate);
String asciiArmoredSecretKey = secretKey.toAsciiArmoredString();
String asciiArmoredCertificate = certificate.toAsciiArmoredString();
assertTrue(asciiArmoredSecretKey.startsWith("-----BEGIN PGP PRIVATE KEY BLOCK-----"));
assertTrue(asciiArmoredCertificate.startsWith("-----BEGIN PGP PUBLIC KEY BLOCK-----"));
@ -88,7 +88,8 @@ public class ModifyKeys {
*/
@Test
public void changePassphrase() throws PGPException {
secretKey = PGPainless.modifyKeyRing(secretKey)
PGPainless api = PGPainless.getInstance();
secretKey = api.modify(secretKey)
.changePassphraseFromOldPassphrase(Passphrase.fromPassword(originalPassphrase))
.withSecureDefaultSettings()
.toNewPassphrase(Passphrase.fromPassword("n3wP4ssW0rD"))
@ -96,9 +97,9 @@ public class ModifyKeys {
// Old passphrase no longer works
assertThrows(WrongPassphraseException.class, () ->
UnlockSecretKey.unlockSecretKey(secretKey.getSecretKey(), Passphrase.fromPassword(originalPassphrase)));
UnlockSecretKey.unlockSecretKey(secretKey.getPGPSecretKeyRing().getSecretKey(), Passphrase.fromPassword(originalPassphrase)));
// But the new one does
UnlockSecretKey.unlockSecretKey(secretKey.getSecretKey(), Passphrase.fromPassword("n3wP4ssW0rD"));
UnlockSecretKey.unlockSecretKey(secretKey.getPGPSecretKeyRing().getSecretKey(), Passphrase.fromPassword("n3wP4ssW0rD"));
}
/**
@ -107,9 +108,10 @@ public class ModifyKeys {
*/
@Test
public void changeSingleSubkeyPassphrase() throws PGPException {
secretKey = PGPainless.modifyKeyRing(secretKey)
PGPainless api = PGPainless.getInstance();
secretKey = api.modify(secretKey)
// Here we change the passphrase of the encryption subkey
.changeSubKeyPassphraseFromOldPassphrase(encryptionSubkeyId.getKeyId(), Passphrase.fromPassword(originalPassphrase))
.changeSubKeyPassphraseFromOldPassphrase(encryptionSubkeyId, Passphrase.fromPassword(originalPassphrase))
.withSecureDefaultSettings()
.toNewPassphrase(Passphrase.fromPassword("cryptP4ssphr4s3"))
.done();
@ -117,12 +119,12 @@ public class ModifyKeys {
// encryption key can now only be unlocked using the new passphrase
assertThrows(WrongPassphraseException.class, () ->
UnlockSecretKey.unlockSecretKey(
secretKey.getSecretKey(encryptionSubkeyId), Passphrase.fromPassword(originalPassphrase)));
secretKey.getSecretKey(encryptionSubkeyId).getPGPSecretKey(), Passphrase.fromPassword(originalPassphrase)));
UnlockSecretKey.unlockSecretKey(
secretKey.getSecretKey(encryptionSubkeyId), Passphrase.fromPassword("cryptP4ssphr4s3"));
secretKey.getSecretKey(encryptionSubkeyId).getPGPSecretKey(), Passphrase.fromPassword("cryptP4ssphr4s3"));
// primary key remains unchanged
UnlockSecretKey.unlockSecretKey(
secretKey.getSecretKey(primaryKeyId), Passphrase.fromPassword(originalPassphrase));
secretKey.getSecretKey(primaryKeyId).getPGPSecretKey(), Passphrase.fromPassword(originalPassphrase));
}
/**
@ -130,13 +132,14 @@ public class ModifyKeys {
*/
@Test
public void addUserId() throws PGPException {
PGPainless api = PGPainless.getInstance();
SecretKeyRingProtector protector =
SecretKeyRingProtector.unlockEachKeyWith(Passphrase.fromPassword(originalPassphrase), secretKey);
secretKey = PGPainless.modifyKeyRing(secretKey)
secretKey = api.modify(secretKey)
.addUserId("additional@user.id", protector)
.done();
KeyRingInfo info = PGPainless.inspectKeyRing(secretKey);
KeyRingInfo info = api.inspect(secretKey);
assertTrue(info.isUserIdValid("additional@user.id"));
assertFalse(info.isUserIdValid("another@user.id"));
}
@ -156,11 +159,12 @@ public class ModifyKeys {
*/
@Test
public void addSubkey() {
PGPainless api = PGPainless.getInstance();
// Protector for unlocking the existing secret key
SecretKeyRingProtector protector =
SecretKeyRingProtector.unlockEachKeyWith(Passphrase.fromPassword(originalPassphrase), secretKey);
Passphrase subkeyPassphrase = Passphrase.fromPassword("subk3yP4ssphr4s3");
secretKey = PGPainless.modifyKeyRing(secretKey)
secretKey = api.modify(secretKey)
.addSubKey(
KeySpec.getBuilder(KeyType.ECDH(EllipticCurve._BRAINPOOLP512R1), KeyFlag.ENCRYPT_COMMS)
.build(),
@ -168,7 +172,7 @@ public class ModifyKeys {
protector)
.done();
KeyRingInfo info = PGPainless.inspectKeyRing(secretKey);
KeyRingInfo info = api.inspect(secretKey);
assertEquals(4, info.getSecretKeys().size());
assertEquals(4, info.getPublicKeys().size());
List<OpenPGPCertificate.OpenPGPComponentKey> encryptionSubkeys = info.getEncryptionSubkeys(EncryptionPurpose.COMMUNICATIONS);
@ -176,7 +180,7 @@ public class ModifyKeys {
OpenPGPCertificate.OpenPGPComponentKey addedKey = encryptionSubkeys.stream()
.filter(it -> !it.getKeyIdentifier().matches(encryptionSubkeyId)).findFirst()
.get();
UnlockSecretKey.unlockSecretKey(secretKey.getSecretKey(addedKey.getKeyIdentifier()), subkeyPassphrase);
UnlockSecretKey.unlockSecretKey(secretKey.getSecretKey(addedKey.getKeyIdentifier()).getPGPSecretKey(), subkeyPassphrase);
}
/**
@ -185,15 +189,16 @@ public class ModifyKeys {
*/
@Test
public void setKeyExpirationDate() {
PGPainless api = PGPainless.getInstance();
Date expirationDate = DateUtil.parseUTCDate("2030-06-24 12:44:56 UTC");
SecretKeyRingProtector protector = SecretKeyRingProtector
.unlockEachKeyWith(Passphrase.fromPassword(originalPassphrase), secretKey);
secretKey = PGPainless.modifyKeyRing(secretKey)
secretKey = api.modify(secretKey)
.setExpirationDate(expirationDate, protector)
.done();
KeyRingInfo info = PGPainless.inspectKeyRing(secretKey);
KeyRingInfo info = api.inspect(secretKey);
assertEquals(DateUtil.formatUTCDate(expirationDate),
DateUtil.formatUTCDate(info.getPrimaryKeyExpirationDate()));
assertEquals(DateUtil.formatUTCDate(expirationDate),
@ -207,19 +212,20 @@ public class ModifyKeys {
*/
@Test
public void revokeUserId() throws PGPException {
PGPainless api = PGPainless.getInstance();
SecretKeyRingProtector protector = SecretKeyRingProtector.unlockEachKeyWith(
Passphrase.fromPassword(originalPassphrase), secretKey);
secretKey = PGPainless.modifyKeyRing(secretKey)
secretKey = api.modify(secretKey)
.addUserId("alcie@pgpainless.org", protector)
.done();
// Initially the user-id is valid
assertTrue(PGPainless.inspectKeyRing(secretKey).isUserIdValid("alcie@pgpainless.org"));
assertTrue(api.inspect(secretKey).isUserIdValid("alcie@pgpainless.org"));
// Revoke the second user-id
secretKey = PGPainless.modifyKeyRing(secretKey)
secretKey = api.modify(secretKey)
.revokeUserId("alcie@pgpainless.org", protector)
.done();
// Now the user-id is no longer valid
assertFalse(PGPainless.inspectKeyRing(secretKey).isUserIdValid("alcie@pgpainless.org"));
assertFalse(api.inspect(secretKey).isUserIdValid("alcie@pgpainless.org"));
}
}

View file

@ -14,8 +14,11 @@ import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
import org.pgpainless.PGPainless;
import org.pgpainless.util.Passphrase;
public class TestKeys {
@ -265,6 +268,10 @@ public class TestKeys {
"=1d67\n" +
"-----END PGP PRIVATE KEY BLOCK-----";
public static OpenPGPKey getJulietKey() throws PGPException, IOException {
return PGPainless.getInstance().toKey(getJulietSecretKeyRing());
}
public static PGPSecretKeyRing getJulietSecretKeyRing() throws IOException, PGPException {
if (julietSecretKeyRing == null) {
julietSecretKeyRing = new PGPSecretKeyRing(
@ -281,6 +288,10 @@ public class TestKeys {
return julietSecretKeyRingCollection;
}
public static OpenPGPCertificate getJulietCertificate() throws IOException {
return PGPainless.getInstance().toCertificate(getJulietPublicKeyRing());
}
public static PGPPublicKeyRing getJulietPublicKeyRing() throws IOException {
if (julietPublicKeyRing == null) {
julietPublicKeyRing = new PGPPublicKeyRing(
@ -297,6 +308,10 @@ public class TestKeys {
return julietPublicKeyRingCollection;
}
public static OpenPGPKey getRomeoKey() throws PGPException, IOException {
return PGPainless.getInstance().toKey(getRomeoSecretKeyRing());
}
public static PGPSecretKeyRing getRomeoSecretKeyRing() throws IOException, PGPException {
if (romeoSecretKeyRing == null) {
romeoSecretKeyRing = new PGPSecretKeyRing(
@ -313,6 +328,10 @@ public class TestKeys {
return romeoSecretKeyRingCollection;
}
public static OpenPGPCertificate getRomeoCertificate() throws IOException {
return PGPainless.getInstance().toCertificate(getRomeoPublicKeyRing());
}
public static PGPPublicKeyRing getRomeoPublicKeyRing() throws IOException {
if (romeoPublicKeyRing == null) {
romeoPublicKeyRing = new PGPPublicKeyRing(
@ -329,6 +348,10 @@ public class TestKeys {
return romeoPublicKeyRingCollection;
}
public static OpenPGPKey getEmilKey() throws PGPException, IOException {
return PGPainless.getInstance().toKey(getEmilSecretKeyRing());
}
public static PGPSecretKeyRing getEmilSecretKeyRing() throws IOException, PGPException {
if (emilSecretKeyRing == null) {
emilSecretKeyRing = new PGPSecretKeyRing(
@ -345,6 +368,10 @@ public class TestKeys {
return emilSecretKeyRingCollection;
}
public static OpenPGPCertificate getEmilCertificate() throws IOException {
return PGPainless.getInstance().toCertificate(getEmilPublicKeyRing());
}
public static PGPPublicKeyRing getEmilPublicKeyRing() throws IOException {
if (emilPublicKeyRing == null) {
emilPublicKeyRing = new PGPPublicKeyRing(
@ -361,6 +388,10 @@ public class TestKeys {
return emilPublicKeyRingCollection;
}
public static OpenPGPKey getCryptieKey() throws PGPException, IOException {
return PGPainless.getInstance().toKey(getCryptieSecretKeyRing());
}
public static PGPSecretKeyRing getCryptieSecretKeyRing() throws IOException, PGPException {
if (cryptieSecretKeyRing == null) {
cryptieSecretKeyRing = new PGPSecretKeyRing(
@ -377,6 +408,10 @@ public class TestKeys {
return cryptieSecretKeyRingCollection;
}
public static OpenPGPCertificate getCryptieCertificate() throws IOException {
return PGPainless.getInstance().toCertificate(getCryptiePublicKeyRing());
}
public static PGPPublicKeyRing getCryptiePublicKeyRing() throws IOException {
if (cryptiePublicKeyRing == null) {
cryptiePublicKeyRing = new PGPPublicKeyRing(

View file

@ -18,10 +18,10 @@ import java.util.List;
import org.bouncycastle.bcpg.sig.IssuerFingerprint;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.HashAlgorithm;
@ -39,10 +39,10 @@ public class KeyGenerationSubpacketsTest {
@Test
public void verifyDefaultSubpacketsForUserIdSignatures()
throws PGPException {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().modernKeyRing("Alice")
.getPGPSecretKeyRing();
Date plus1Sec = new Date(secretKeys.getPublicKey().getCreationTime().getTime() + 1000);
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey().modernKeyRing("Alice");
Date plus1Sec = new Date(secretKeys.getPrimarySecretKey().getCreationTime().getTime() + 1000);
KeyRingInfo info = api.inspect(secretKeys);
PGPSignature userIdSig = info.getLatestUserIdCertification("Alice");
assertNotNull(userIdSig);
int keyFlags = userIdSig.getHashedSubPackets().getKeyFlags();
@ -54,7 +54,7 @@ public class KeyGenerationSubpacketsTest {
assertEquals("Alice", info.getPrimaryUserId());
secretKeys = PGPainless.modifyKeyRing(secretKeys, plus1Sec)
secretKeys = api.modify(secretKeys, plus1Sec)
.addUserId("Bob",
new SelfSignatureSubpackets.Callback() {
@Override
@ -66,7 +66,7 @@ public class KeyGenerationSubpacketsTest {
.addUserId("Alice", SecretKeyRingProtector.unprotectedKeys())
.done();
info = PGPainless.inspectKeyRing(secretKeys, plus1Sec);
info = api.inspect(secretKeys, plus1Sec);
userIdSig = info.getLatestUserIdCertification("Alice");
assertNotNull(userIdSig);
@ -89,7 +89,7 @@ public class KeyGenerationSubpacketsTest {
Date now = plus1Sec;
Date t1 = new Date(now.getTime() + 1000 * 60 * 60);
secretKeys = PGPainless.modifyKeyRing(secretKeys, t1)
secretKeys = api.modify(secretKeys, t1)
.addUserId("Alice", new SelfSignatureSubpackets.Callback() {
@Override
public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) {
@ -98,7 +98,7 @@ public class KeyGenerationSubpacketsTest {
}
}, SecretKeyRingProtector.unprotectedKeys())
.done();
info = PGPainless.inspectKeyRing(secretKeys, t1);
info = api.inspect(secretKeys, t1);
assertEquals("Alice", info.getPrimaryUserId());
assertEquals(Collections.singleton(HashAlgorithm.SHA1), info.getPreferredHashAlgorithms("Alice"));
}
@ -106,29 +106,29 @@ public class KeyGenerationSubpacketsTest {
@Test
public void verifyDefaultSubpacketsForSubkeyBindingSignatures()
throws PGPException {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().modernKeyRing("Alice")
.getPGPSecretKeyRing();
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey().modernKeyRing("Alice");
KeyRingInfo info = api.inspect(secretKeys);
List<OpenPGPCertificate.OpenPGPComponentKey> keysBefore = info.getPublicKeys();
secretKeys = PGPainless.modifyKeyRing(secretKeys)
secretKeys = api.modify(secretKeys)
.addSubKey(KeySpec.getBuilder(KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519), KeyFlag.SIGN_DATA).build(),
Passphrase.emptyPassphrase(), SecretKeyRingProtector.unprotectedKeys())
.done();
info = PGPainless.inspectKeyRing(secretKeys);
info = api.inspect(secretKeys);
List<OpenPGPCertificate.OpenPGPComponentKey> keysAfter = new ArrayList<>(info.getPublicKeys());
keysAfter.removeAll(keysBefore);
assertEquals(1, keysAfter.size());
OpenPGPCertificate.OpenPGPComponentKey newSigningKey = keysAfter.get(0);
PGPSignature bindingSig = info.getCurrentSubkeyBindingSignature(newSigningKey.getKeyIdentifier().getKeyId());
PGPSignature bindingSig = info.getCurrentSubkeyBindingSignature(newSigningKey.getKeyIdentifier());
assertNotNull(bindingSig);
assureSignatureHasDefaultSubpackets(bindingSig, secretKeys, KeyFlag.SIGN_DATA);
assertNotNull(bindingSig.getHashedSubPackets().getEmbeddedSignatures().get(0));
secretKeys = PGPainless.modifyKeyRing(secretKeys)
secretKeys = api.modify(secretKeys)
.addSubKey(KeySpec.getBuilder(KeyType.XDH_LEGACY(XDHLegacySpec._X25519), KeyFlag.ENCRYPT_COMMS).build(),
Passphrase.emptyPassphrase(),
new SelfSignatureSubpackets.Callback() {
@ -139,24 +139,24 @@ public class KeyGenerationSubpacketsTest {
}, SecretKeyRingProtector.unprotectedKeys())
.done();
info = PGPainless.inspectKeyRing(secretKeys);
info = api.inspect(secretKeys);
keysAfter = new ArrayList<>(info.getPublicKeys());
keysAfter.removeAll(keysBefore);
keysAfter.remove(newSigningKey);
assertEquals(1, keysAfter.size());
OpenPGPCertificate.OpenPGPComponentKey newEncryptionKey = keysAfter.get(0);
bindingSig = info.getCurrentSubkeyBindingSignature(newEncryptionKey.getKeyIdentifier().getKeyId());
bindingSig = info.getCurrentSubkeyBindingSignature(newEncryptionKey.getKeyIdentifier());
assertNotNull(bindingSig);
assertNull(bindingSig.getHashedSubPackets().getIssuerFingerprint());
assertEquals(KeyFlag.toBitmask(KeyFlag.ENCRYPT_COMMS), bindingSig.getHashedSubPackets().getKeyFlags());
}
private void assureSignatureHasDefaultSubpackets(PGPSignature signature, PGPSecretKeyRing secretKeys, KeyFlag... keyFlags) {
private void assureSignatureHasDefaultSubpackets(PGPSignature signature, OpenPGPKey secretKeys, KeyFlag... keyFlags) {
PGPSignatureSubpacketVector hashedSubpackets = signature.getHashedSubPackets();
assertNotNull(hashedSubpackets.getIssuerFingerprint());
assertEquals(secretKeys.getPublicKey().getKeyID(), hashedSubpackets.getIssuerKeyID());
assertEquals(secretKeys.getKeyIdentifier().getKeyId(), hashedSubpackets.getIssuerKeyID());
assertArrayEquals(
secretKeys.getPublicKey().getFingerprint(),
secretKeys.getFingerprint(),
hashedSubpackets.getIssuerFingerprint().getFingerprint());
assertEquals(hashedSubpackets.getKeyFlags(), KeyFlag.toBitmask(keyFlags));
}

View file

@ -24,9 +24,7 @@ import java.util.Set;
import org.bouncycastle.bcpg.KeyIdentifier;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.JUtils;
@ -50,7 +48,6 @@ import org.pgpainless.key.generation.type.ecc.EllipticCurve;
import org.pgpainless.key.generation.type.eddsa_legacy.EdDSALegacyCurve;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.key.protection.UnprotectedKeysProtector;
import org.pgpainless.key.util.KeyRingUtils;
import org.pgpainless.key.util.RevocationAttributes;
import org.pgpainless.key.util.UserId;
import org.pgpainless.util.DateUtil;
@ -62,11 +59,12 @@ public class KeyRingInfoTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void testWithEmilsKeys() throws IOException, PGPException {
PGPainless api = PGPainless.getInstance();
PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing();
PGPPublicKeyRing publicKeys = TestKeys.getEmilPublicKeyRing();
KeyRingInfo sInfo = PGPainless.inspectKeyRing(secretKeys);
KeyRingInfo pInfo = PGPainless.inspectKeyRing(publicKeys);
OpenPGPKey secretKeys = TestKeys.getEmilKey();
OpenPGPCertificate publicKeys = TestKeys.getEmilCertificate();
KeyRingInfo sInfo = api.inspect(secretKeys);
KeyRingInfo pInfo = api.inspect(publicKeys);
assertEquals(TestKeys.EMIL_KEY_ID, sInfo.getKeyIdentifier().getKeyId());
assertEquals(TestKeys.EMIL_KEY_ID, pInfo.getKeyIdentifier().getKeyId());
@ -108,13 +106,13 @@ public class KeyRingInfoTest {
assertNull(sInfo.getRevocationDate());
assertNull(pInfo.getRevocationDate());
Date revocationDate = DateUtil.now();
PGPSecretKeyRing revoked = PGPainless.modifyKeyRing(secretKeys).revoke(
OpenPGPKey revoked = api.modify(secretKeys).revoke(
new UnprotectedKeysProtector(),
RevocationAttributes.createKeyRevocation()
.withReason(RevocationAttributes.Reason.KEY_RETIRED)
.withoutDescription()
).done();
KeyRingInfo rInfo = PGPainless.inspectKeyRing(revoked);
KeyRingInfo rInfo = api.inspect(revoked);
assertNotNull(rInfo.getRevocationDate());
assertEquals(revocationDate.getTime(), rInfo.getRevocationDate().getTime(), 5);
assertEquals(revocationDate.getTime(), rInfo.getLastModified().getTime(), 5);
@ -125,32 +123,34 @@ public class KeyRingInfoTest {
@Test
public void testIsFullyDecrypted() throws IOException, PGPException {
PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing();
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = TestKeys.getEmilKey();
KeyRingInfo info = api.inspect(secretKeys);
assertTrue(info.isFullyDecrypted());
secretKeys = encryptSecretKeys(secretKeys);
info = PGPainless.inspectKeyRing(secretKeys);
secretKeys = encryptSecretKeys(secretKeys, api);
info = api.inspect(secretKeys);
assertFalse(info.isFullyDecrypted());
}
@Test
public void testIsFullyEncrypted() throws IOException, PGPException {
PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing();
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = TestKeys.getEmilKey();
KeyRingInfo info = api.inspect(secretKeys);
assertFalse(info.isFullyEncrypted());
secretKeys = encryptSecretKeys(secretKeys);
info = PGPainless.inspectKeyRing(secretKeys);
secretKeys = encryptSecretKeys(secretKeys, api);
info = api.inspect(secretKeys);
assertTrue(info.isFullyEncrypted());
}
private static PGPSecretKeyRing encryptSecretKeys(PGPSecretKeyRing secretKeys) throws PGPException {
return PGPainless.modifyKeyRing(secretKeys)
private static OpenPGPKey encryptSecretKeys(OpenPGPKey secretKeys, PGPainless api) throws PGPException {
return api.modify(secretKeys)
.changePassphraseFromOldPassphrase(Passphrase.emptyPassphrase())
.withSecureDefaultSettings()
.toNewPassphrase(Passphrase.fromPassword("sw0rdf1sh"))
@ -160,27 +160,29 @@ public class KeyRingInfoTest {
@Test
public void testGetSecretKey() throws IOException, PGPException {
PGPSecretKeyRing secretKeys = TestKeys.getCryptieSecretKeyRing();
PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys);
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = TestKeys.getCryptieKey();
OpenPGPCertificate publicKeys = secretKeys.toCertificate();
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
KeyRingInfo info = api.inspect(secretKeys);
OpenPGPKey.OpenPGPSecretKey primarySecretKey = info.getSecretKey();
assertNotNull(primarySecretKey);
assertEquals(KeyRingUtils.requirePrimarySecretKeyFrom(secretKeys), primarySecretKey.getPGPSecretKey());
assertEquals(secretKeys.getPrimarySecretKey().getPGPSecretKey(), primarySecretKey.getPGPSecretKey());
info = PGPainless.inspectKeyRing(publicKeys);
info = api.inspect(publicKeys);
assertNull(info.getSecretKey());
}
@Test
public void testGetPublicKey() throws IOException, PGPException {
PGPSecretKeyRing secretKeys = TestKeys.getCryptieSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = TestKeys.getCryptieKey();
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
assertEquals(KeyRingUtils.requirePrimaryPublicKeyFrom(secretKeys), info.getPrimaryKey().getPGPPublicKey());
KeyRingInfo info = api.inspect(secretKeys);
assertEquals(secretKeys.getPrimaryKey().getPGPPublicKey(), info.getPrimaryKey().getPGPPublicKey());
assertEquals(KeyRingUtils.requirePrimarySecretKeyFrom(secretKeys),
KeyRingUtils.requireSecretKeyFrom(secretKeys, secretKeys.getPublicKey().getKeyID()));
assertEquals(secretKeys.getPrimarySecretKey().getPGPSecretKey(),
secretKeys.getPGPSecretKeyRing().getSecretKey(secretKeys.getKeyIdentifier()));
}
@TestTemplate
@ -224,8 +226,8 @@ public class KeyRingInfoTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void testGetKeysWithFlagsAndExpiry() {
PGPSecretKeyRing secretKeys = PGPainless.buildKeyRing()
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = PGPainless.buildKeyRing()
.setPrimaryKey(KeySpec.getBuilder(
KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519), KeyFlag.CERTIFY_OTHER))
.addSubkey(KeySpec.getBuilder(
@ -234,10 +236,9 @@ public class KeyRingInfoTest {
.addSubkey(KeySpec.getBuilder(
KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1), KeyFlag.SIGN_DATA))
.addUserId(UserId.builder().withName("Alice").withEmail("alice@pgpainless.org").build())
.build()
.getPGPSecretKeyRing();
.build();
Iterator<PGPSecretKey> keys = secretKeys.iterator();
Iterator<PGPSecretKey> keys = secretKeys.getPGPSecretKeyRing().iterator();
Date now = DateUtil.now();
Calendar calendar = Calendar.getInstance();
@ -255,11 +256,11 @@ public class KeyRingInfoTest {
PGPSecretKey signingKey = keys.next();
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
secretKeys = PGPainless.modifyKeyRing(secretKeys)
secretKeys = api.modify(secretKeys)
.setExpirationDate(primaryKeyExpiration, protector)
.done();
KeyRingInfo info = new KeyRingInfo(secretKeys);
KeyRingInfo info = api.inspect(secretKeys);
List<OpenPGPCertificate.OpenPGPComponentKey> encryptionKeys = info.getKeysWithKeyFlag(KeyFlag.ENCRYPT_STORAGE);
assertEquals(1, encryptionKeys.size());
@ -354,9 +355,10 @@ public class KeyRingInfoTest {
"crH02GDG8CotAnEHkLTz9GPO80q8mowzBV0EtHsXb4TeAFw5T5Qd0a5I+wk=\n" +
"=Vcb3\n" +
"-----END PGP ARMORED FILE-----\n";
OpenPGPCertificate certificate = PGPainless.getInstance().readKey().parseCertificate(KEY);
PGPainless api = PGPainless.getInstance();
OpenPGPCertificate certificate = api.readKey().parseCertificate(KEY);
KeyRingInfo info = PGPainless.inspectKeyRing(certificate, DateUtil.parseUTCDate("2021-10-10 00:00:00 UTC"));
KeyRingInfo info = api.inspect(certificate, DateUtil.parseUTCDate("2021-10-10 00:00:00 UTC"));
// Subkey is hard revoked
assertFalse(info.isKeyValidlyBound(new KeyIdentifier(5364407983539305061L)));
}
@ -434,14 +436,15 @@ public class KeyRingInfoTest {
"=7Feh\n" +
"-----END PGP ARMORED FILE-----\n";
OpenPGPCertificate certificate = PGPainless.getInstance().readKey().parseCertificate(KEY);
PGPainless api = PGPainless.getInstance();
OpenPGPCertificate certificate = api.readKey().parseCertificate(KEY);
final KeyIdentifier subkeyId = new KeyIdentifier(5364407983539305061L);
KeyRingInfo inspectDuringRevokedPeriod = PGPainless.inspectKeyRing(certificate, DateUtil.parseUTCDate("2019-01-02 00:00:00 UTC"));
KeyRingInfo inspectDuringRevokedPeriod = api.inspect(certificate, DateUtil.parseUTCDate("2019-01-02 00:00:00 UTC"));
assertFalse(inspectDuringRevokedPeriod.isKeyValidlyBound(subkeyId));
assertNotNull(inspectDuringRevokedPeriod.getSubkeyRevocationSignature(subkeyId));
KeyRingInfo inspectAfterRebinding = PGPainless.inspectKeyRing(certificate, DateUtil.parseUTCDate("2020-01-02 00:00:00 UTC"));
KeyRingInfo inspectAfterRebinding = api.inspect(certificate, DateUtil.parseUTCDate("2020-01-02 00:00:00 UTC"));
assertTrue(inspectAfterRebinding.isKeyValidlyBound(subkeyId));
}
@ -518,9 +521,10 @@ public class KeyRingInfoTest {
"=MhJL\n" +
"-----END PGP ARMORED FILE-----\n";
OpenPGPCertificate keys = PGPainless.getInstance().readKey().parseCertificate(KEY);
PGPainless api = PGPainless.getInstance();
OpenPGPCertificate keys = api.readKey().parseCertificate(KEY);
KeyRingInfo info = PGPainless.inspectKeyRing(keys);
KeyRingInfo info = api.inspect(keys);
// Primary key is hard revoked
assertFalse(info.isKeyValidlyBound(keys.getKeyIdentifier()));
assertFalse(info.isFullyEncrypted());
@ -528,49 +532,48 @@ public class KeyRingInfoTest {
@Test
public void getSecretKeyTest() {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().modernKeyRing("Alice")
.getPGPSecretKeyRing();
OpenPGPKey key = new OpenPGPKey(secretKeys);
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey().modernKeyRing("Alice");
KeyRingInfo info = api.inspect(secretKeys);
OpenPgpV4Fingerprint primaryKeyFingerprint = new OpenPgpV4Fingerprint(secretKeys);
OpenPGPKey.OpenPGPSecretKey primaryKey = info.getSecretKey(primaryKeyFingerprint);
assertNotNull(primaryKey);
assertEquals(key.getPrimarySecretKey().getKeyIdentifier(), primaryKey.getKeyIdentifier());
assertEquals(secretKeys.getPrimarySecretKey().getKeyIdentifier(), primaryKey.getKeyIdentifier());
}
@Test
public void testGetLatestKeyCreationDate() throws PGPException, IOException {
PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing();
OpenPGPKey secretKeys = TestKeys.getEmilKey();
Date latestCreationDate = DateUtil.parseUTCDate("2020-01-12 18:01:44 UTC");
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
KeyRingInfo info = PGPainless.getInstance().inspect(secretKeys);
JUtils.assertDateEquals(latestCreationDate, info.getLatestKeyCreationDate());
}
@Test
public void testGetExpirationDateForUse_SPLIT() throws PGPException, IOException {
PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing();
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
OpenPGPKey secretKeys = TestKeys.getEmilKey();
KeyRingInfo info = PGPainless.getInstance().inspect(secretKeys);
assertThrows(IllegalArgumentException.class, () -> info.getExpirationDateForUse(KeyFlag.SPLIT));
}
@Test
public void testGetExpirationDateForUse_SHARED() throws PGPException, IOException {
PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing();
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
OpenPGPKey secretKeys = TestKeys.getEmilKey();
KeyRingInfo info = PGPainless.getInstance().inspect(secretKeys);
assertThrows(IllegalArgumentException.class, () -> info.getExpirationDateForUse(KeyFlag.SHARED));
}
@Test
public void testGetExpirationDateForUse_NoSuchKey() {
PGPSecretKeyRing secretKeys = PGPainless.buildKeyRing()
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = PGPainless.buildKeyRing()
.addUserId("Alice")
.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519), KeyFlag.CERTIFY_OTHER))
.build()
.getPGPSecretKeyRing();
.build();
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
KeyRingInfo info = api.inspect(secretKeys);
assertThrows(NoSuchElementException.class, () -> info.getExpirationDateForUse(KeyFlag.ENCRYPT_COMMS));
}
@ -600,8 +603,8 @@ public class KeyRingInfoTest {
"/+XL+qMMgLHaQ25aA11GVAkC\n" +
"=7gbt\n" +
"-----END PGP PRIVATE KEY BLOCK-----";
OpenPGPKey secretKeys = PGPainless.getInstance().readKey().parseKey(KEY);
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.readKey().parseKey(KEY);
final KeyIdentifier pkid = new KeyIdentifier(6643807985200014832L);
final KeyIdentifier skid1 = new KeyIdentifier(-2328413746552029063L);
final KeyIdentifier skid2 = new KeyIdentifier(-3276877650571760552L);
@ -611,7 +614,7 @@ public class KeyRingInfoTest {
Arrays.asList(CompressionAlgorithm.ZLIB, CompressionAlgorithm.BZIP2, CompressionAlgorithm.ZIP, CompressionAlgorithm.UNCOMPRESSED));
Set<SymmetricKeyAlgorithm> preferredSymmetricAlgorithms = new LinkedHashSet<>(
Arrays.asList(SymmetricKeyAlgorithm.AES_256, SymmetricKeyAlgorithm.AES_192, SymmetricKeyAlgorithm.AES_128));
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
KeyRingInfo info = api.inspect(secretKeys);
// Bob is an invalid userId
assertThrows(NoSuchElementException.class, () -> info.getPreferredSymmetricKeyAlgorithms("Bob"));
@ -694,10 +697,10 @@ public class KeyRingInfoTest {
"C9h35EjDuD+1COXUOoW2B8LX6m2yf8cY72K70QgtGemj7UWhXL5u/wARAQAB\n" +
"=A3B8\n" +
"-----END PGP PUBLIC KEY BLOCK-----\n";
OpenPGPCertificate certificate = PGPainless.getInstance().readKey().parseCertificate(KEY);
PGPainless api = PGPainless.getInstance();
OpenPGPCertificate certificate = api.readKey().parseCertificate(KEY);
OpenPgpV4Fingerprint unboundKey = new OpenPgpV4Fingerprint("D622C916384E0F6D364907E55D918BBD521CCD10");
KeyRingInfo info = PGPainless.inspectKeyRing(certificate);
KeyRingInfo info = api.inspect(certificate);
assertFalse(info.isKeyValidlyBound(unboundKey.getKeyIdentifier()));
@ -773,9 +776,9 @@ public class KeyRingInfoTest {
"qDzPRwEAhdVBeryRUcwjgwHX0xmMFK7vLkdonn8BR2++nXBO2g8=\n" +
"=ZRAy\n" +
"-----END PGP PRIVATE KEY BLOCK-----\n";
OpenPGPKey secretKeys = PGPainless.getInstance().readKey().parseKey(KEY);
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.readKey().parseKey(KEY);
KeyRingInfo info = api.inspect(secretKeys);
List<String> emails = info.getEmailAddresses();
assertEquals(emails, Arrays.asList("alice@email.tld", "alice@pgpainless.org", "alice@openpgp.org", "alice@rfc4880.spec"));
@ -805,9 +808,9 @@ public class KeyRingInfoTest {
"J5wP\n" +
"=nFoO\n" +
"-----END PGP PUBLIC KEY BLOCK-----";
OpenPGPCertificate cert = PGPainless.getInstance().readKey().parseCertificate(CERT);
KeyRingInfo info = PGPainless.inspectKeyRing(cert);
PGPainless api = PGPainless.getInstance();
OpenPGPCertificate cert = api.readKey().parseCertificate(CERT);
KeyRingInfo info = api.inspect(cert);
assertTrue(info.isUsableForEncryption());
}
@ -833,9 +836,9 @@ public class KeyRingInfoTest {
"2XO/hpB2T8VXFfFKwj7U9LGkX+ciLg==\n" +
"=etPP\n" +
"-----END PGP PUBLIC KEY BLOCK-----";
OpenPGPCertificate publicKeys = PGPainless.getInstance().readKey().parseCertificate(CERT);
KeyRingInfo info = PGPainless.inspectKeyRing(publicKeys);
PGPainless api = PGPainless.getInstance();
OpenPGPCertificate publicKeys = api.readKey().parseCertificate(CERT);
KeyRingInfo info = api.inspect(publicKeys);
assertTrue(info.isUsableForEncryption(EncryptionPurpose.COMMUNICATIONS));
assertTrue(info.isUsableForEncryption(EncryptionPurpose.ANY));
@ -870,8 +873,9 @@ public class KeyRingInfoTest {
"AQCjeV+3VT+u1movwIYv4XkzB6gB+B2C+DK9nvG5sXZhBg==\n" +
"=uqmO\n" +
"-----END PGP PUBLIC KEY BLOCK-----";
OpenPGPCertificate publicKeys = PGPainless.getInstance().readKey().parseCertificate(CERT);
KeyRingInfo info = PGPainless.inspectKeyRing(publicKeys);
PGPainless api = PGPainless.getInstance();
OpenPGPCertificate publicKeys = api.readKey().parseCertificate(CERT);
KeyRingInfo info = api.inspect(publicKeys);
assertFalse(info.isUsableForEncryption());
assertFalse(info.isUsableForEncryption(EncryptionPurpose.ANY));

View file

@ -7,7 +7,7 @@ package org.pgpainless.key.info;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.key.protection.SecretKeyRingProtector;
@ -16,13 +16,13 @@ public class PrimaryUserIdTest {
@Test
public void testGetPrimaryUserId() throws PGPException {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().simpleEcKeyRing("alice@wonderland.lit")
.getPGPSecretKeyRing();
secretKeys = PGPainless.modifyKeyRing(secretKeys)
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey().simpleEcKeyRing("alice@wonderland.lit");
secretKeys = api.modify(secretKeys)
.addUserId("mad_alice@wonderland.lit", SecretKeyRingProtector.unprotectedKeys())
.done();
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
KeyRingInfo info = api.inspect(secretKeys);
assertEquals("alice@wonderland.lit", info.getPrimaryUserId());
}
}

View file

@ -15,11 +15,12 @@ import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import org.bouncycastle.bcpg.KeyIdentifier;
import org.bouncycastle.bcpg.SignatureSubpacketTags;
import org.bouncycastle.bcpg.sig.RevocationReason;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.KeyFlag;
@ -37,7 +38,8 @@ public class UserIdRevocationTest {
@Test
public void testRevocationWithoutRevocationAttributes() throws PGPException {
PGPSecretKeyRing secretKeys = PGPainless.buildKeyRing()
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = PGPainless.buildKeyRing()
.setPrimaryKey(KeySpec.getBuilder(
KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519),
KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER))
@ -45,28 +47,27 @@ public class UserIdRevocationTest {
KeyType.XDH_LEGACY(XDHLegacySpec._X25519), KeyFlag.ENCRYPT_COMMS))
.addUserId("primary@key.id")
.addUserId("secondary@key.id")
.build()
.getPGPSecretKeyRing();
.build();
// make a copy with revoked subkey
PGPSecretKeyRing revoked = PGPainless.modifyKeyRing(secretKeys)
OpenPGPKey revoked = api.modify(secretKeys)
.revokeUserId("secondary@key.id", new UnprotectedKeysProtector())
.done();
KeyRingInfo info = PGPainless.inspectKeyRing(revoked);
KeyRingInfo info = api.inspect(revoked);
List<String> userIds = info.getUserIds();
assertEquals(Arrays.asList("primary@key.id", "secondary@key.id"), userIds);
assertTrue(info.isUserIdValid("primary@key.id"));
assertFalse(info.isUserIdValid("sedondary@key.id"));
assertFalse(info.isUserIdValid("tertiary@key.id"));
info = PGPainless.inspectKeyRing(secretKeys);
info = api.inspect(secretKeys);
assertTrue(info.isUserIdValid("secondary@key.id")); // key on original secret key ring is still valid
revoked = PGPainless.modifyKeyRing(secretKeys)
revoked = api.modify(secretKeys)
.revokeUserId("secondary@key.id", new UnprotectedKeysProtector())
.done();
info = PGPainless.inspectKeyRing(revoked);
info = api.inspect(revoked);
userIds = info.getUserIds();
assertEquals(Arrays.asList("primary@key.id", "secondary@key.id"), userIds);
assertTrue(info.isUserIdValid("primary@key.id"));
@ -76,23 +77,23 @@ public class UserIdRevocationTest {
@Test
public void testRevocationWithRevocationReason() {
PGPSecretKeyRing secretKeys = PGPainless.buildKeyRing()
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = PGPainless.buildKeyRing()
.setPrimaryKey(KeySpec.getBuilder(
KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519),
KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER))
.addSubkey(KeySpec.getBuilder(KeyType.XDH_LEGACY(XDHLegacySpec._X25519), KeyFlag.ENCRYPT_COMMS))
.addUserId("primary@key.id")
.addUserId("secondary@key.id")
.build()
.getPGPSecretKeyRing();
.build();
secretKeys = PGPainless.modifyKeyRing(secretKeys)
secretKeys = api.modify(secretKeys)
.revokeUserId("secondary@key.id", new UnprotectedKeysProtector(),
RevocationAttributes.createCertificateRevocation()
.withReason(RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID)
.withDescription("I lost my mail password"))
.done();
KeyRingInfo info = new KeyRingInfo(secretKeys);
KeyRingInfo info = api.inspect(secretKeys);
PGPSignature signature = info.getUserIdRevocation("secondary@key.id");
assertNotNull(signature);
@ -104,31 +105,31 @@ public class UserIdRevocationTest {
@Test
public void unknownKeyThrowsIllegalArgumentException() throws IOException, PGPException {
PGPSecretKeyRing secretKeys = TestKeys.getCryptieSecretKeyRing();
OpenPGPKey secretKeys = TestKeys.getCryptieKey();
SecretKeyRingProtector protector = PasswordBasedSecretKeyRingProtector
.forKey(secretKeys.getSecretKey(), TestKeys.CRYPTIE_PASSPHRASE);
.forKey(secretKeys, TestKeys.CRYPTIE_PASSPHRASE);
assertThrows(NoSuchElementException.class, () -> PGPainless.modifyKeyRing(secretKeys)
.revokeSubKey(1L, protector));
assertThrows(NoSuchElementException.class, () -> PGPainless.getInstance().modify(secretKeys)
.revokeSubKey(new KeyIdentifier(1L), protector));
}
@Test
public void unknownUserIdThrowsNoSuchElementException() throws IOException, PGPException {
PGPSecretKeyRing secretKeys = TestKeys.getCryptieSecretKeyRing();
OpenPGPKey secretKeys = TestKeys.getCryptieKey();
SecretKeyRingProtector protector = PasswordBasedSecretKeyRingProtector
.forKey(secretKeys.getSecretKey(), TestKeys.CRYPTIE_PASSPHRASE);
.forKey(secretKeys, TestKeys.CRYPTIE_PASSPHRASE);
assertThrows(NoSuchElementException.class, () -> PGPainless.modifyKeyRing(secretKeys)
assertThrows(NoSuchElementException.class, () -> PGPainless.getInstance().modify(secretKeys)
.revokeUserId("invalid@user.id", protector));
}
@Test
public void invalidRevocationReasonThrowsIllegalArgumentException() throws IOException, PGPException {
PGPSecretKeyRing secretKeys = TestKeys.getCryptieSecretKeyRing();
OpenPGPKey secretKeys = TestKeys.getCryptieKey();
SecretKeyRingProtector protector = PasswordBasedSecretKeyRingProtector
.forKey(secretKeys.getSecretKey(), TestKeys.CRYPTIE_PASSPHRASE);
.forKey(secretKeys, TestKeys.CRYPTIE_PASSPHRASE);
assertThrows(IllegalArgumentException.class, () -> PGPainless.modifyKeyRing(secretKeys)
assertThrows(IllegalArgumentException.class, () -> PGPainless.getInstance().modify(secretKeys)
.revokeUserId("cryptie@encrypted.key", protector,
RevocationAttributes.createKeyRevocation().withReason(RevocationAttributes.Reason.KEY_RETIRED)
.withDescription("This is not a valid certification revocation reason.")));

View file

@ -16,8 +16,7 @@ import java.util.List;
import org.bouncycastle.bcpg.KeyIdentifier;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
import org.pgpainless.PGPainless;
@ -39,14 +38,15 @@ public class AddSubKeyTest {
@ExtendWith(TestAllImplementations.class)
public void testAddSubKey()
throws IOException, PGPException {
PGPSecretKeyRing secretKeys = TestKeys.getCryptieSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = TestKeys.getCryptieKey();
List<KeyIdentifier> keyIdentifiersBefore = new ArrayList<>();
for (Iterator<PGPPublicKey> it = secretKeys.getPublicKeys(); it.hasNext(); ) {
for (Iterator<PGPPublicKey> it = secretKeys.getPGPSecretKeyRing().getPublicKeys(); it.hasNext(); ) {
keyIdentifiersBefore.add(it.next().getKeyIdentifier());
}
secretKeys = PGPainless.modifyKeyRing(secretKeys)
secretKeys = api.modify(secretKeys)
.addSubKey(
KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256), KeyFlag.SIGN_DATA).build(),
Passphrase.fromPassword("subKeyPassphrase"),
@ -54,7 +54,7 @@ public class AddSubKeyTest {
.done();
List<KeyIdentifier> keyIdentifiersAfter = new ArrayList<>();
for (Iterator<PGPPublicKey> it = secretKeys.getPublicKeys(); it.hasNext(); ) {
for (Iterator<PGPPublicKey> it = secretKeys.getPGPSecretKeyRing().getPublicKeys(); it.hasNext(); ) {
keyIdentifiersAfter.add(it.next().getKeyIdentifier());
}
assertNotEquals(keyIdentifiersAfter, keyIdentifiersBefore);
@ -62,12 +62,12 @@ public class AddSubKeyTest {
keyIdentifiersAfter.removeAll(keyIdentifiersBefore);
KeyIdentifier subKeyIdentifier = keyIdentifiersAfter.get(0);
PGPSecretKey subKey = secretKeys.getSecretKey(subKeyIdentifier);
OpenPGPKey.OpenPGPSecretKey subKey = secretKeys.getSecretKey(subKeyIdentifier);
SecretKeyRingProtector protector = SecretKeyRingProtector.unlockEachKeyWith(
Passphrase.fromPassword("subKeyPassphrase"), secretKeys);
UnlockSecretKey.unlockSecretKey(subKey, protector);
KeyRingInfo info = new KeyRingInfo(secretKeys);
KeyRingInfo info = api.inspect(secretKeys);
assertEquals(Collections.singletonList(KeyFlag.SIGN_DATA), info.getKeyFlagsOf(subKeyIdentifier));
}
}

View file

@ -14,9 +14,9 @@ import java.util.List;
import org.bouncycastle.bcpg.sig.NotationData;
import org.bouncycastle.openpgp.PGPKeyPair;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.JUtils;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
@ -38,11 +38,11 @@ public class AddSubkeyWithModifiedBindingSignatureSubpacketsTest {
@Test
public void bindEncryptionSubkeyAndModifyBindingSignatureHashedSubpackets() {
PGPainless api = PGPainless.getInstance();
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.modernKeyRing("Alice <alice@pgpainless.org>")
.getPGPSecretKeyRing();
KeyRingInfo before = PGPainless.inspectKeyRing(secretKeys);
OpenPGPKey secretKeys = api.generateKey()
.modernKeyRing("Alice <alice@pgpainless.org>");
KeyRingInfo before = api.inspect(secretKeys);
List<OpenPGPCertificate.OpenPGPComponentKey> signingKeysBefore = before.getSigningSubkeys();
PGPKeyPair secretSubkey = KeyRingBuilder.generateKeyPair(
@ -50,7 +50,7 @@ public class AddSubkeyWithModifiedBindingSignatureSubpacketsTest {
OpenPGPKeyVersion.v4);
long secondsUntilExpiration = 1000;
secretKeys = PGPainless.modifyKeyRing(secretKeys)
secretKeys = api.modify(secretKeys)
.addSubKey(secretSubkey, new SelfSignatureSubpackets.Callback() {
@Override
public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) {
@ -60,7 +60,7 @@ public class AddSubkeyWithModifiedBindingSignatureSubpacketsTest {
}, SecretKeyRingProtector.unprotectedKeys(), protector, KeyFlag.SIGN_DATA)
.done();
KeyRingInfo after = PGPainless.inspectKeyRing(secretKeys);
KeyRingInfo after = api.inspect(secretKeys);
List<OpenPGPCertificate.OpenPGPComponentKey> signingKeysAfter = after.getSigningSubkeys();
signingKeysAfter.removeAll(signingKeysBefore);
assertFalse(signingKeysAfter.isEmpty());

View file

@ -37,31 +37,31 @@ public class AddUserIdTest {
@ExtendWith(TestAllImplementations.class)
public void addUserIdToExistingKeyRing()
throws PGPException {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.simpleEcKeyRing("alice@wonderland.lit", "rabb1th0le")
.getPGPSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey()
.simpleEcKeyRing("alice@wonderland.lit", "rabb1th0le");
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
KeyRingInfo info = api.inspect(secretKeys);
Iterator<String> userIds = info.getValidUserIds().iterator();
assertEquals("alice@wonderland.lit", userIds.next());
assertFalse(userIds.hasNext());
SecretKeyRingProtector protector = PasswordBasedSecretKeyRingProtector.forKey(secretKeys, Passphrase.fromPassword("rabb1th0le"));
secretKeys = PGPainless.modifyKeyRing(secretKeys)
secretKeys = api.modify(secretKeys)
.addUserId("cheshirecat@wonderland.lit", protector)
.done();
info = PGPainless.inspectKeyRing(secretKeys);
info = api.inspect(secretKeys);
userIds = info.getValidUserIds().iterator();
assertEquals("alice@wonderland.lit", userIds.next());
assertEquals("cheshirecat@wonderland.lit", userIds.next());
assertFalse(userIds.hasNext());
secretKeys = PGPainless.modifyKeyRing(secretKeys)
secretKeys = api.modify(secretKeys)
.revokeUserId("cheshirecat@wonderland.lit", protector)
.done();
info = PGPainless.inspectKeyRing(secretKeys);
info = api.inspect(secretKeys);
userIds = info.getValidUserIds().iterator();
assertEquals("alice@wonderland.lit", userIds.next());
assertFalse(userIds.hasNext());
@ -96,20 +96,21 @@ public class AddUserIdTest {
"=bk4o\r\n" +
"-----END PGP PRIVATE KEY BLOCK-----\r\n";
OpenPGPKey key = PGPainless.getInstance().readKey().parseKey(ARMORED_PRIVATE_KEY);
PGPSecretKeyRing secretKeys = key.getPGPSecretKeyRing();
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
PGPainless api = PGPainless.getInstance();
OpenPGPKey key = api.readKey().parseKey(ARMORED_PRIVATE_KEY);
KeyRingInfo info = api.inspect(key);
Iterator<String> userIds = info.getValidUserIds().iterator();
assertEquals("<user@example.com>", userIds.next());
assertFalse(userIds.hasNext());
SecretKeyRingProtector protector = new UnprotectedKeysProtector();
secretKeys = PGPainless.modifyKeyRing(secretKeys)
key = api.modify(key)
.revokeUserId("<user@example.com>", protector)
.addUserId("cheshirecat@wonderland.lit", protector)
.done();
info = PGPainless.inspectKeyRing(secretKeys);
info = api.inspect(key);
userIds = info.getValidUserIds().iterator();
assertEquals("cheshirecat@wonderland.lit", userIds.next());
assertFalse(userIds.hasNext());
@ -118,17 +119,17 @@ public class AddUserIdTest {
@Test
public void addNewPrimaryUserIdTest() {
Date now = new Date();
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.modernKeyRing("Alice")
.getPGPSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey()
.modernKeyRing("Alice");
UserId bob = UserId.builder().withName("Bob").noEmail().noComment().build();
assertNotEquals("Bob", PGPainless.inspectKeyRing(secretKeys).getPrimaryUserId());
assertNotEquals("Bob", api.inspect(secretKeys).getPrimaryUserId());
secretKeys = PGPainless.modifyKeyRing(secretKeys, DateExtensionsKt.plusSeconds(now, 1))
secretKeys = api.modify(secretKeys, DateExtensionsKt.plusSeconds(now, 1))
.addPrimaryUserId(bob, SecretKeyRingProtector.unprotectedKeys())
.done();
assertEquals("Bob", PGPainless.inspectKeyRing(secretKeys, DateExtensionsKt.plusSeconds(now, 2)).getPrimaryUserId());
assertEquals("Bob", api.inspect(secretKeys, DateExtensionsKt.plusSeconds(now, 2)).getPrimaryUserId());
}
}

View file

@ -7,7 +7,7 @@ package org.pgpainless.key.modification;
import java.io.IOException;
import java.util.Date;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.JUtils;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
@ -138,7 +138,8 @@ public class ChangeExpirationOnKeyWithDifferentSignatureTypesTest {
@ExtendWith(TestAllImplementations.class)
public void setExpirationDate_keyHasSigClass10()
throws IOException {
PGPSecretKeyRing keys = PGPainless.readKeyRing().secretKeyRing(keyWithGenericCertification);
PGPainless api = PGPainless.getInstance();
OpenPGPKey keys = api.readKey().parseKey(keyWithGenericCertification);
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
executeTestForKeys(keys, protector);
}
@ -147,20 +148,23 @@ public class ChangeExpirationOnKeyWithDifferentSignatureTypesTest {
@ExtendWith(TestAllImplementations.class)
public void setExpirationDate_keyHasSigClass12()
throws IOException {
PGPSecretKeyRing keys = PGPainless.readKeyRing().secretKeyRing(keyWithCasualCertification);
PGPainless api = PGPainless.getInstance();
OpenPGPKey keys = api.readKey().parseKey(keyWithCasualCertification);
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
executeTestForKeys(keys, protector);
}
private void executeTestForKeys(PGPSecretKeyRing keys, SecretKeyRingProtector protector) {
private void executeTestForKeys(OpenPGPKey keys, SecretKeyRingProtector protector) {
PGPainless api = PGPainless.getInstance();
Date expirationDate = new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 14);
// round date for test stability
expirationDate = DateUtil.toSecondsPrecision(expirationDate);
PGPSecretKeyRing modded = PGPainless.modifyKeyRing(keys)
OpenPGPKey modded = api.modify(keys)
.setExpirationDate(expirationDate, protector)
.done();
JUtils.assertDateEquals(expirationDate, PGPainless.inspectKeyRing(modded).getPrimaryKeyExpirationDate());
JUtils.assertDateEquals(expirationDate, api.inspect(modded).getPrimaryKeyExpirationDate());
}
}

View file

@ -14,7 +14,7 @@ import java.util.Calendar;
import java.util.Date;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.JUtils;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
@ -35,27 +35,28 @@ public class ChangeExpirationTest {
@ExtendWith(TestAllImplementations.class)
public void setExpirationDateAndThenUnsetIt_OnPrimaryKey()
throws PGPException, IOException {
PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing();
KeyRingInfo sInfo = PGPainless.inspectKeyRing(secretKeys);
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = TestKeys.getEmilKey();
KeyRingInfo sInfo = api.inspect(secretKeys);
assertNull(sInfo.getPrimaryKeyExpirationDate());
assertNull(sInfo.getSubkeyExpirationDate(subKeyFingerprint));
Date now = new Date();
Date date = DateUtil.parseUTCDate("2020-11-27 16:10:32 UTC");
secretKeys = PGPainless.modifyKeyRing(secretKeys)
secretKeys = api.modify(secretKeys)
.setExpirationDate(date, new UnprotectedKeysProtector()).done();
sInfo = PGPainless.inspectKeyRing(secretKeys);
sInfo = api.inspect(secretKeys);
assertNotNull(sInfo.getPrimaryKeyExpirationDate());
assertEquals(date.getTime(), sInfo.getPrimaryKeyExpirationDate().getTime());
// subkey unchanged
assertNull(sInfo.getSubkeyExpirationDate(subKeyFingerprint));
Date t1 = new Date(now.getTime() + 1000 * 60 * 60);
secretKeys = PGPainless.modifyKeyRing(secretKeys, t1)
secretKeys = api.modify(secretKeys, t1)
.setExpirationDate(null, new UnprotectedKeysProtector()).done();
sInfo = PGPainless.inspectKeyRing(secretKeys, t1);
sInfo = api.inspect(secretKeys, t1);
assertNull(sInfo.getPrimaryKeyExpirationDate());
assertNull(sInfo.getSubkeyExpirationDate(subKeyFingerprint));
}
@ -64,9 +65,9 @@ public class ChangeExpirationTest {
@ExtendWith(TestAllImplementations.class)
public void setExpirationDateAndThenUnsetIt_OnSubkey()
throws PGPException, IOException {
PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing();
KeyRingInfo sInfo = PGPainless.inspectKeyRing(secretKeys);
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = TestKeys.getEmilKey();
KeyRingInfo sInfo = api.inspect(secretKeys);
assertNull(sInfo.getPrimaryKeyExpirationDate());
@ -76,42 +77,43 @@ public class ChangeExpirationTest {
calendar.add(Calendar.DATE, 5);
Date expiration = calendar.getTime(); // in 5 days
secretKeys = PGPainless.modifyKeyRing(secretKeys)
secretKeys = api.modify(secretKeys)
.setExpirationDate(expiration, new UnprotectedKeysProtector()).done();
sInfo = PGPainless.inspectKeyRing(secretKeys);
sInfo = api.inspect(secretKeys);
assertNotNull(sInfo.getPrimaryKeyExpirationDate());
JUtils.assertDateEquals(expiration, sInfo.getPrimaryKeyExpirationDate());
Date t1 = new Date(now.getTime() + 1000 * 60 * 60);
secretKeys = PGPainless.modifyKeyRing(secretKeys, t1)
secretKeys = api.modify(secretKeys, t1)
.setExpirationDate(null, new UnprotectedKeysProtector()).done();
sInfo = PGPainless.inspectKeyRing(secretKeys, t1);
sInfo = api.inspect(secretKeys, t1);
assertNull(sInfo.getPrimaryKeyExpirationDate());
}
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void testExtremeExpirationDates() throws PGPException, IOException {
PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = TestKeys.getEmilKey();
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
// seconds from 2021 to 2199 will overflow 32bit integers
Date farAwayExpiration = DateUtil.parseUTCDate("2199-01-01 00:00:00 UTC");
final PGPSecretKeyRing finalKeys = secretKeys;
final OpenPGPKey finalKeys = secretKeys;
assertThrows(IllegalArgumentException.class, () ->
PGPainless.modifyKeyRing(finalKeys)
api.modify(finalKeys)
.setExpirationDate(farAwayExpiration, protector)
.done());
Date notSoFarAwayExpiration = DateUtil.parseUTCDate("2100-01-01 00:00:00 UTC");
secretKeys = PGPainless.modifyKeyRing(secretKeys)
secretKeys = api.modify(secretKeys)
.setExpirationDate(notSoFarAwayExpiration, protector)
.done();
Date actualExpiration = PGPainless.inspectKeyRing(secretKeys)
Date actualExpiration = api.inspect(secretKeys)
.getPrimaryKeyExpirationDate();
JUtils.assertDateEquals(notSoFarAwayExpiration, actualExpiration);
}

View file

@ -5,8 +5,8 @@
package org.pgpainless.key.modification;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.key.info.KeyRingInfo;
@ -26,13 +26,14 @@ public class ChangePrimaryUserIdAndExpirationDatesTest {
@Test
public void generateA_primaryB_revokeA_cantSecondaryA()
throws PGPException {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.modernKeyRing("A")
.getPGPSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey()
.modernKeyRing("A");
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
Date now = new Date();
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys, now);
KeyRingInfo info = api.inspect(secretKeys, now);
assertFalse(info.isHardRevoked("A"));
assertFalse(info.isHardRevoked("B"));
assertIsPrimaryUserId("A", info);
@ -41,10 +42,10 @@ public class ChangePrimaryUserIdAndExpirationDatesTest {
// One hour later
Date oneHourLater = new Date(now.getTime() + millisInHour);
secretKeys = PGPainless.modifyKeyRing(secretKeys, oneHourLater)
secretKeys = api.modify(secretKeys, oneHourLater)
.addPrimaryUserId("B", protector)
.done();
info = PGPainless.inspectKeyRing(secretKeys, oneHourLater);
info = api.inspect(secretKeys, oneHourLater);
assertIsPrimaryUserId("B", info);
assertIsNotPrimaryUserId("A", info);
@ -52,10 +53,10 @@ public class ChangePrimaryUserIdAndExpirationDatesTest {
// Two hours later
Date twoHoursLater = new Date(now.getTime() + 2 * millisInHour);
secretKeys = PGPainless.modifyKeyRing(secretKeys, twoHoursLater)
secretKeys = api.modify(secretKeys, twoHoursLater)
.revokeUserId("A", protector) // hard revoke A
.done();
info = PGPainless.inspectKeyRing(secretKeys, twoHoursLater);
info = api.inspect(secretKeys, twoHoursLater);
assertTrue(info.isHardRevoked("A"));
assertFalse(info.isHardRevoked("B"));
@ -65,71 +66,71 @@ public class ChangePrimaryUserIdAndExpirationDatesTest {
// Three hours later
Date threeHoursLater = new Date(now.getTime() + 3 * millisInHour);
PGPSecretKeyRing finalSecretKeys = secretKeys;
OpenPGPKey finalSecretKeys = secretKeys;
assertThrows(IllegalArgumentException.class, () ->
PGPainless.modifyKeyRing(finalSecretKeys, threeHoursLater).addUserId("A", protector));
api.modify(finalSecretKeys, threeHoursLater).addUserId("A", protector));
}
@Test
public void generateA_primaryExpire_isExpired() {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.modernKeyRing("A")
.getPGPSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey()
.modernKeyRing("A");
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
KeyRingInfo info = api.inspect(secretKeys);
assertIsPrimaryUserId("A", info);
Date now = new Date();
Date later = new Date(now.getTime() + millisInHour);
secretKeys = PGPainless.modifyKeyRing(secretKeys, now)
secretKeys = api.modify(secretKeys, now)
.setExpirationDate(later, protector) // expire the whole key
.done();
Date evenLater = new Date(now.getTime() + 2 * millisInHour);
info = PGPainless.inspectKeyRing(secretKeys, evenLater);
info = api.inspect(secretKeys, evenLater);
assertFalse(info.isUserIdValid("A")); // is expired by now
}
@Test
public void generateA_primaryB_primaryExpire_bIsStillPrimary() {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.modernKeyRing("A")
.getPGPSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey()
.modernKeyRing("A");
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
Date now = new Date();
// Generate key with primary user-id A
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
KeyRingInfo info = api.inspect(secretKeys);
assertIsPrimaryUserId("A", info);
// later set primary user-id to B
Date t1 = new Date(now.getTime() + millisInHour);
secretKeys = PGPainless.modifyKeyRing(secretKeys, t1)
secretKeys = api.modify(secretKeys, t1)
.addPrimaryUserId("B", protector)
.done();
info = PGPainless.inspectKeyRing(secretKeys, t1);
info = api.inspect(secretKeys, t1);
assertIsPrimaryUserId("B", info);
assertIsNotPrimaryUserId("A", info);
// Even later expire the whole key
Date t2 = new Date(now.getTime() + 2 * millisInHour);
Date expiration = new Date(now.getTime() + 10 * millisInHour);
secretKeys = PGPainless.modifyKeyRing(secretKeys, t2)
secretKeys = api.modify(secretKeys, t2)
.setExpirationDate(expiration, protector) // expire the whole key in 1 hour
.done();
Date t3 = new Date(now.getTime() + 3 * millisInHour);
info = PGPainless.inspectKeyRing(secretKeys, t3);
info = api.inspect(secretKeys, t3);
assertIsValid("A", info);
assertIsValid("B", info);
assertIsPrimaryUserId("B", info);
assertIsNotPrimaryUserId("A", info);
info = PGPainless.inspectKeyRing(secretKeys, expiration);
info = api.inspect(secretKeys, expiration);
assertIsPrimaryUserId("B", info); // B is still primary, even though
assertFalse(info.isUserIdValid("A")); // key is expired by now
assertFalse(info.isUserIdValid("B"));
@ -137,23 +138,23 @@ public class ChangePrimaryUserIdAndExpirationDatesTest {
@Test
public void generateA_expire_certify() {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().modernKeyRing("A")
.getPGPSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey().modernKeyRing("A");
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
Date now = new Date();
Date t1 = new Date(now.getTime() + millisInHour);
secretKeys = PGPainless.modifyKeyRing(secretKeys, now)
secretKeys = api.modify(secretKeys, now)
.setExpirationDate(t1, protector)
.done();
Date t2 = new Date(now.getTime() + 2 * millisInHour);
Date t4 = new Date(now.getTime() + 4 * millisInHour);
secretKeys = PGPainless.modifyKeyRing(secretKeys, t2)
secretKeys = api.modify(secretKeys, t2)
.setExpirationDate(t4, protector)
.done();
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
KeyRingInfo info = api.inspect(secretKeys);
assertIsValid("A", info);
assertIsPrimaryUserId("A", info);
}
@ -161,28 +162,28 @@ public class ChangePrimaryUserIdAndExpirationDatesTest {
@Test
public void generateA_expire_primaryB_expire_isPrimaryB()
throws PGPException {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().modernKeyRing("A")
.getPGPSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey().modernKeyRing("A");
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
Date now = new Date();
Date t1 = new Date(now.getTime() + millisInHour);
secretKeys = PGPainless.modifyKeyRing(secretKeys, t1)
secretKeys = api.modify(secretKeys, t1)
.setExpirationDate(t1, protector)
.done();
Date t2 = new Date(now.getTime() + 2 * millisInHour);
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys, t2);
KeyRingInfo info = api.inspect(secretKeys, t2);
assertIsPrimaryUserId("A", info);
assertIsNotValid("A", info); // A is expired
secretKeys = PGPainless.modifyKeyRing(secretKeys, t2)
secretKeys = api.modify(secretKeys, t2)
.addPrimaryUserId("B", protector)
.done();
Date t3 = new Date(now.getTime() + 3 * millisInHour);
info = PGPainless.inspectKeyRing(secretKeys, t3);
info = api.inspect(secretKeys, t3);
assertIsPrimaryUserId("B", info);
assertIsNotValid("B", info); // A and B are still expired
@ -190,19 +191,19 @@ public class ChangePrimaryUserIdAndExpirationDatesTest {
Date t4 = new Date(now.getTime() + 4 * millisInHour);
Date t5 = new Date(now.getTime() + 5 * millisInHour);
secretKeys = PGPainless.modifyKeyRing(secretKeys, t3)
secretKeys = api.modify(secretKeys, t3)
.setExpirationDate(t5, protector)
.done();
info = PGPainless.inspectKeyRing(secretKeys, t4);
info = api.inspect(secretKeys, t4);
assertIsValid("B", info);
assertIsValid("A", info); // A got re-validated when changing exp date
assertIsPrimaryUserId("B", info);
secretKeys = PGPainless.modifyKeyRing(secretKeys, t4)
secretKeys = api.modify(secretKeys, t4)
.addUserId("A", protector) // re-certify A as non-primary user-id
.done();
info = PGPainless.inspectKeyRing(secretKeys, t4);
info = api.inspect(secretKeys, t4);
assertIsValid("B", info);
assertIsValid("A", info);

View file

@ -15,8 +15,8 @@ import java.util.Iterator;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.api.OpenPGPImplementation;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.TestTemplate;
@ -35,8 +35,8 @@ import org.pgpainless.util.Passphrase;
public class ChangeSecretKeyRingPassphraseTest {
private final PGPSecretKeyRing keyRing = PGPainless.generateKeyRing().simpleEcKeyRing("password@encryp.ted", "weakPassphrase")
.getPGPSecretKeyRing();
private final OpenPGPKey keyRing = PGPainless.getInstance()
.generateKey().simpleEcKeyRing("password@encryp.ted", "weakPassphrase");
public ChangeSecretKeyRingPassphraseTest() {
}
@ -44,66 +44,64 @@ public class ChangeSecretKeyRingPassphraseTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void changePassphraseOfWholeKeyRingTest() throws PGPException {
PGPSecretKeyRing secretKeys = PGPainless.modifyKeyRing(keyRing)
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.modify(keyRing)
.changePassphraseFromOldPassphrase(Passphrase.fromPassword("weakPassphrase"))
.withSecureDefaultSettings()
.toNewPassphrase(Passphrase.fromPassword("1337p455phr453"))
.done();
PGPSecretKeyRing changedPassphraseKeyRing = secretKeys;
assertEquals(KeyRingProtectionSettings.secureDefaultSettings().getEncryptionAlgorithm().getAlgorithmId(),
changedPassphraseKeyRing.getSecretKey().getKeyEncryptionAlgorithm());
secretKeys.getPGPSecretKeyRing().getSecretKey().getKeyEncryptionAlgorithm());
assertThrows(PGPException.class, () ->
signDummyMessageWithKeysAndPassphrase(changedPassphraseKeyRing, Passphrase.emptyPassphrase()),
signDummyMessageWithKeysAndPassphrase(api, secretKeys, Passphrase.emptyPassphrase()),
"Unlocking secret key ring with empty passphrase MUST fail.");
assertThrows(PGPException.class, () ->
signDummyMessageWithKeysAndPassphrase(changedPassphraseKeyRing, Passphrase.fromPassword("weakPassphrase")),
signDummyMessageWithKeysAndPassphrase(api, secretKeys, Passphrase.fromPassword("weakPassphrase")),
"Unlocking secret key ring with old passphrase MUST fail.");
assertDoesNotThrow(() -> signDummyMessageWithKeysAndPassphrase(changedPassphraseKeyRing, Passphrase.fromPassword("1337p455phr453")),
assertDoesNotThrow(() -> signDummyMessageWithKeysAndPassphrase(api, secretKeys, Passphrase.fromPassword("1337p455phr453")),
"Unlocking the secret key ring with the new passphrase MUST succeed.");
}
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void changePassphraseOfWholeKeyRingToEmptyPassphrase() throws PGPException, IOException {
PGPSecretKeyRing secretKeys = PGPainless.modifyKeyRing(keyRing)
PGPainless api = PGPainless.getInstance();
OpenPGPKey changedPassphraseKeyRing = api.modify(keyRing)
.changePassphraseFromOldPassphrase(Passphrase.fromPassword("weakPassphrase"))
.withSecureDefaultSettings()
.toNoPassphrase()
.done();
PGPSecretKeyRing changedPassphraseKeyRing = secretKeys;
assertEquals(SymmetricKeyAlgorithm.NULL.getAlgorithmId(),
changedPassphraseKeyRing.getSecretKey().getKeyEncryptionAlgorithm());
changedPassphraseKeyRing.getPGPSecretKeyRing().getSecretKey().getKeyEncryptionAlgorithm());
signDummyMessageWithKeysAndPassphrase(changedPassphraseKeyRing, Passphrase.emptyPassphrase());
signDummyMessageWithKeysAndPassphrase(api, changedPassphraseKeyRing, Passphrase.emptyPassphrase());
}
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void changePassphraseOfSingleSubkeyToNewPassphrase() throws PGPException {
Iterator<PGPSecretKey> keys = keyRing.getSecretKeys();
PGPainless api = PGPainless.getInstance();
Iterator<PGPSecretKey> keys = keyRing.getPGPSecretKeyRing().getSecretKeys();
PGPSecretKey primaryKey = keys.next();
PGPSecretKey subKey = keys.next();
extractPrivateKey(primaryKey, Passphrase.fromPassword("weakPassphrase"));
extractPrivateKey(subKey, Passphrase.fromPassword("weakPassphrase"));
PGPSecretKeyRing secretKeys = PGPainless.modifyKeyRing(keyRing)
OpenPGPKey secretKeys = api.modify(keyRing)
.changeSubKeyPassphraseFromOldPassphrase(subKey.getPublicKey().getKeyIdentifier(),
Passphrase.fromPassword("weakPassphrase"))
.withSecureDefaultSettings()
.toNewPassphrase(Passphrase.fromPassword("subKeyPassphrase"))
.done();
keys = secretKeys.getSecretKeys();
keys = secretKeys.getPGPSecretKeyRing().getSecretKeys();
primaryKey = keys.next();
subKey = keys.next();
@ -124,18 +122,18 @@ public class ChangeSecretKeyRingPassphraseTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void changePassphraseOfSingleSubkeyToEmptyPassphrase() throws PGPException {
Iterator<PGPSecretKey> keys = keyRing.getSecretKeys();
PGPainless api = PGPainless.getInstance();
Iterator<PGPSecretKey> keys = keyRing.getPGPSecretKeyRing().getSecretKeys();
PGPSecretKey primaryKey = keys.next();
PGPSecretKey subKey = keys.next();
PGPSecretKeyRing secretKeys = PGPainless.modifyKeyRing(keyRing)
OpenPGPKey secretKeys = api.modify(keyRing)
.changeSubKeyPassphraseFromOldPassphrase(subKey.getKeyIdentifier(), Passphrase.fromPassword("weakPassphrase"))
.withSecureDefaultSettings()
.toNoPassphrase()
.done();
keys = secretKeys.getSecretKeys();
keys = secretKeys.getPGPSecretKeyRing().getSecretKeys();
primaryKey = keys.next();
subKey = keys.next();
@ -176,13 +174,13 @@ public class ChangeSecretKeyRingPassphraseTest {
UnlockSecretKey.unlockSecretKey(secretKey, decryptor);
}
private void signDummyMessageWithKeysAndPassphrase(PGPSecretKeyRing keyRing, Passphrase passphrase) throws IOException, PGPException {
private void signDummyMessageWithKeysAndPassphrase(PGPainless api, OpenPGPKey key, Passphrase passphrase) throws IOException, PGPException {
String dummyMessage = "dummy";
ByteArrayOutputStream dummy = new ByteArrayOutputStream();
EncryptionStream stream = PGPainless.encryptAndOrSign().onOutputStream(dummy)
EncryptionStream stream = api.generateMessage().onOutputStream(dummy)
.withOptions(ProducerOptions.sign(SigningOptions.get()
.addInlineSignature(PasswordBasedSecretKeyRingProtector.forKey(keyRing, passphrase),
keyRing, DocumentSignatureType.BINARY_DOCUMENT)));
.addInlineSignature(PasswordBasedSecretKeyRingProtector.forKey(key, passphrase),
key, DocumentSignatureType.BINARY_DOCUMENT)));
Streams.pipeAll(new ByteArrayInputStream(dummyMessage.getBytes()), stream);
stream.close();

View file

@ -4,8 +4,8 @@
package org.pgpainless.key.modification;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.JUtils;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
@ -25,26 +25,27 @@ public class ChangeSubkeyExpirationTimeTest {
@Test
public void changeExpirationTimeOfSubkey() {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().modernKeyRing("Alice")
.getPGPSecretKeyRing();
Date now = secretKeys.getPublicKey().getCreationTime();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey().modernKeyRing("Alice");
Date now = secretKeys.getPrimaryKey().getCreationTime();
Date inAnHour = new Date(now.getTime() + 1000 * 60 * 60);
OpenPGPCertificate.OpenPGPComponentKey encryptionKey = PGPainless.inspectKeyRing(secretKeys)
OpenPGPCertificate.OpenPGPComponentKey encryptionKey = api.inspect(secretKeys)
.getEncryptionSubkeys(EncryptionPurpose.ANY).get(0);
secretKeys = PGPainless.modifyKeyRing(secretKeys)
secretKeys = api.modify(secretKeys)
.setExpirationDateOfSubkey(
inAnHour,
encryptionKey.getKeyIdentifier().getKeyId(),
encryptionKey.getKeyIdentifier(),
SecretKeyRingProtector.unprotectedKeys())
.done();
JUtils.assertDateEquals(inAnHour, PGPainless.inspectKeyRing(secretKeys)
JUtils.assertDateEquals(inAnHour, api.inspect(secretKeys)
.getSubkeyExpirationDate(OpenPgpFingerprint.of(encryptionKey.getPGPPublicKey())));
}
@Test
public void changeExpirationTimeOfExpiredSubkey() throws IOException {
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.readKey().parseKey(
"-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
"Version: PGPainless\n" +
"Comment: CA52 4D5D E3D8 9CD9 105B BA45 3761 076B C6B5 3000\n" +
@ -79,13 +80,13 @@ public class ChangeSubkeyExpirationTimeTest {
OpenPgpFingerprint encryptionSubkey = new OpenPgpV4Fingerprint("2E541354A23C9943375EC27A3EF133ED8720D636");
JUtils.assertDateEquals(
DateUtil.parseUTCDate("2023-12-07 16:29:46 UTC"),
PGPainless.inspectKeyRing(secretKeys).getSubkeyExpirationDate(encryptionSubkey));
api.inspect(secretKeys).getSubkeyExpirationDate(encryptionSubkey));
// re-validate the subkey by setting its expiry to null (no expiry)
secretKeys = PGPainless.modifyKeyRing(secretKeys)
.setExpirationDateOfSubkey(null, encryptionSubkey.getKeyId(), SecretKeyRingProtector.unprotectedKeys())
secretKeys = api.modify(secretKeys)
.setExpirationDateOfSubkey(null, encryptionSubkey.getKeyIdentifier(), SecretKeyRingProtector.unprotectedKeys())
.done();
assertNull(PGPainless.inspectKeyRing(secretKeys).getSubkeyExpirationDate(encryptionSubkey));
assertNull(api.inspect(secretKeys).getSubkeyExpirationDate(encryptionSubkey));
}
}

View file

@ -17,9 +17,9 @@ import java.nio.charset.StandardCharsets;
import java.util.NoSuchElementException;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
@ -73,9 +73,10 @@ public class FixUserIdDoesNotBreakEncryptionCapabilityTest {
@Test
public void manualReplaceUserIdWithFixedVersionDoesNotHinderEncryptionCapability() throws IOException, PGPException {
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(SECRET_KEY);
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.readKey().parseKey(SECRET_KEY);
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
PGPSecretKeyRing modified = PGPainless.modifyKeyRing(secretKeys)
OpenPGPKey modified = api.modify(secretKeys)
.addUserId(userIdAfter, new SelfSignatureSubpackets.Callback() {
@Override
public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) {
@ -85,8 +86,8 @@ public class FixUserIdDoesNotBreakEncryptionCapabilityTest {
.removeUserId(userIdBefore, protector)
.done();
KeyRingInfo before = PGPainless.inspectKeyRing(secretKeys);
KeyRingInfo after = PGPainless.inspectKeyRing(modified);
KeyRingInfo before = api.inspect(secretKeys);
KeyRingInfo after = api.inspect(modified);
assertEquals(userIdBefore, before.getPrimaryUserId());
assertEquals(userIdAfter, after.getPrimaryUserId());
@ -104,34 +105,38 @@ public class FixUserIdDoesNotBreakEncryptionCapabilityTest {
@Test
public void testReplaceUserId_missingOldUserIdThrows() throws IOException {
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(SECRET_KEY);
assertThrows(NoSuchElementException.class, () -> PGPainless.modifyKeyRing(secretKeys)
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.readKey().parseKey(SECRET_KEY);
assertThrows(NoSuchElementException.class, () -> api.modify(secretKeys)
.replaceUserId("missing", userIdAfter, SecretKeyRingProtector.unprotectedKeys()));
}
@Test
public void testReplaceUserId_emptyOldUserIdThrows() throws IOException {
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(SECRET_KEY);
assertThrows(IllegalArgumentException.class, () -> PGPainless.modifyKeyRing(secretKeys)
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.readKey().parseKey(SECRET_KEY);
assertThrows(IllegalArgumentException.class, () -> api.modify(secretKeys)
.replaceUserId(" ", userIdAfter, SecretKeyRingProtector.unprotectedKeys()));
}
@Test
public void testReplaceUserId_emptyNewUserIdThrows() throws IOException {
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(SECRET_KEY);
assertThrows(IllegalArgumentException.class, () -> PGPainless.modifyKeyRing(secretKeys)
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.readKey().parseKey(SECRET_KEY);
assertThrows(IllegalArgumentException.class, () -> api.modify(secretKeys)
.replaceUserId(userIdBefore, " ", SecretKeyRingProtector.unprotectedKeys()));
}
@Test
public void testReplaceImplicitUserIdDoesNotBreakStuff() throws IOException, PGPException {
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(SECRET_KEY);
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.readKey().parseKey(SECRET_KEY);
PGPSecretKeyRing edited = PGPainless.modifyKeyRing(secretKeys)
OpenPGPKey edited = api.modify(secretKeys)
.replaceUserId(userIdBefore, userIdAfter, SecretKeyRingProtector.unprotectedKeys())
.done();
KeyRingInfo info = PGPainless.inspectKeyRing(edited);
KeyRingInfo info = api.inspect(edited);
assertTrue(info.isUserIdValid(userIdAfter));
assertEquals(userIdAfter, info.getPrimaryUserId());
@ -139,10 +144,10 @@ public class FixUserIdDoesNotBreakEncryptionCapabilityTest {
assertNotNull(latestCertification);
assertTrue(latestCertification.getHashedSubPackets().isPrimaryUserID());
PGPPublicKeyRing cert = PGPainless.extractCertificate(edited);
OpenPGPCertificate cert = edited.toCertificate();
ByteArrayOutputStream out = new ByteArrayOutputStream();
EncryptionStream encryptionStream = PGPainless.encryptAndOrSign()
EncryptionStream encryptionStream = api.generateMessage()
.onOutputStream(out)
.withOptions(ProducerOptions.encrypt(EncryptionOptions.get()
.addRecipient(cert)));
@ -151,7 +156,7 @@ public class FixUserIdDoesNotBreakEncryptionCapabilityTest {
encryptionStream.close();
EncryptionResult result = encryptionStream.getResult();
assertTrue(result.isEncryptedFor(cert));
assertTrue(result.isEncryptedFor(cert.getPGPPublicKeyRing()));
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
ByteArrayOutputStream plain = new ByteArrayOutputStream();

View file

@ -10,7 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.util.Passphrase;
@ -166,17 +166,18 @@ public class GnuDummyS2KChangePassphraseTest {
@Test
public void testChangePassphraseToNoPassphraseIgnoresGnuDummyS2KKeys() throws PGPException, IOException {
PGPSecretKeyRing secretKey = PGPainless.readKeyRing().secretKeyRing(KEY_WITH_GNU_DUMMY_S2K_PRIMARY_KEY);
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKey = api.readKey().parseKey(KEY_WITH_GNU_DUMMY_S2K_PRIMARY_KEY);
assertFalse(PGPainless.inspectKeyRing(secretKey).isFullyDecrypted());
assertFalse(api.inspect(secretKey).isFullyDecrypted());
secretKey = PGPainless.modifyKeyRing(secretKey)
secretKey = api.modify(secretKey)
.changePassphraseFromOldPassphrase(passphrase)
.withSecureDefaultSettings()
.toNoPassphrase()
.done();
assertTrue(PGPainless.inspectKeyRing(secretKey).isFullyDecrypted());
assertTrue(api.inspect(secretKey).isFullyDecrypted());
}
}

View file

@ -10,9 +10,9 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.util.Date;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
import org.pgpainless.PGPainless;
@ -26,11 +26,11 @@ public class OldSignatureSubpacketsArePreservedOnNewSigTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void verifyOldSignatureSubpacketsArePreservedOnNewExpirationDateSig() {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.simpleEcKeyRing("Alice <alice@wonderland.lit>")
.getPGPSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey()
.simpleEcKeyRing("Alice <alice@wonderland.lit>");
PGPSignature oldSignature = PGPainless.inspectKeyRing(secretKeys).getLatestUserIdCertification("Alice <alice@wonderland.lit>");
PGPSignature oldSignature = api.inspect(secretKeys).getLatestUserIdCertification("Alice <alice@wonderland.lit>");
assertNotNull(oldSignature);
PGPSignatureSubpacketVector oldPackets = oldSignature.getHashedSubPackets();
@ -40,10 +40,10 @@ public class OldSignatureSubpacketsArePreservedOnNewSigTest {
Date t1 = new Date(now.getTime() + millisInHour);
Date expiration = new Date(now.getTime() + 5 * 24 * millisInHour); // in 5 days
secretKeys = PGPainless.modifyKeyRing(secretKeys, t1)
secretKeys = api.modify(secretKeys, t1)
.setExpirationDate(expiration, new UnprotectedKeysProtector())
.done();
PGPSignature newSignature = PGPainless.inspectKeyRing(secretKeys, t1).getLatestUserIdCertification("Alice <alice@wonderland.lit>");
PGPSignature newSignature = api.inspect(secretKeys, t1).getLatestUserIdCertification("Alice <alice@wonderland.lit>");
assertNotNull(newSignature);
PGPSignatureSubpacketVector newPackets = newSignature.getHashedSubPackets();

View file

@ -10,7 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.EnumMap;
import java.util.Map;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.EncryptionPurpose;
@ -28,31 +28,31 @@ public class RefuseToAddWeakSubkeyTest {
@Test
public void testEditorRefusesToAddWeakSubkey() {
PGPainless api = PGPainless.getInstance();
// ensure default policy is set
Policy oldPolicy = PGPainless.getPolicy();
Policy oldPolicy = api.getAlgorithmPolicy();
Policy adjusted = oldPolicy.copy().withPublicKeyAlgorithmPolicy(
Policy.PublicKeyAlgorithmPolicy.bsi2021PublicKeyAlgorithmPolicy()
).build();
PGPainless.getInstance().setAlgorithmPolicy(adjusted);
api.setAlgorithmPolicy(adjusted);
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.modernKeyRing("Alice")
.getPGPSecretKeyRing();
SecretKeyRingEditorInterface editor = PGPainless.modifyKeyRing(secretKeys);
OpenPGPKey secretKeys = api.generateKey()
.modernKeyRing("Alice");
SecretKeyRingEditorInterface editor = api.modify(secretKeys);
KeySpec spec = KeySpec.getBuilder(KeyType.RSA(RsaLength._1024), KeyFlag.ENCRYPT_COMMS).build();
assertThrows(IllegalArgumentException.class, () ->
editor.addSubKey(spec, Passphrase.emptyPassphrase(), SecretKeyRingProtector.unprotectedKeys()));
PGPainless.getInstance().setAlgorithmPolicy(oldPolicy);
api.setAlgorithmPolicy(oldPolicy);
}
@Test
public void testEditorAllowsToAddWeakSubkeyIfCompliesToPublicKeyAlgorithmPolicy() {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.modernKeyRing("Alice")
.getPGPSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey()
.modernKeyRing("Alice");
Policy oldPolicy = PGPainless.getPolicy();
Policy oldPolicy = api.getAlgorithmPolicy();
// set weak policy
Map<PublicKeyAlgorithm, Integer> minimalBitStrengths = new EnumMap<>(PublicKeyAlgorithm.class);
@ -75,11 +75,11 @@ public class RefuseToAddWeakSubkeyTest {
minimalBitStrengths.put(PublicKeyAlgorithm.DIFFIE_HELLMAN, 2000);
// §7.2.2
minimalBitStrengths.put(PublicKeyAlgorithm.ECDH, 250);
PGPainless.getInstance().setAlgorithmPolicy(oldPolicy.copy()
api.setAlgorithmPolicy(oldPolicy.copy()
.withPublicKeyAlgorithmPolicy(new Policy.PublicKeyAlgorithmPolicy(minimalBitStrengths))
.build());
SecretKeyRingEditorInterface editor = PGPainless.modifyKeyRing(secretKeys);
SecretKeyRingEditorInterface editor = api.modify(secretKeys);
KeySpec spec = KeySpec.getBuilder(KeyType.RSA(RsaLength._1024), KeyFlag.ENCRYPT_COMMS)
.setKeyCreationDate(editor.getReferenceTime()) // The key gets created after we instantiate the editor.
.build();
@ -87,9 +87,9 @@ public class RefuseToAddWeakSubkeyTest {
secretKeys = editor.addSubKey(spec, Passphrase.emptyPassphrase(), SecretKeyRingProtector.unprotectedKeys())
.done();
assertEquals(2, PGPainless.inspectKeyRing(secretKeys).getEncryptionSubkeys(EncryptionPurpose.ANY).size());
assertEquals(2, api.inspect(secretKeys).getEncryptionSubkeys(EncryptionPurpose.ANY).size());
// reset default policy
PGPainless.getInstance().setAlgorithmPolicy(oldPolicy);
api.setAlgorithmPolicy(oldPolicy);
}
}

View file

@ -15,9 +15,10 @@ import java.io.IOException;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.bouncycastle.openpgp.api.OpenPGPSignature;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.key.TestKeys;
@ -30,9 +31,10 @@ public class RevocationCertificateTest {
@Test
public void createRevocationCertificateTest() throws PGPException, IOException {
PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = TestKeys.getEmilKey();
PGPSignature revocation = PGPainless.modifyKeyRing(secretKeys)
OpenPGPSignature revocation = api.modify(secretKeys)
.createRevocation(SecretKeyRingProtector.unprotectedKeys(),
RevocationAttributes.createKeyRevocation()
.withReason(RevocationAttributes.Reason.KEY_RETIRED)
@ -40,66 +42,68 @@ public class RevocationCertificateTest {
assertNotNull(revocation);
assertTrue(PGPainless.inspectKeyRing(secretKeys).isKeyValidlyBound(secretKeys.getPublicKey().getKeyID()));
assertTrue(api.inspect(secretKeys).isKeyValidlyBound(secretKeys.getKeyIdentifier()));
// merge key and revocation certificate
PGPSecretKeyRing revokedKey = KeyRingUtils.keysPlusSecretKey(
secretKeys,
KeyRingUtils.secretKeyPlusSignature(secretKeys.getSecretKey(), revocation));
secretKeys.getPGPSecretKeyRing(),
KeyRingUtils.secretKeyPlusSignature(secretKeys.getPrimarySecretKey().getPGPSecretKey(), revocation.getSignature()));
assertFalse(PGPainless.inspectKeyRing(revokedKey).isKeyValidlyBound(secretKeys.getPublicKey().getKeyID()));
assertFalse(api.inspect(api.toKey(revokedKey)).isKeyValidlyBound(secretKeys.getKeyIdentifier()));
}
@Test
public void createMinimalRevocationCertificateTest() throws PGPException, IOException {
PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = TestKeys.getEmilKey();
PGPPublicKeyRing minimalRevocationCert = PGPainless.modifyKeyRing(secretKeys).createMinimalRevocationCertificate(
OpenPGPCertificate minimalRevocationCert = api.modify(secretKeys).createMinimalRevocationCertificate(
SecretKeyRingProtector.unprotectedKeys(),
RevocationAttributes.createKeyRevocation().withReason(RevocationAttributes.Reason.KEY_RETIRED).withoutDescription());
assertEquals(1, minimalRevocationCert.size());
PGPPublicKey key = minimalRevocationCert.getPublicKey();
assertEquals(secretKeys.getPublicKey().getKeyID(), key.getKeyID());
assertEquals(1, minimalRevocationCert.getPGPKeyRing().size());
PGPPublicKey key = minimalRevocationCert.getPrimaryKey().getPGPPublicKey();
assertEquals(secretKeys.getKeyIdentifier(), key.getKeyIdentifier());
assertEquals(1, CollectionUtils.iteratorToList(key.getSignatures()).size());
assertFalse(key.getUserIDs().hasNext());
assertFalse(key.getUserAttributes().hasNext());
assertNull(key.getTrustData());
PGPPublicKeyRing originalCert = PGPainless.extractCertificate(secretKeys);
PGPPublicKeyRing mergedCert = PGPainless.mergeCertificate(originalCert, minimalRevocationCert);
OpenPGPCertificate originalCert = secretKeys.toCertificate();
OpenPGPCertificate mergedCert = api.mergeCertificate(originalCert, minimalRevocationCert);
assertTrue(PGPainless.inspectKeyRing(mergedCert).getRevocationState().isSoftRevocation());
assertTrue(api.inspect(mergedCert).getRevocationState().isSoftRevocation());
}
@Test
public void createMinimalRevocationCertificateForFreshKeyTest() {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().modernKeyRing("Alice <alice@example.org>")
.getPGPSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey().modernKeyRing("Alice <alice@example.org>");
PGPPublicKeyRing minimalRevocationCert = PGPainless.modifyKeyRing(secretKeys).createMinimalRevocationCertificate(
OpenPGPCertificate minimalRevocationCert = api.modify(secretKeys).createMinimalRevocationCertificate(
SecretKeyRingProtector.unprotectedKeys(),
RevocationAttributes.createKeyRevocation().withReason(RevocationAttributes.Reason.KEY_RETIRED).withoutDescription());
assertEquals(1, minimalRevocationCert.size());
PGPPublicKey key = minimalRevocationCert.getPublicKey();
assertEquals(secretKeys.getPublicKey().getKeyID(), key.getKeyID());
assertEquals(1, minimalRevocationCert.getKeys().size());
PGPPublicKey key = minimalRevocationCert.getPGPPublicKeyRing().getPublicKey();
assertEquals(secretKeys.getKeyIdentifier(), key.getKeyIdentifier());
assertEquals(1, CollectionUtils.iteratorToList(key.getSignatures()).size());
assertFalse(key.getUserIDs().hasNext());
assertFalse(key.getUserAttributes().hasNext());
assertNull(key.getTrustData());
PGPPublicKeyRing originalCert = PGPainless.extractCertificate(secretKeys);
PGPPublicKeyRing mergedCert = PGPainless.mergeCertificate(originalCert, minimalRevocationCert);
OpenPGPCertificate originalCert = secretKeys.toCertificate();
OpenPGPCertificate mergedCert = api.mergeCertificate(originalCert, minimalRevocationCert);
assertTrue(PGPainless.inspectKeyRing(mergedCert).getRevocationState().isSoftRevocation());
assertTrue(api.inspect(mergedCert).getRevocationState().isSoftRevocation());
}
@Test
public void createMinimalRevocationCertificate_wrongReason() throws PGPException, IOException {
PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = TestKeys.getEmilKey();
assertThrows(IllegalArgumentException.class,
() -> PGPainless.modifyKeyRing(secretKeys).createMinimalRevocationCertificate(
() -> api.modify(secretKeys).createMinimalRevocationCertificate(
SecretKeyRingProtector.unprotectedKeys(),
RevocationAttributes.createCertificateRevocation()
.withReason(RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID)

View file

@ -7,13 +7,14 @@ package org.pgpainless.key.modification;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
import org.pgpainless.PGPainless;
@ -24,7 +25,7 @@ import org.pgpainless.util.TestAllImplementations;
/**
* Test that makes sure that PGPainless can deal with keys that carry a key
* signature of type 0x10 (generic certification).
*
* <p>
* Originally PGPainless would only handle keys with key signature type
* 0x13 (positive certification) and would otherwise crash when negotiating
* algorithms, esp. when revoking a key.
@ -70,23 +71,26 @@ public class RevokeKeyWithGenericCertificationSignatureTest {
}
private KeyPair revokeKey(String priv) throws IOException, PGPException {
byte[] armoredBytes = priv.getBytes(StandardCharsets.UTF_8);
PGPSecretKeyRing r = PGPainless.readKeyRing()
.secretKeyRing(armoredBytes);
PGPSecretKey secretKey = r.getSecretKey();
PGPainless api = PGPainless.getInstance();
OpenPGPKey key = api.readKey().parseKey(priv);
OpenPGPKey onlyPrimaryKey = api.toKey(
new PGPSecretKeyRing(
Collections.singletonList(key.getPrimarySecretKey().getPGPSecretKey())
)
);
// this is not ideal, but still valid usage
PGPSecretKeyRing secretKeyRing =
PGPainless.modifyKeyRing(new PGPSecretKeyRing(Arrays.asList(secretKey)))
OpenPGPKey revokedPrimaryKey =
api.modify(onlyPrimaryKey)
.revoke(new UnprotectedKeysProtector()).done();
PGPPublicKey pkr = secretKeyRing.getPublicKeys().next();
PGPPublicKey pkr = revokedPrimaryKey.getPGPSecretKeyRing().getPublicKeys().next();
ByteArrayOutputStream pubOutBytes = new ByteArrayOutputStream();
try (ArmoredOutputStream pubOut = ArmoredOutputStreamFactory.get(pubOutBytes)) {
pkr.encode(pubOut);
}
pubOutBytes.close();
PGPSecretKey skr = secretKeyRing.getSecretKeys().next();
PGPSecretKey skr = revokedPrimaryKey.getPGPSecretKeyRing().getSecretKeys().next();
ByteArrayOutputStream secOutBytes = new ByteArrayOutputStream();
try (ArmoredOutputStream privOut = ArmoredOutputStreamFactory.get(secOutBytes)) {
skr.encode(privOut);

View file

@ -7,7 +7,7 @@ package org.pgpainless.key.modification;
import java.io.IOException;
import java.util.Date;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.JUtils;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
@ -99,16 +99,17 @@ public class RevokeKeyWithoutPreferredAlgorithmsOnPrimaryKey {
@ExtendWith(TestAllImplementations.class)
public void testChangingExpirationTimeWithKeyWithoutPrefAlgos()
throws IOException {
PGPainless api = PGPainless.getInstance();
Date expirationDate = DateUtil.now();
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(KEY);
OpenPGPKey secretKeys = api.readKey().parseKey(KEY);
SecretKeyRingProtector protector = new UnprotectedKeysProtector();
SecretKeyRingEditorInterface modify = PGPainless.modifyKeyRing(secretKeys)
SecretKeyRingEditorInterface modify = api.modify(secretKeys)
.setExpirationDate(expirationDate, protector);
secretKeys = modify.done();
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
KeyRingInfo info = api.inspect(secretKeys);
JUtils.assertDateEquals(expirationDate, info.getPrimaryKeyExpirationDate());
}

View file

@ -19,8 +19,9 @@ import org.bouncycastle.bcpg.sig.IssuerFingerprint;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.bouncycastle.openpgp.api.OpenPGPSignature;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
@ -33,7 +34,6 @@ import org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditorInterfac
import org.pgpainless.key.protection.PasswordBasedSecretKeyRingProtector;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.key.util.RevocationAttributes;
import org.pgpainless.signature.SignatureUtils;
import org.pgpainless.signature.subpackets.RevocationSignatureSubpackets;
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
import org.pgpainless.util.TestAllImplementations;
@ -43,10 +43,11 @@ public class RevokeSubKeyTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void revokeSukeyTest() throws IOException, PGPException {
PGPSecretKeyRing secretKeys = TestKeys.getCryptieSecretKeyRing();
public void revokeSubkeyTest() throws IOException, PGPException {
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = TestKeys.getCryptieKey();
Iterator<PGPSecretKey> keysIterator = secretKeys.iterator();
Iterator<PGPSecretKey> keysIterator = secretKeys.getPGPSecretKeyRing().iterator();
PGPSecretKey primaryKey = keysIterator.next();
PGPSecretKey subKey = keysIterator.next();
@ -55,10 +56,10 @@ public class RevokeSubKeyTest {
SecretKeyRingProtector protector = PasswordBasedSecretKeyRingProtector
.forKey(secretKeys, Passphrase.fromPassword("password123"));
secretKeys = PGPainless.modifyKeyRing(secretKeys)
secretKeys = api.modify(secretKeys)
.revokeSubKey(new OpenPgpV4Fingerprint(subKey), protector)
.done();
keysIterator = secretKeys.iterator();
keysIterator = secretKeys.getPGPSecretKeyRing().iterator();
primaryKey = keysIterator.next();
subKey = keysIterator.next();
@ -68,19 +69,20 @@ public class RevokeSubKeyTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void detachedRevokeSubkeyTest() throws IOException, PGPException {
PGPSecretKeyRing secretKeys = TestKeys.getCryptieSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = TestKeys.getCryptieKey();
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(secretKeys);
SecretKeyRingProtector protector = PasswordBasedSecretKeyRingProtector.forKey(secretKeys, Passphrase.fromPassword("password123"));
PGPSignature revocationCertificate = PGPainless.modifyKeyRing(secretKeys)
OpenPGPSignature revocationCertificate = api.modify(secretKeys)
.createRevocation(fingerprint, protector, RevocationAttributes.createKeyRevocation()
.withReason(RevocationAttributes.Reason.KEY_RETIRED)
.withDescription("Key no longer used."));
PGPPublicKey publicKey = secretKeys.getPublicKey();
PGPPublicKey publicKey = secretKeys.getPGPSecretKeyRing().getPublicKey();
assertFalse(publicKey.hasRevocation());
publicKey = PGPPublicKey.addCertification(publicKey, revocationCertificate);
publicKey = PGPPublicKey.addCertification(publicKey, revocationCertificate.getSignature());
assertTrue(publicKey.hasRevocation());
}
@ -88,19 +90,20 @@ public class RevokeSubKeyTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void testRevocationSignatureTypeCorrect() throws IOException, PGPException {
PGPSecretKeyRing secretKeys = TestKeys.getCryptieSecretKeyRing();
Iterator<PGPPublicKey> keysIterator = secretKeys.getPublicKeys();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = TestKeys.getCryptieKey();
Iterator<PGPPublicKey> keysIterator = secretKeys.getPGPKeyRing().getPublicKeys();
PGPPublicKey primaryKey = keysIterator.next();
PGPPublicKey subKey = keysIterator.next();
SecretKeyRingProtector protector = PasswordBasedSecretKeyRingProtector
.forKey(secretKeys, Passphrase.fromPassword("password123"));
SecretKeyRingEditorInterface editor = PGPainless.modifyKeyRing(secretKeys);
PGPSignature keyRevocation = editor.createRevocation(primaryKey.getKeyID(), protector, (RevocationAttributes) null);
PGPSignature subkeyRevocation = editor.createRevocation(subKey.getKeyID(), protector, (RevocationAttributes) null);
SecretKeyRingEditorInterface editor = api.modify(secretKeys);
OpenPGPSignature keyRevocation = editor.createRevocation(primaryKey.getKeyIdentifier(), protector, (RevocationAttributes) null);
OpenPGPSignature subkeyRevocation = editor.createRevocation(subKey.getKeyIdentifier(), protector, (RevocationAttributes) null);
assertEquals(SignatureType.KEY_REVOCATION.getCode(), keyRevocation.getSignatureType());
assertEquals(SignatureType.SUBKEY_REVOCATION.getCode(), subkeyRevocation.getSignatureType());
assertEquals(SignatureType.KEY_REVOCATION.getCode(), keyRevocation.getSignature().getSignatureType());
assertEquals(SignatureType.SUBKEY_REVOCATION.getCode(), subkeyRevocation.getSignature().getSignatureType());
}
@Test
@ -126,39 +129,39 @@ public class RevokeSubKeyTest {
@Test
public void inspectSubpacketsOnDefaultRevocationSignature()
throws PGPException {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().modernKeyRing("Alice")
.getPGPSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey().modernKeyRing("Alice");
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
PGPPublicKey encryptionSubkey = PGPainless.inspectKeyRing(secretKeys)
PGPPublicKey encryptionSubkey = api.inspect(secretKeys)
.getEncryptionSubkeys(EncryptionPurpose.ANY).get(0).getPGPPublicKey();
secretKeys = PGPainless.modifyKeyRing(secretKeys)
.revokeSubKey(encryptionSubkey.getKeyID(), protector)
secretKeys = api.modify(secretKeys)
.revokeSubKey(encryptionSubkey.getKeyIdentifier(), protector)
.done();
encryptionSubkey = secretKeys.getPublicKey(encryptionSubkey.getKeyID());
encryptionSubkey = secretKeys.getPGPSecretKeyRing().getPublicKey(encryptionSubkey.getKeyIdentifier());
PGPSignature revocation = encryptionSubkey.getSignaturesOfType(SignatureType.SUBKEY_REVOCATION.getCode()).next();
assertNotNull(revocation);
assertArrayEquals(
secretKeys.getPublicKey().getFingerprint(),
secretKeys.getPGPSecretKeyRing().getPublicKey().getFingerprint(),
revocation.getHashedSubPackets().getIssuerFingerprint().getFingerprint());
assertEquals(secretKeys.getPublicKey().getKeyID(),
assertEquals(secretKeys.getPGPSecretKeyRing().getPublicKey().getKeyID(),
revocation.getHashedSubPackets().getIssuerKeyID());
assertNull(SignatureSubpacketsUtil.getRevocationReason(revocation));
assertTrue(SignatureUtils.isHardRevocation(revocation));
assertTrue(revocation.isHardRevocation());
}
@Test
public void inspectSubpacketsOnModifiedRevocationSignature() {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().modernKeyRing("Alice")
.getPGPSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey().modernKeyRing("Alice");
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
PGPPublicKey encryptionSubkey = PGPainless.inspectKeyRing(secretKeys)
PGPPublicKey encryptionSubkey = api.inspect(secretKeys)
.getEncryptionSubkeys(EncryptionPurpose.ANY).get(0).getPGPPublicKey();
secretKeys = PGPainless.modifyKeyRing(secretKeys)
.revokeSubKey(encryptionSubkey.getKeyID(), protector, new RevocationSignatureSubpackets.Callback() {
secretKeys = api.modify(secretKeys)
.revokeSubKey(encryptionSubkey.getKeyIdentifier(), protector, new RevocationSignatureSubpackets.Callback() {
@Override
public void modifyHashedSubpackets(RevocationSignatureSubpackets hashedSubpackets) {
hashedSubpackets.setRevocationReason(
@ -171,14 +174,14 @@ public class RevokeSubKeyTest {
})
.done();
encryptionSubkey = secretKeys.getPublicKey(encryptionSubkey.getKeyID());
encryptionSubkey = secretKeys.getPGPSecretKeyRing().getPublicKey(encryptionSubkey.getKeyIdentifier());
PGPSignature revocation = encryptionSubkey.getSignaturesOfType(SignatureType.SUBKEY_REVOCATION.getCode()).next();
assertNotNull(revocation);
assertNull(revocation.getHashedSubPackets().getIssuerFingerprint());
assertEquals(secretKeys.getPublicKey().getKeyID(),
assertEquals(secretKeys.getKeyIdentifier().getKeyId(),
revocation.getHashedSubPackets().getIssuerKeyID());
assertNotNull(SignatureSubpacketsUtil.getRevocationReason(revocation));
assertFalse(SignatureUtils.isHardRevocation(revocation));
assertFalse(revocation.isHardRevocation());
}
}

View file

@ -13,46 +13,45 @@ import java.util.Date;
import java.util.NoSuchElementException;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.key.info.KeyRingInfo;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.key.util.RevocationAttributes;
import org.pgpainless.util.selection.userid.SelectUserId;
public class RevokeUserIdsTest {
@Test
public void revokeWithSelectUserId() throws PGPException {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.modernKeyRing("Alice <alice@pgpainless.org>")
.getPGPSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey()
.modernKeyRing("Alice <alice@pgpainless.org>");
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
secretKeys = PGPainless.modifyKeyRing(secretKeys)
secretKeys = api.modify(secretKeys)
.addUserId("Allice <alice@example.org>", protector)
.addUserId("Alice <alice@example.org>", protector)
.done();
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
KeyRingInfo info = api.inspect(secretKeys);
assertTrue(info.isUserIdValid("Alice <alice@pgpainless.org>"));
assertTrue(info.isUserIdValid("Allice <alice@example.org>"));
assertTrue(info.isUserIdValid("Alice <alice@example.org>"));
Date n1 = new Date(info.getCreationDate().getTime() + 1000); // 1 sec later
secretKeys = PGPainless.modifyKeyRing(secretKeys, n1)
secretKeys = api.modify(secretKeys, n1)
.revokeUserIds(
SelectUserId.containsEmailAddress("alice@example.org"),
protector,
RevocationAttributes.createCertificateRevocation()
.withReason(RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID)
.withoutDescription())
.withoutDescription(),
uid -> uid.contains("alice@example.org"))
.done();
info = PGPainless.inspectKeyRing(secretKeys, n1);
info = api.inspect(secretKeys, n1);
assertTrue(info.isUserIdValid("Alice <alice@pgpainless.org>"));
assertFalse(info.isUserIdValid("Allice <alice@example.org>"));
assertFalse(info.isUserIdValid("Alice <alice@example.org>"));
@ -60,28 +59,28 @@ public class RevokeUserIdsTest {
@Test
public void removeUserId() throws PGPException {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.modernKeyRing("Alice <alice@pgpainless.org>")
.getPGPSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey()
.modernKeyRing("Alice <alice@pgpainless.org>");
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
secretKeys = PGPainless.modifyKeyRing(secretKeys)
secretKeys = api.modify(secretKeys)
.addUserId("Allice <alice@example.org>", protector)
.addUserId("Alice <alice@example.org>", protector)
.done();
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
KeyRingInfo info = api.inspect(secretKeys);
assertTrue(info.isUserIdValid("Alice <alice@pgpainless.org>"));
assertTrue(info.isUserIdValid("Allice <alice@example.org>"));
assertTrue(info.isUserIdValid("Alice <alice@example.org>"));
Date n1 = new Date(info.getCreationDate().getTime() + 1000);
secretKeys = PGPainless.modifyKeyRing(secretKeys, n1)
secretKeys = api.modify(secretKeys, n1)
.removeUserId("Allice <alice@example.org>", protector)
.done();
info = PGPainless.inspectKeyRing(secretKeys, n1);
info = api.inspect(secretKeys, n1);
assertTrue(info.isUserIdValid("Alice <alice@pgpainless.org>"));
assertFalse(info.isUserIdValid("Allice <alice@example.org>"));
assertTrue(info.isUserIdValid("Alice <alice@example.org>"));
@ -95,14 +94,14 @@ public class RevokeUserIdsTest {
@Test
public void emptySelectionYieldsNoSuchElementException() {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.modernKeyRing("Alice <alice@pgpainless.org>")
.getPGPSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey()
.modernKeyRing("Alice <alice@pgpainless.org>");
assertThrows(NoSuchElementException.class, () ->
PGPainless.modifyKeyRing(secretKeys).revokeUserIds(
SelectUserId.containsEmailAddress("alice@example.org"),
api.modify(secretKeys).revokeUserIds(
SecretKeyRingProtector.unprotectedKeys(),
(RevocationAttributes) null));
(RevocationAttributes) null,
uid -> uid.contains("alice@example.org")));
}
}

View file

@ -16,6 +16,7 @@ import org.bouncycastle.bcpg.SecretKeyPacket;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
@ -67,27 +68,27 @@ public class S2KUsageFixTest {
@Test
public void verifyOutFixInChangePassphraseWorks()
throws PGPException {
PGPSecretKeyRing before = PGPainless.generateKeyRing().modernKeyRing("Alice", "before")
.getPGPSecretKeyRing();
for (PGPSecretKey key : before) {
PGPainless api = PGPainless.getInstance();
OpenPGPKey before = api.generateKey().modernKeyRing("Alice", "before");
for (PGPSecretKey key : before.getPGPSecretKeyRing()) {
assertEquals(SecretKeyPacket.USAGE_SHA1, key.getS2KUsage());
}
PGPSecretKeyRing unprotected = PGPainless.modifyKeyRing(before)
OpenPGPKey unprotected = api.modify(before)
.changePassphraseFromOldPassphrase(Passphrase.fromPassword("before"))
.withSecureDefaultSettings()
.toNoPassphrase()
.done();
for (PGPSecretKey key : unprotected) {
for (PGPSecretKey key : unprotected.getPGPSecretKeyRing()) {
assertEquals(SecretKeyPacket.USAGE_NONE, key.getS2KUsage());
}
PGPSecretKeyRing after = PGPainless.modifyKeyRing(unprotected)
OpenPGPKey after = api.modify(unprotected)
.changePassphraseFromOldPassphrase(Passphrase.emptyPassphrase())
.withSecureDefaultSettings()
.toNewPassphrase(Passphrase.fromPassword("after"))
.done();
for (PGPSecretKey key : after) {
for (PGPSecretKey key : after.getPGPSecretKeyRing()) {
assertEquals(SecretKeyPacket.USAGE_SHA1, key.getS2KUsage());
}
}
@ -95,18 +96,19 @@ public class S2KUsageFixTest {
@Test
public void testFixS2KUsageFrom_USAGE_CHECKSUM_to_USAGE_SHA1()
throws IOException, PGPException {
PGPSecretKeyRing keys = PGPainless.readKeyRing().secretKeyRing(KEY_WITH_USAGE_CHECKSUM);
PGPainless api = PGPainless.getInstance();
OpenPGPKey keys = api.readKey().parseKey(KEY_WITH_USAGE_CHECKSUM);
SecretKeyRingProtector protector = SecretKeyRingProtector.unlockAnyKeyWith(Passphrase.fromPassword("after"));
PGPSecretKeyRing fixed = S2KUsageFix.replaceUsageChecksumWithUsageSha1(keys, protector);
PGPSecretKeyRing fixed = S2KUsageFix.replaceUsageChecksumWithUsageSha1(keys.getPGPSecretKeyRing(), protector);
for (PGPSecretKey key : fixed) {
assertEquals(SecretKeyPacket.USAGE_SHA1, key.getS2KUsage());
}
testCanStillDecrypt(keys, protector);
testCanStillDecrypt(api.toKey(fixed), protector);
}
private void testCanStillDecrypt(PGPSecretKeyRing keys, SecretKeyRingProtector protector)
private void testCanStillDecrypt(OpenPGPKey keys, SecretKeyRingProtector protector)
throws PGPException, IOException {
ByteArrayInputStream in = new ByteArrayInputStream(MESSAGE.getBytes(StandardCharsets.UTF_8));
DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify()

View file

@ -34,6 +34,7 @@ import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
import org.bouncycastle.openpgp.api.OpenPGPImplementation;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.CompressionAlgorithm;
@ -43,7 +44,6 @@ import org.pgpainless.algorithm.SignatureType;
import org.pgpainless.key.TestKeys;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.key.protection.UnlockSecretKey;
import org.pgpainless.policy.Policy;
import org.pgpainless.signature.consumer.SignaturePicker;
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
@ -51,17 +51,17 @@ public class SignatureSubpacketsUtilTest {
@Test
public void testGetKeyExpirationTimeAsDate() {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.modernKeyRing("Expire")
.getPGPSecretKeyRing();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey()
.modernKeyRing("Expire");
Date expiration = Date.from(new Date().toInstant().plus(365, ChronoUnit.DAYS));
secretKeys = PGPainless.modifyKeyRing(secretKeys)
secretKeys = api.modify(secretKeys)
.setExpirationDate(expiration, SecretKeyRingProtector.unprotectedKeys())
.done();
PGPSignature expirationSig = SignaturePicker.pickCurrentUserIdCertificationSignature(
secretKeys, "Expire", Policy.getInstance(), new Date());
OpenPGPCertificate.OpenPGPComponentKey notTheRightKey = PGPainless.inspectKeyRing(secretKeys).getSigningSubkeys().get(0);
secretKeys.getPGPSecretKeyRing(), "Expire", api.getAlgorithmPolicy(), new Date());
OpenPGPCertificate.OpenPGPComponentKey notTheRightKey = api.inspect(secretKeys).getSigningSubkeys().get(0);
assertThrows(IllegalArgumentException.class, () ->
SignatureSubpacketsUtil.getKeyExpirationTimeAsDate(expirationSig, notTheRightKey.getPGPPublicKey()));
@ -69,18 +69,19 @@ public class SignatureSubpacketsUtilTest {
@Test
public void testGetRevocable() throws PGPException, IOException {
PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing();
PGPPrivateKey certKey = UnlockSecretKey.unlockSecretKey(secretKeys.getSecretKey(), SecretKeyRingProtector.unprotectedKeys());
OpenPGPKey secretKeys = TestKeys.getEmilKey();
PGPPrivateKey certKey = UnlockSecretKey.unlockSecretKey(secretKeys.getPrimarySecretKey().getPGPSecretKey(),
SecretKeyRingProtector.unprotectedKeys());
PGPSignatureGenerator generator = getSignatureGenerator(certKey, SignatureType.CASUAL_CERTIFICATION);
PGPSignature withoutRevocable = generator.generateCertification(secretKeys.getPublicKey());
PGPSignature withoutRevocable = generator.generateCertification(secretKeys.getPrimaryKey().getPGPPublicKey());
assertNull(SignatureSubpacketsUtil.getRevocable(withoutRevocable));
generator = getSignatureGenerator(certKey, SignatureType.CASUAL_CERTIFICATION);
PGPSignatureSubpacketGenerator hashed = new PGPSignatureSubpacketGenerator();
hashed.setRevocable(true, true);
generator.setHashedSubpackets(hashed.generate());
PGPSignature withRevocable = generator.generateCertification(secretKeys.getPublicKey());
PGPSignature withRevocable = generator.generateCertification(secretKeys.getPrimaryKey().getPGPPublicKey());
assertNotNull(SignatureSubpacketsUtil.getRevocable(withRevocable));
}

View file

@ -14,7 +14,7 @@ import java.util.List;
import java.util.stream.Collectors;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.key.protection.SecretKeyRingProtector;
@ -24,17 +24,17 @@ public class SelectUserIdTest {
@Test
public void testSelectUserIds() throws PGPException {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.simpleEcKeyRing("<alice@wonderland.lit>")
.getPGPSecretKeyRing();
secretKeys = PGPainless.modifyKeyRing(secretKeys)
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey()
.simpleEcKeyRing("<alice@wonderland.lit>");
secretKeys = api.modify(secretKeys)
.addUserId(
UserId.builder().withName("Alice Liddell").noComment()
.withEmail("crazy@the-rabbit.hole").build(),
SecretKeyRingProtector.unprotectedKeys())
.done();
List<String> userIds = PGPainless.inspectKeyRing(secretKeys).getValidUserIds();
List<String> userIds = api.inspect(secretKeys).getValidUserIds();
List<String> validEmail = userIds.stream().filter(SelectUserId.and(
SelectUserId.validUserId(secretKeys),
SelectUserId.containsEmailAddress("alice@wonderland.lit")
@ -54,14 +54,14 @@ public class SelectUserIdTest {
@Test
public void testContainsSubstring() throws PGPException {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().simpleEcKeyRing("wine drinker")
.getPGPSecretKeyRing();
secretKeys = PGPainless.modifyKeyRing(secretKeys)
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey().simpleEcKeyRing("wine drinker");
secretKeys = api.modify(secretKeys)
.addUserId("this is not a quine", SecretKeyRingProtector.unprotectedKeys())
.addUserId("this is not a crime", SecretKeyRingProtector.unprotectedKeys())
.done();
List<String> userIds = PGPainless.inspectKeyRing(secretKeys).getValidUserIds();
List<String> userIds = api.inspect(secretKeys).getValidUserIds();
List<String> containSubstring = userIds.stream().filter(SelectUserId.containsSubstring("ine")).collect(Collectors.toList());
assertEquals(Arrays.asList("wine drinker", "this is not a quine"), containSubstring);
@ -69,9 +69,9 @@ public class SelectUserIdTest {
@Test
public void testContainsEmailAddress() {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().simpleEcKeyRing("Alice <alice@wonderland.lit>")
.getPGPSecretKeyRing();
List<String> userIds = PGPainless.inspectKeyRing(secretKeys).getValidUserIds();
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey().simpleEcKeyRing("Alice <alice@wonderland.lit>");
List<String> userIds = api.inspect(secretKeys).getValidUserIds();
assertEquals("Alice <alice@wonderland.lit>", userIds.stream().filter(
SelectUserId.containsEmailAddress("alice@wonderland.lit")).findFirst().get());
@ -83,15 +83,15 @@ public class SelectUserIdTest {
@Test
public void testAndOrNot() throws PGPException {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().simpleEcKeyRing("Alice <alice@wonderland.lit>")
.getPGPSecretKeyRing();
secretKeys = PGPainless.modifyKeyRing(secretKeys)
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey().simpleEcKeyRing("Alice <alice@wonderland.lit>");
secretKeys = api.modify(secretKeys)
.addUserId("Alice <another@email.address>", SecretKeyRingProtector.unprotectedKeys())
.addUserId("<crazy@the-rabbit.hole>", SecretKeyRingProtector.unprotectedKeys())
.addUserId("Crazy Girl <alice@wonderland.lit>", SecretKeyRingProtector.unprotectedKeys())
.done();
List<String> userIds = PGPainless.inspectKeyRing(secretKeys).getValidUserIds();
List<String> userIds = api.inspect(secretKeys).getValidUserIds();
List<String> or = userIds.stream().filter(SelectUserId.or(
SelectUserId.containsEmailAddress("alice@wonderland.lit"),
@ -110,12 +110,12 @@ public class SelectUserIdTest {
@Test
public void testFirstMatch() throws PGPException {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().simpleEcKeyRing("First UserID")
.getPGPSecretKeyRing();
secretKeys = PGPainless.modifyKeyRing(secretKeys)
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.generateKey().simpleEcKeyRing("First UserID");
secretKeys = api.modify(secretKeys)
.addUserId("Second UserID", SecretKeyRingProtector.unprotectedKeys())
.done();
List<String> userIds = PGPainless.inspectKeyRing(secretKeys).getValidUserIds();
List<String> userIds = api.inspect(secretKeys).getValidUserIds();
assertEquals("First UserID", userIds.stream().filter(SelectUserId.validUserId(secretKeys)).findFirst().get());
assertEquals("Second UserID", userIds.stream().filter(SelectUserId.containsSubstring("Second")).findFirst().get());
}

View file

@ -9,10 +9,11 @@ import java.io.InputStream
import java.io.OutputStream
import java.lang.RuntimeException
import org.bouncycastle.openpgp.PGPException
import org.bouncycastle.openpgp.PGPPublicKeyRing
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection
import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.pgpainless.PGPainless
import org.pgpainless.bouncycastle.extensions.openPgpFingerprint
import org.pgpainless.bouncycastle.extensions.toOpenPGPCertificate
import org.pgpainless.exception.WrongPassphraseException
import org.pgpainless.key.util.KeyRingUtils
import org.pgpainless.key.util.RevocationAttributes
@ -38,7 +39,7 @@ class RevokeKeyImpl : RevokeKey {
secretKeyRings.forEach { protector.addSecretKey(it) }
val revocationCertificates = mutableListOf<PGPPublicKeyRing>()
val revocationCertificates = mutableListOf<OpenPGPCertificate>()
secretKeyRings.forEach { secretKeys ->
val editor = PGPainless.modifyKeyRing(secretKeys)
try {
@ -53,7 +54,8 @@ class RevokeKeyImpl : RevokeKey {
val certificate = PGPainless.extractCertificate(secretKeys)
val revocation = editor.createRevocation(protector, attributes)
revocationCertificates.add(
KeyRingUtils.injectCertification(certificate, revocation))
KeyRingUtils.injectCertification(certificate, revocation.signature)
.toOpenPGPCertificate())
}
} catch (e: WrongPassphraseException) {
throw SOPGPException.KeyIsProtected(
@ -67,7 +69,8 @@ class RevokeKeyImpl : RevokeKey {
return object : Ready() {
override fun writeTo(outputStream: OutputStream) {
val collection = PGPPublicKeyRingCollection(revocationCertificates)
val collection =
PGPPublicKeyRingCollection(revocationCertificates.map { it.pgpPublicKeyRing })
if (armor) {
val armorOut = ArmoredOutputStreamFactory.get(outputStream)
collection.encode(armorOut)

View file

@ -7,7 +7,7 @@ package sop.testsuite.pgpainless.operation;
import org.bouncycastle.bcpg.KeyIdentifier;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.pgpainless.PGPainless;
@ -32,13 +32,13 @@ public class PGPainlessChangeKeyPasswordTest extends ChangeKeyPasswordTest {
@ParameterizedTest
@MethodSource("provideInstances")
public void changePasswordOfKeyWithSeparateSubkeyPasswords(SOP sop) throws IOException, PGPException {
PGPSecretKeyRing secretKeys = PGPainless.buildKeyRing()
PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = PGPainless.buildKeyRing()
.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519), KeyFlag.CERTIFY_OTHER))
.addSubkey(KeySpec.getBuilder(KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519), KeyFlag.SIGN_DATA))
.addSubkey(KeySpec.getBuilder(KeyType.XDH_LEGACY(XDHLegacySpec._X25519), KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE))
.build()
.getPGPSecretKeyRing();
Iterator<PGPPublicKey> keys = secretKeys.getPublicKeys();
.build();
Iterator<PGPPublicKey> keys = secretKeys.getPGPSecretKeyRing().getPublicKeys();
KeyIdentifier primaryKeyId = keys.next().getKeyIdentifier();
KeyIdentifier signingKeyId = keys.next().getKeyIdentifier();
KeyIdentifier encryptKeyId = keys.next().getKeyIdentifier();
@ -47,7 +47,7 @@ public class PGPainlessChangeKeyPasswordTest extends ChangeKeyPasswordTest {
String p2 = "0r4ng3";
String p3 = "dr4g0n";
secretKeys = PGPainless.modifyKeyRing(secretKeys)
secretKeys = api.modify(secretKeys)
.changeSubKeyPassphraseFromOldPassphrase(primaryKeyId, Passphrase.emptyPassphrase())
.withSecureDefaultSettings()
.toNewPassphrase(Passphrase.fromPassword(p1))