public byte[] ExportPrivateKey( ReadOnlySpan <byte> passwordBytes, S2kParameters s2kParameters) { DSAParameters dsaParameters = new DSAParameters(); byte[] secretPart = Array.Empty <byte>(); try { dsaParameters = dsa.ExportParameters(true); secretPart = CryptoPool.Rent(MPInteger.GetMPEncodedLength(dsaParameters.X !)); MPInteger.TryWriteInteger(dsaParameters.X, secretPart, out var secretSize); int publicKeySize = MPInteger.GetMPEncodedLength(dsaParameters.P !, dsaParameters.Q !, dsaParameters.G !, dsaParameters.Y !); int encryptedSecretSize = S2kBasedEncryption.GetEncryptedLength(s2kParameters, secretSize); int expectedLength = publicKeySize + encryptedSecretSize; var destination = new byte[expectedLength]; WriteOpenPgpPublicKey(dsaParameters, destination); S2kBasedEncryption.EncryptSecretKey(passwordBytes, s2kParameters, secretPart.AsSpan(0, secretSize), destination.AsSpan(publicKeySize)); return(destination); } finally { CryptoPool.Return(secretPart); CryptographicOperations.ZeroMemory(dsaParameters.X); } }
public static int GetEncryptedLength( S2kParameters s2kParameters, int sourceLength, int version = 4) { if (version >= 4) { // 4 bytes for usage tag, encryption algorithm, s2k type, hash algorithm // 8 bytes for salt // 1 byte for iteration count // IV of cipher's block size in bytes // 20 for SHA-1 checksum using var symmetricAlgorithm = PgpUtilities.GetSymmetricAlgorithm(s2kParameters.EncryptionAlgorithm); return (4 + 8 + 1 + ((symmetricAlgorithm.BlockSize + 7) / 8) + sourceLength + (s2kParameters.UsageTag == S2kUsageTag.Checksum ? 2 : 20)); } else { using var symmetricAlgorithm = PgpUtilities.GetSymmetricAlgorithm(s2kParameters.EncryptionAlgorithm); // 1 for encryption algorithm, hash is fixed at MD5, no salt and no iterations, 2 bytes for checksum return(1 + ((symmetricAlgorithm.BlockSize + 7) / 8) + sourceLength + 2); } }
public byte[] ExportPrivateKey( ReadOnlySpan <byte> passwordBytes, S2kParameters s2kParameters) { RSAParameters rsaParameters = new RSAParameters(); byte[] secretPart = Array.Empty <byte>(); try { rsaParameters = rsa.ExportParameters(true); secretPart = CryptoPool.Rent(MPInteger.GetMPEncodedLength(rsaParameters.D !, rsaParameters.P !, rsaParameters.Q !, rsaParameters.InverseQ !)); MPInteger.TryWriteInteger(rsaParameters.D, secretPart, out var dBytesWritten); MPInteger.TryWriteInteger(rsaParameters.P, secretPart.AsSpan(dBytesWritten), out var pBytesWritten); MPInteger.TryWriteInteger(rsaParameters.Q, secretPart.AsSpan(dBytesWritten + pBytesWritten), out var qBytesWritten); MPInteger.TryWriteInteger(rsaParameters.InverseQ, secretPart.AsSpan(dBytesWritten + pBytesWritten + qBytesWritten), out var iqBytesWritten); int secretSize = dBytesWritten + pBytesWritten + qBytesWritten + iqBytesWritten; int encryptedSecretSize = S2kBasedEncryption.GetEncryptedLength(s2kParameters, secretSize); int expectedLength = MPInteger.GetMPEncodedLength(rsaParameters.Modulus !, rsaParameters.Exponent !) + encryptedSecretSize; var destination = new byte[expectedLength]; MPInteger.TryWriteInteger(rsaParameters.Modulus, destination, out int modulusWritten); MPInteger.TryWriteInteger(rsaParameters.Exponent, destination.AsSpan(modulusWritten), out int exponentWritten); S2kBasedEncryption.EncryptSecretKey(passwordBytes, s2kParameters, secretPart.AsSpan(0, secretSize), destination.AsSpan(modulusWritten + exponentWritten)); return(destination.AsSpan(0, modulusWritten + exponentWritten + encryptedSecretSize).ToArray()); } finally { CryptoPool.Return(secretPart); CryptographicOperations.ZeroMemory(rsaParameters.D); CryptographicOperations.ZeroMemory(rsaParameters.P); CryptographicOperations.ZeroMemory(rsaParameters.Q); CryptographicOperations.ZeroMemory(rsaParameters.InverseQ); CryptographicOperations.ZeroMemory(rsaParameters.DP); CryptographicOperations.ZeroMemory(rsaParameters.DQ); } }
public byte[] ExportPrivateKey( ReadOnlySpan <byte> passwordBytes, S2kParameters s2kParameters) { ECParameters ecParameters = new ECParameters(); byte[] secretPart = Array.Empty <byte>(); try { ecParameters = ecdh.ExportParameters(true); if (ecdh is X25519) { Array.Reverse(ecParameters.D !); } int secretSize = MPInteger.GetMPEncodedLength(ecParameters.D !); secretPart = CryptoPool.Rent(secretSize); MPInteger.TryWriteInteger(ecParameters.D, secretPart, out var _); int encryptedSecretLength = S2kBasedEncryption.GetEncryptedLength(s2kParameters, secretSize); int estimatedLength = 32 /* OID */ + MPInteger.GetMPEncodedLength(ecParameters.Q.X !, ecParameters.Q.Y !) + 1 /* EC Point type */ + 4 /* KDF Parameters */ + encryptedSecretLength; var destination = new byte[estimatedLength]; WriteOpenPgpECParameters(ecParameters, destination, out int bytesWritten); WriteKDFParameters(destination.AsSpan(bytesWritten)); S2kBasedEncryption.EncryptSecretKey(passwordBytes, s2kParameters, secretPart.AsSpan(0, secretSize), destination.AsSpan(bytesWritten + 4)); return(destination.AsSpan(0, bytesWritten + 4 + encryptedSecretLength).ToArray()); } finally { CryptoPool.Return(secretPart); if (ecParameters.D != null) { CryptographicOperations.ZeroMemory(ecParameters.D); } } }
public static void EncryptSecretKey( ReadOnlySpan <byte> password, S2kParameters s2kParameters, ReadOnlySpan <byte> source, Span <byte> destination, int version = 4) { if (password.Length == 0) { destination[0] = (byte)S2kUsageTag.None; source.CopyTo(destination.Slice(1)); return; } using var c = PgpUtilities.GetSymmetricAlgorithm(s2kParameters.EncryptionAlgorithm); int keySizeInBytes = (c.KeySize + 7) / 8; Span <byte> keyBytes = stackalloc byte[keySizeInBytes]; if (version <= 3) { MakeKey(password, PgpHashAlgorithm.MD5, Array.Empty <byte>(), 0, keyBytes); destination[0] = (byte)s2kParameters.EncryptionAlgorithm; c.GenerateIV(); c.IV.CopyTo(destination.Slice(1)); int bytesWritten = 13 + ((c.BlockSize + 7) / 8); destination = destination.Slice(bytesWritten); c.Key = keyBytes.ToArray(); int checksum = 0; foreach (var b in source) { checksum += b; } for (int i = 0; i < 4; i++) { using var encryptor = new ZeroPaddedCryptoTransform(c.CreateEncryptor()); destination[0] = source[0]; destination[1] = source[1]; var mpInteger = MPInteger.ReadInteger(source, out int bytesConsumed).ToArray(); source = source.Slice(bytesConsumed); var data = encryptor.TransformFinalBlock(mpInteger, 0, mpInteger.Length); data.AsSpan().CopyTo(destination.Slice(2)); destination = destination.Slice(2 + data.Length); CryptographicOperations.ZeroMemory(mpInteger); if (i != 4) { c.IV = data.AsSpan(data.Length - (c.BlockSize / 8)).ToArray(); } } destination[0] = (byte)(checksum >> 8); destination[1] = (byte)(checksum); } else { var salt = new byte[8]; RandomNumberGenerator.Fill(salt); byte rawIterationCount = 0x60; int iterationCount = (16 + (rawIterationCount & 15)) << ((rawIterationCount >> 4) + 6); MakeKey(password, s2kParameters.HashAlgorithm, salt, iterationCount, keyBytes); destination[0] = (byte)s2kParameters.UsageTag; destination[1] = (byte)s2kParameters.EncryptionAlgorithm; destination[2] = 3; // Salted & iterated destination[3] = (byte)s2kParameters.HashAlgorithm; salt.CopyTo(destination.Slice(4)); destination[12] = rawIterationCount; c.GenerateIV(); c.IV.CopyTo(destination.Slice(13)); int bytesWritten = 13 + ((c.BlockSize + 7) / 8); destination = destination.Slice(bytesWritten); c.Key = keyBytes.ToArray(); using var encryptor = new ZeroPaddedCryptoTransform(c.CreateEncryptor()); byte[] checksumBytes; if (s2kParameters.UsageTag == S2kUsageTag.Sha1) { using var sha1 = IncrementalHash.CreateHash(HashAlgorithmName.SHA1); sha1.AppendData(source); checksumBytes = sha1.GetHashAndReset(); } else { int checksum = 0; foreach (var b in source) { checksum += b; } checksumBytes = new byte[] { (byte)(checksum >> 8), (byte)checksum }; } var decSource = new byte[source.Length + checksumBytes.Length]; source.CopyTo(decSource); checksumBytes.CopyTo(decSource, source.Length); var data = encryptor.TransformFinalBlock(decSource, 0, decSource.Length); data.AsSpan().CopyTo(destination); CryptographicOperations.ZeroMemory(data); CryptographicOperations.ZeroMemory(decSource); } }