diff options
author | David Hook <dgh@bouncycastle.org> | 2019-01-15 11:01:18 +1100 |
---|---|---|
committer | David Hook <dgh@bouncycastle.org> | 2019-01-15 11:01:18 +1100 |
commit | f25f7bed6807096d9a67d31f547398c1f6f213e4 (patch) | |
tree | e05afc98f495985870a7b4edbf8ab45f63a75e68 | |
parent | added alg constructor (diff) | |
download | BouncyCastle.NET-ed25519-f25f7bed6807096d9a67d31f547398c1f6f213e4.tar.xz |
first cut on Pkcs8
-rw-r--r-- | crypto/src/crmf/EncryptedValueBuilder.cs | 163 | ||||
-rw-r--r-- | crypto/src/crmf/IEncryptedValuePadder.cs | 29 | ||||
-rw-r--r-- | crypto/src/crypto/ICipher.cs | 43 | ||||
-rw-r--r-- | crypto/src/crypto/ICipherBuilder.cs | 31 | ||||
-rw-r--r-- | crypto/src/crypto/ICipherBuilderWithKey.cs | 14 | ||||
-rw-r--r-- | crypto/src/crypto/IDecryptorBuilderProvider.cs | 18 | ||||
-rw-r--r-- | crypto/src/crypto/Security.cs | 108 | ||||
-rw-r--r-- | crypto/src/pkcs/Pkcs8EncryptedPrivateKeyInfo.cs | 106 | ||||
-rw-r--r-- | crypto/src/pkcs/Pkcs8EncryptedPrivateKeyInfoBuilder.cs | 52 | ||||
-rw-r--r-- | crypto/src/pkcs/PkcsException.cs | 18 | ||||
-rw-r--r-- | crypto/src/pkcs/PkcsIOException.cs | 19 | ||||
-rw-r--r-- | crypto/src/util/io/MemoryInputStream.cs | 13 | ||||
-rw-r--r-- | crypto/src/util/io/MemoryOutputStream.cs | 10 |
13 files changed, 624 insertions, 0 deletions
diff --git a/crypto/src/crmf/EncryptedValueBuilder.cs b/crypto/src/crmf/EncryptedValueBuilder.cs new file mode 100644 index 000000000..f9279bd53 --- /dev/null +++ b/crypto/src/crmf/EncryptedValueBuilder.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Crmf; +using System.IO; +using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crmf +{ + public class EncryptedValueBuilder + { + private IKeyWrapper wrapper; + private ICipherBuilderWithKey encryptor; + private EncryptedValuePadder padder; + + /** + * Create a builder that makes EncryptedValue structures. + * + * @param wrapper a wrapper for key used to encrypt the actual data contained in the EncryptedValue. + * @param encryptor an output encryptor to encrypt the actual data contained in the EncryptedValue. + */ + public EncryptedValueBuilder(IKeyWrapper wrapper, ICipherBuilderWithKey encryptor) : this(wrapper, encryptor, null) + { + } + + /** + * Create a builder that makes EncryptedValue structures with fixed length blocks padded using the passed in padder. + * + * @param wrapper a wrapper for key used to encrypt the actual data contained in the EncryptedValue. + * @param encryptor an output encryptor to encrypt the actual data contained in the EncryptedValue. + * @param padder a padder to ensure that the EncryptedValue created will always be a constant length. + */ + public EncryptedValueBuilder(IKeyWrapper wrapper, ICipherBuilderWithKey encryptor, EncryptedValuePadder padder) + { + this.wrapper = wrapper; + this.encryptor = encryptor; + this.padder = padder; + } + + /** + * Build an EncryptedValue structure containing the passed in pass phrase. + * + * @param revocationPassphrase a revocation pass phrase. + * @return an EncryptedValue containing the encrypted pass phrase. + * @throws CrmfException on a failure to encrypt the data, or wrap the symmetric key for this value. + */ + public EncryptedValue Build(char[] revocationPassphrase) + { + return encryptData(padData(Strings.ToUtf8ByteArray(revocationPassphrase))); + } + + /** + * Build an EncryptedValue structure containing the certificate contained in + * the passed in holder. + * + * @param holder a holder containing a certificate. + * @return an EncryptedValue containing the encrypted certificate. + * @throws CrmfException on a failure to encrypt the data, or wrap the symmetric key for this value. + */ + public EncryptedValue Build(X509Certificate holder) + { + try + { + return encryptData(padData(holder.GetEncoded())); + } + catch (IOException e) + { + throw new CrmfException("cannot encode certificate: " + e.Message, e); + } + } + + /** + * Build an EncryptedValue structure containing the private key contained in + * the passed info structure. + * + * @param privateKeyInfo a PKCS#8 private key info structure. + * @return an EncryptedValue containing an EncryptedPrivateKeyInfo structure. + * @throws CrmfException on a failure to encrypt the data, or wrap the symmetric key for this value. + */ + public EncryptedValue Build(PrivateKeyInfo privateKeyInfo) + { + Pkcs8EncryptedPrivateKeyInfoBuilder encInfoBldr = new Pkcs8EncryptedPrivateKeyInfoBuilder(privateKeyInfo); + + AlgorithmIdentifier intendedAlg = privateKeyInfo.PrivateKeyAlgorithm; + AlgorithmIdentifier symmAlg = (AlgorithmIdentifier)encryptor.AlgorithmDetails; + DerBitString encSymmKey; + + try + { + Pkcs8EncryptedPrivateKeyInfo encInfo = encInfoBldr.Build(encryptor); + + encSymmKey = new DerBitString(wrapper.Wrap(((KeyParameter)encryptor.Key).GetKey()).Collect()); + + AlgorithmIdentifier keyAlg = (AlgorithmIdentifier)wrapper.AlgorithmDetails; + Asn1OctetString valueHint = null; + + return new EncryptedValue(intendedAlg, symmAlg, encSymmKey, keyAlg, valueHint, new DerBitString(encInfo.GetEncryptedData())); + } + catch (Exception e) + { + throw new CrmfException("cannot wrap key: " + e.Message, e); + } + } + + private EncryptedValue encryptData(byte[] data) + { + MemoryOutputStream bOut = new MemoryOutputStream(); + + Stream eOut = encryptor.BuildCipher(bOut).Stream; + + try + { + eOut.Write(data, 0, data.Length); + + eOut.Close(); + } + catch (IOException e) + { + throw new CrmfException("cannot process data: " + e.Message, e); + } + + AlgorithmIdentifier intendedAlg = null; + AlgorithmIdentifier symmAlg = (AlgorithmIdentifier)encryptor.AlgorithmDetails; + DerBitString encSymmKey; + + try + { + encSymmKey = new DerBitString(wrapper.Wrap(((KeyParameter)encryptor.Key).GetKey()).Collect()); + } + catch (Exception e) + { + throw new CrmfException("cannot wrap key: " + e.Message, e); + } + + AlgorithmIdentifier keyAlg = (AlgorithmIdentifier)wrapper.AlgorithmDetails; + Asn1OctetString valueHint = null; + DerBitString encValue = new DerBitString(bOut.ToArray()); + + return new EncryptedValue(intendedAlg, symmAlg, encSymmKey, keyAlg, valueHint, encValue); + } + + private byte[] padData(byte[] data) + { + if (padder != null) + { + return padder.GetPaddedData(data); + } + + return data; + } + } +} diff --git a/crypto/src/crmf/IEncryptedValuePadder.cs b/crypto/src/crmf/IEncryptedValuePadder.cs new file mode 100644 index 000000000..b620186dc --- /dev/null +++ b/crypto/src/crmf/IEncryptedValuePadder.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Crmf +{ + /** + * An encrypted value padder is used to make sure that prior to a value been + * encrypted the data is padded to a standard length. + */ + public interface EncryptedValuePadder + { + /** + * Return a byte array of padded data. + * + * @param data the data to be padded. + * @return a padded byte array containing data. + */ + byte[] GetPaddedData(byte[] data); + + /** + * Return a byte array of with padding removed. + * + * @param paddedData the data to be padded. + * @return an array containing the original unpadded data. + */ + byte[] GetUnpaddedData(byte[] paddedData); + } +} diff --git a/crypto/src/crypto/ICipher.cs b/crypto/src/crypto/ICipher.cs new file mode 100644 index 000000000..9041e61ad --- /dev/null +++ b/crypto/src/crypto/ICipher.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Org.BouncyCastle.Crypto +{ + /// <summary> + /// Base interface for a ciphers that do not require data to be block aligned. + /// <para> + /// Note: In cases where the underlying algorithm is block based, these ciphers may add or remove padding as needed. + /// </para> + /// </summary> + public interface ICipher + { + /// <summary> + /// Return the size of the output buffer required for a Write() plus a + /// close() with the write() being passed inputLen bytes. + /// <para> + /// The returned size may be dependent on the initialisation of this cipher + /// and may not be accurate once subsequent input data is processed as the cipher may + /// add, add or remove padding, as it sees fit. + /// </para> + /// </summary> + /// <returns>The space required to accommodate a call to processBytes and doFinal with inputLen bytes of input.</returns> + /// <param name="inputLen">The length of the expected input.</param> + int GetMaxOutputSize(int inputLen); + + /// <summary> + /// Return the size of the output buffer required for a write() with the write() being + /// passed inputLen bytes and just updating the cipher output. + /// </summary> + /// <returns>The space required to accommodate a call to processBytes with inputLen bytes of input.</returns> + /// <param name="inputLen">The length of the expected input.</param> + int GetUpdateOutputSize(int inputLen); + + /// <summary> + /// Gets the stream for reading/writing data processed/to be processed. + /// </summary> + /// <value>The stream associated with this cipher.</value> + Stream Stream { get; } + } +} diff --git a/crypto/src/crypto/ICipherBuilder.cs b/crypto/src/crypto/ICipherBuilder.cs new file mode 100644 index 000000000..5d4d1279c --- /dev/null +++ b/crypto/src/crypto/ICipherBuilder.cs @@ -0,0 +1,31 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto +{ + /// <summary> + /// Base interface for cipher builders. + /// </summary> + public interface ICipherBuilder + { + /// <summary> + /// Return the algorithm and parameter details associated with any cipher built. + /// </summary> + Object AlgorithmDetails { get ; } + + /// <summary> + /// Return the maximum output size that a given input will produce. + /// </summary> + /// <param name="inputLen">the length of the expected input.</param> + /// <returns>The maximum possible output size that can produced for the expected input length.</returns> + int GetMaxOutputSize (int inputLen); + + /// <summary> + /// Build a cipher that operates on the passed in stream. + /// </summary> + /// <param name="stream">The stream to write/read any encrypted/decrypted data.</param> + /// <returns>A cipher based around the given stream.</returns> + ICipher BuildCipher(Stream stream); + } +} + diff --git a/crypto/src/crypto/ICipherBuilderWithKey.cs b/crypto/src/crypto/ICipherBuilderWithKey.cs new file mode 100644 index 000000000..01a7a2caf --- /dev/null +++ b/crypto/src/crypto/ICipherBuilderWithKey.cs @@ -0,0 +1,14 @@ + +namespace Org.BouncyCastle.Crypto +{ + /// <summary> + /// A cipher builder that can also return the key it was initialized with. + /// </summary> + public interface ICipherBuilderWithKey: ICipherBuilder + { + /// <summary> + /// Return the key we were initialized with. + /// </summary> + ICipherParameters Key { get; } + } +} diff --git a/crypto/src/crypto/IDecryptorBuilderProvider.cs b/crypto/src/crypto/IDecryptorBuilderProvider.cs new file mode 100644 index 000000000..7f151e3ae --- /dev/null +++ b/crypto/src/crypto/IDecryptorBuilderProvider.cs @@ -0,0 +1,18 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /// <summary> + /// Interface describing a provider of cipher builders for creating decrypting ciphers. + /// </summary> + public interface IDecryptorBuilderProvider + { + /// <summary> + /// Return a cipher builder for creating decrypting ciphers. + /// </summary> + /// <param name="algorithmDetails">The algorithm details/parameters to use to create the final cipher.</param> + /// <returns>A new cipher builder.</returns> + ICipherBuilder CreateDecryptorBuilder (Object algorithmDetails); + } +} + diff --git a/crypto/src/crypto/Security.cs b/crypto/src/crypto/Security.cs new file mode 100644 index 000000000..716679044 --- /dev/null +++ b/crypto/src/crypto/Security.cs @@ -0,0 +1,108 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Paddings; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using System; +using System.Text; + +namespace crypto +{ + public class Security + { + // USAGE + + //var key = Security.GenerateText(32); + + //var iv = Security.GenerateText(16); + + //var encrypted = Security.Encrypt("MY SECRET", key, iv); + + //var dencrypted = Security.Decrypt(encrypted, key, iv); + + + /// <summary> + /// Return a salted hash based on PBKDF2 for the UTF-8 encoding of the argument text. + /// </summary> + /// <param name="text">Provided key text</param> + /// <param name="salt">Base64 encoded string representing the salt</param> + /// <returns></returns> + public static String ComputeHash(string text, string salt) + { + var data = Encoding.UTF8.GetBytes(text); + var sha = new Sha512Digest(); + var gen = new Pkcs5S2ParametersGenerator(sha); + + gen.Init(data, Convert.FromBase64String(salt), 2048); + + return Convert.ToBase64String(((KeyParameter)gen.GenerateDerivedParameters(sha.GetDigestSize() * 8)).GetKey()); + } + + public static String Decrypt(String cipherText, String key, String iv) + + { + + var cipher = CreateCipher(false, key, iv); + + var textAsBytes = cipher.DoFinal(Convert.FromBase64String(cipherText)); + + + + return Encoding.UTF8.GetString(textAsBytes, 0, textAsBytes.Length); + + } + + + + public static String Encrypt(String plainText, String key, String iv) + + { + + var cipher = CreateCipher(true, key, iv); + + + + return Convert.ToBase64String(cipher.DoFinal(Encoding.UTF8.GetBytes(plainText))); + + } + + + + public static String GenerateText(int size) + + { + + var textAsBytes = new Byte[size]; + + var secureRandom = SecureRandom.GetInstance("SHA256PRNG", true); + + + + secureRandom.NextBytes(textAsBytes); + + return Convert.ToBase64String(textAsBytes); + + } + + + + private static PaddedBufferedBlockCipher CreateCipher(Boolean isEncryption, String key, String iv) + + { + + var cipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(new RijndaelEngine()), new ISO10126d2Padding()); + + var keyParam = new KeyParameter(Convert.FromBase64String(key)); + + ICipherParameters cipherParams = String.IsNullOrEmpty(iv) ? (ICipherParameters)keyParam : new ParametersWithIV(keyParam, Convert.FromBase64String(iv)); + + cipher.Init(isEncryption, cipherParams); + + return cipher; + + } + } +} diff --git a/crypto/src/pkcs/Pkcs8EncryptedPrivateKeyInfo.cs b/crypto/src/pkcs/Pkcs8EncryptedPrivateKeyInfo.cs new file mode 100644 index 000000000..4c4ae83eb --- /dev/null +++ b/crypto/src/pkcs/Pkcs8EncryptedPrivateKeyInfo.cs @@ -0,0 +1,106 @@ + +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Utilities.IO; +using System; +using System.IO; + +namespace Org.BouncyCastle.Pkcs +{ + /// <summary> + /// A holding class for a PKCS#8 encrypted private key info object that allows for its decryption. + /// </summary> + public class Pkcs8EncryptedPrivateKeyInfo + { + private EncryptedPrivateKeyInfo encryptedPrivateKeyInfo; + + private static EncryptedPrivateKeyInfo parseBytes(byte[] pkcs8Encoding) + { + try + { + return EncryptedPrivateKeyInfo.GetInstance(pkcs8Encoding); + } + + catch (ArgumentException e) + { + throw new PkcsIOException("malformed data: " + e.Message, e); + } + catch (Exception e) + { + throw new PkcsIOException("malformed data: " + e.Message, e); + } + } + + /// <summary> + /// Base constructor from a PKCS#8 EncryptedPrivateKeyInfo object. + /// </summary> + /// <param name="encryptedPrivateKeyInfo">A PKCS#8 EncryptedPrivateKeyInfo object.</param> + public Pkcs8EncryptedPrivateKeyInfo(EncryptedPrivateKeyInfo encryptedPrivateKeyInfo) + { + this.encryptedPrivateKeyInfo = encryptedPrivateKeyInfo; + } + + /// <summary> + /// Base constructor from a BER encoding of a PKCS#8 EncryptedPrivateKeyInfo object. + /// </summary> + /// <param name="encryptedPrivateKeyInfo">A BER encoding of a PKCS#8 EncryptedPrivateKeyInfo objects.</param> + public Pkcs8EncryptedPrivateKeyInfo(byte[] encryptedPrivateKeyInfo) : this(parseBytes(encryptedPrivateKeyInfo)) + { + + } + + /// <summary> + /// Returns the underlying ASN.1 structure inside this object. + /// </summary> + /// <returns>Return the EncryptedPrivateKeyInfo structure in this object.</returns> + public EncryptedPrivateKeyInfo ToAsn1Structure() + { + return encryptedPrivateKeyInfo; + } + + /// <summary> + /// Returns a copy of the encrypted data in this structure. + /// </summary> + /// <returns>Return a copy of the encrypted data in this object.</returns> + public byte[] GetEncryptedData() + { + return encryptedPrivateKeyInfo.GetEncryptedData(); + } + + /// <summary> + /// Return a binary ASN.1 encoding of the EncryptedPrivateKeyInfo structure in this object. + /// </summary> + /// <returns>A byte array containing the encoded object.</returns> + public byte[] GetEncoded() + { + return encryptedPrivateKeyInfo.GetEncoded(); + } + + /// <summary> + /// Get a decryptor from the passed in provider and decrypt the encrypted private key info, returning the result. + /// </summary> + /// <param name="inputDecryptorProvider">A provider to query for decryptors for the object.</param> + /// <returns>The decrypted private key info structure.</returns> + public PrivateKeyInfo DecryptPrivateKeyInfo(IDecryptorBuilderProvider inputDecryptorProvider) + { + try + { + ICipherBuilder decryptorBuilder = inputDecryptorProvider.CreateDecryptorBuilder(encryptedPrivateKeyInfo.EncryptionAlgorithm); + + ICipher encIn = decryptorBuilder.BuildCipher(new MemoryInputStream(encryptedPrivateKeyInfo.GetEncryptedData())); + + using (Stream strm = encIn.Stream) + { + byte[] data = Streams.ReadAll(encIn.Stream); + + return PrivateKeyInfo.GetInstance(data); + } + } + catch (Exception e) + { + throw new PkcsException("unable to read encrypted data: " + e.Message, e); + } + } + } +} diff --git a/crypto/src/pkcs/Pkcs8EncryptedPrivateKeyInfoBuilder.cs b/crypto/src/pkcs/Pkcs8EncryptedPrivateKeyInfoBuilder.cs new file mode 100644 index 000000000..3b05deea7 --- /dev/null +++ b/crypto/src/pkcs/Pkcs8EncryptedPrivateKeyInfoBuilder.cs @@ -0,0 +1,52 @@ +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Utilities.IO; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Org.BouncyCastle.Pkcs +{ + public class Pkcs8EncryptedPrivateKeyInfoBuilder + { + private PrivateKeyInfo privateKeyInfo; + + public Pkcs8EncryptedPrivateKeyInfoBuilder(byte[] privateKeyInfo): this(PrivateKeyInfo.GetInstance(privateKeyInfo)) + { + } + + public Pkcs8EncryptedPrivateKeyInfoBuilder(PrivateKeyInfo privateKeyInfo) + { + this.privateKeyInfo = privateKeyInfo; + } + + /// <summary> + /// Create the encrypted private key info using the passed in encryptor. + /// </summary> + /// <param name="encryptor">The encryptor to use.</param> + /// <returns>An encrypted private key info containing the original private key info.</returns> + public Pkcs8EncryptedPrivateKeyInfo Build( + ICipherBuilder encryptor) + { + try + { + MemoryStream bOut = new MemoryOutputStream(); + ICipher cOut = encryptor.BuildCipher(bOut); + byte[] keyData = privateKeyInfo.GetEncoded(); + + using (var str = cOut.Stream) + { + str.Write(keyData, 0, keyData.Length); + } + + return new Pkcs8EncryptedPrivateKeyInfo(new EncryptedPrivateKeyInfo((AlgorithmIdentifier)encryptor.AlgorithmDetails, bOut.ToArray())); + } + catch (IOException) + { + throw new InvalidOperationException("cannot encode privateKeyInfo"); + } + } + } +} diff --git a/crypto/src/pkcs/PkcsException.cs b/crypto/src/pkcs/PkcsException.cs new file mode 100644 index 000000000..f82d36724 --- /dev/null +++ b/crypto/src/pkcs/PkcsException.cs @@ -0,0 +1,18 @@ +using System; + +namespace Org.BouncyCastle.Pkcs +{ + /// <summary> + /// Base exception for PKCS related issues. + /// </summary> + public class PkcsException : Exception + { + public PkcsException(String message) : base(message) + { + } + + public PkcsException(String message, Exception underlying) : base(message, underlying) + { + } + } +} diff --git a/crypto/src/pkcs/PkcsIOException.cs b/crypto/src/pkcs/PkcsIOException.cs new file mode 100644 index 000000000..19f17a394 --- /dev/null +++ b/crypto/src/pkcs/PkcsIOException.cs @@ -0,0 +1,19 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Pkcs +{ + /// <summary> + /// Base exception for parsing related issues in the PKCS namespace. + /// </summary> + public class PkcsIOException: IOException + { + public PkcsIOException(String message) : base(message) + { + } + + public PkcsIOException(String message, Exception underlying) : base(message, underlying) + { + } + } +} diff --git a/crypto/src/util/io/MemoryInputStream.cs b/crypto/src/util/io/MemoryInputStream.cs new file mode 100644 index 000000000..d353314ee --- /dev/null +++ b/crypto/src/util/io/MemoryInputStream.cs @@ -0,0 +1,13 @@ +using System.IO; + +namespace Org.BouncyCastle.Utilities.IO +{ + public class MemoryInputStream : MemoryStream + { + public MemoryInputStream(byte[] buffer) : base(buffer, false) + { + } + + public sealed override bool CanWrite { get { return false; } } + } +} diff --git a/crypto/src/util/io/MemoryOutputStream.cs b/crypto/src/util/io/MemoryOutputStream.cs new file mode 100644 index 000000000..a6de64680 --- /dev/null +++ b/crypto/src/util/io/MemoryOutputStream.cs @@ -0,0 +1,10 @@ + +using System.IO; + +namespace Org.BouncyCastle.Utilities.IO +{ + public class MemoryOutputStream: MemoryStream + { + public sealed override bool CanRead { get { return false; } } + } +} |