/// <summary> /// Return a copy of the passed in secret key, encrypted using a new password /// and the passed in algorithm. /// </summary> /// <remarks> /// Allows the caller to handle the encoding of the passphrase to bytes. /// </remarks> /// <param name="key">The PgpSecretKey to be copied.</param> /// <param name="rawOldPassPhrase">The current password for the key.</param> /// <param name="rawNewPassPhrase">The new password for the key.</param> public static PgpSecretKey CopyWithNewPassword( PgpSecretKey key, ReadOnlySpan <byte> rawOldPassPhrase, ReadOnlySpan <byte> rawNewPassPhrase) { if (key == null) { throw new ArgumentNullException(nameof(key)); } if (key.IsPrivateKeyEmpty) { throw new PgpException("no private key in this SecretKey - public key present only."); } byte[] rawKeyData = CryptoPool.Rent(key.keyPacket.KeyBytes.Length - key.keyPacket.PublicKeyLength + 0x20); try { S2kBasedEncryption.DecryptSecretKey( rawOldPassPhrase, key.keyPacket.KeyBytes.AsSpan(key.keyPacket.PublicKeyLength), rawKeyData, out int rawKeySize, key.keyPacket.Version); // Use the default S2K parameters var s2kParameters = new S2kParameters(); var newKeyData = new byte[S2kBasedEncryption.GetEncryptedLength(s2kParameters, rawKeySize, key.keyPacket.Version) + key.keyPacket.PublicKeyLength]; key.keyPacket.KeyBytes.AsSpan(0, key.keyPacket.PublicKeyLength).CopyTo(newKeyData); S2kBasedEncryption.EncryptSecretKey( rawNewPassPhrase, s2kParameters, rawKeyData.AsSpan(0, rawKeySize), newKeyData.AsSpan(key.keyPacket.PublicKeyLength), key.keyPacket.Version); SecretKeyPacket newKeyPacket; if (key.keyPacket is SecretSubkeyPacket) { newKeyPacket = new SecretSubkeyPacket(key.Algorithm, key.CreationTime, newKeyData); } else { newKeyPacket = new SecretKeyPacket(key.Algorithm, key.CreationTime, newKeyData); } return(new PgpSecretKey(newKeyPacket, key)); } finally { CryptoPool.Return(rawKeyData); } }
/// <summary> /// Return a copy of the passed in secret key, encrypted using a new password /// and the passed in algorithm. /// </summary> /// <param name="key">The PgpSecretKey to be copied.</param> /// <param name="oldPassPhrase">The current password for the key.</param> /// <param name="newPassPhrase">The new password for the key.</param> public static PgpSecretKey CopyWithNewPassword( PgpSecretKey key, ReadOnlySpan <char> oldPassPhrase, ReadOnlySpan <char> newPassPhrase) { int oldPassPhraseByteCount = Encoding.UTF8.GetByteCount(oldPassPhrase); int newPassPhraseByteCount = Encoding.UTF8.GetByteCount(newPassPhrase); byte[] passphraseBuffer = CryptoPool.Rent(oldPassPhraseByteCount + newPassPhraseByteCount); try { Encoding.UTF8.GetBytes(oldPassPhrase, passphraseBuffer); Encoding.UTF8.GetBytes(newPassPhrase, passphraseBuffer.AsSpan(oldPassPhraseByteCount)); return(CopyWithNewPassword(key, passphraseBuffer.AsSpan(0, oldPassPhraseByteCount), passphraseBuffer.AsSpan(oldPassPhraseByteCount, newPassPhraseByteCount))); } finally { CryptoPool.Return(passphraseBuffer, oldPassPhraseByteCount + newPassPhraseByteCount); } }
/// <summary> /// Return a copy of the passed in secret key ring, with the master key and sub keys encrypted /// using a new password and the passed in algorithm. /// </summary> /// <param name="ring">The <c>PgpSecretKeyRing</c> to be copied.</param> /// <param name="oldPassPhrase">The current password for key.</param> /// <param name="newPassPhrase">The new password for the key.</param> /// <param name="newEncAlgorithm">The algorithm to be used for the encryption.</param> /// <param name="rand">Source of randomness.</param> public static PgpSecretKeyRing CopyWithNewPassword( PgpSecretKeyRing ring, ReadOnlySpan <char> oldPassPhrase, ReadOnlySpan <char> newPassPhrase) { IList <PgpSecretKey> newKeys = new List <PgpSecretKey>(ring.keys.Count); foreach (PgpSecretKey secretKey in ring.GetSecretKeys()) { if (secretKey.IsPrivateKeyEmpty) { newKeys.Add(secretKey); } else { newKeys.Add(PgpSecretKey.CopyWithNewPassword(secretKey, oldPassPhrase, newPassPhrase)); } } return(new PgpSecretKeyRing(newKeys, ring.extraPubKeys)); }