I have a randomly generated 128 bit guid (cryptographically secure). How can I use this as a seed to generate a public and private key for Bitcoin, using C#? By seed, I mean that every time I use the same guid as input, it should result in the same public/private keys.
I have looked at NBitcoin, but don't understand how to pull it off.
You can directly create 32 random bytes to be your private key. Example below. But it is VERY important: these 32 bytes must be from cryptographically-secure pseudorandom generator. For example, if you use C# built-in Random class, anyone will be able to restore your private keys with regular computer. You need to be very careful if you plan to use this in real bitcoin network. I am not sure if Guid generation is cryptographically-secure.
static void Main(string[] args)
{
byte[] GetRawKey()
{
// private key=1 in this example
byte[] data = new byte[32];
data[^1] = 1;
return data;
}
var key = new Key(GetRawKey()); // private key
var pair = key.CreateKeyPair(); // key pair (pub+priv keys)
var addrP2pkh = key.GetAddress(ScriptPubKeyType.Legacy, Network.Main); // P2PKH address
var addrP2sh = key.GetAddress(ScriptPubKeyType.SegwitP2SH, Network.Main); // Segwit P2SH address
Console.WriteLine(addrP2pkh.ToString());
Console.WriteLine(addrP2sh.ToString());
}
Related
I'm trying to implement signed URLs for short lived access to static files.
The idea is:
generate an URL with an expiration timestamp (e.g. https://example.com/file.png?download=false&expires=1586852158)
sign it with HMACSHA256 and a shared secret and append the signature at the end of URL (e.g. https://example.com/file.png?download=false&expires=1586852158&signature=6635ea14baeeaaffe71333cf6c7fa1f0af9f6cd1a17abb4e75ca275dec5906d1
When i receive the request on the server, I take out the signature parameter and verify that the rest of the URL signed with HMACSHA256 and the same shared secret results in the same signature.
The implementation is as follows:
public static class URLSigner
{
private static string GetSignatureForUri(string uri, byte[] key)
{
var hmac = new HMACSHA256(key);
var signature = hmac.ComputeHash(Encoding.UTF8.GetBytes(uri));
var hexSignature = BitConverter.ToString(signature).Replace("-", string.Empty).ToLowerInvariant();
return hexSignature;
}
public static string SignUri(string uri, byte[] key)
{
var hexSignature = GetSignatureForUri(uri, key);
return QueryHelpers.AddQueryString(uri, new Dictionary<string, string> { { "signature", hexSignature }});
}
public static bool VerifyUri(string uri, byte[] key)
{
var signatureRegex = "[\\?&]signature=([a-z0-9]+)$";
var signatureMatch = Regex.Match(uri, signatureRegex);
if (!signatureMatch.Success || signatureMatch.Groups.Count != 2)
return false;
var parsedSignature = signatureMatch.Groups[1].Value;
var originalUri = Regex.Replace(uri, signatureRegex, "");
var hexSignature = GetSignatureForUri(originalUri, key);
return hexSignature == parsedSignature;
}
}
and it's used like so:
var signedUri = URLSigner.SignUri("https://example.com/file.png?download=false", secretKey);
var isVerified = URLSigner.VerifyUri(signedUri, secretKey);
Is this implementation of signed URLs reasonably secure?
Your implementation seems to be missing the verification of the expiration time, so any one key would currently work indefinitely.
Otherwise, I don't see anything wrong with this approach in general. You may want to add in a key beyond just the timestamp for identifying the user or request in some way though.
Here's a good article on how the general approach is used for one time passwords which is essentially what you are doing.
https://www.freecodecamp.org/news/how-time-based-one-time-passwords-work-and-why-you-should-use-them-in-your-app-fdd2b9ed43c3/
Yes, it is secure, as long as the key is treated properly. The hash should be able to ensure data integrity (data in URL are not modified by other people).
Perhaps, one little improvement is to dispose the HMACSHA256 object (maybe by using), but that may not be related to security.
I have one concern. You are saying you want to use HMACSHA256 and a private key, but in security terminology what you're passing to the HMAC is not a private key, it's a shared secret.
If you have to had a public, private key for your sign and verify authentication, I would suggest using the RSACryptoServiceProvider. With RSA you have two keys, public key and private key.
Your client creates a private key and keep it and give its public key to the server. So only client can sign and anyone with public key can verify it.
On another note, no matter what algorithm you ended up using, I would suggest to add the signature to a authorization header instead of query string. This is more common and you don't need to match a regex in each request.
I use this snippet to see private and public keys generated by DSA:
byte[] publicKey, hash, signedHash;
string strToSign = "Hello, world!";
SHA512Managed shaComputer = new SHA512Managed();
using (ECDsaCng dsaSigner = new ECDsaCng())
{
publicKey = dsaSigner.Key.Export(CngKeyBlobFormat.GenericPublicBlob);
Console.WriteLine($"DSA public key: {TransformHash(publicKey)}");
Console.WriteLine();
byte[] privateKey = dsaSigner.Key.Export(CngKeyBlobFormat.GenericPrivateBlob);
Console.WriteLine($"DSA private key: {TransformHash(privateKey)}");
Console.WriteLine();
}
But I see theese keys look very similar, because private key starts with public key:
Is that normal?
"because private key starts with public key
I believe this is the case. This is documented for GenericPrivateBlob:
https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cngkeyblobformat.genericprivateblob?redirectedfrom=MSDN&view=netframework-4.7.2
A generic private key BLOB can contain a private key of any type and does not necessarily contain the corresponding public key. The type of key that the BLOB contains can be determined only by examining the BLOB.
Emphasis mine, however in this case it does look like the public key is prepended before the private key.
I am using NBitcoin nuget package. I try to create private and pub key(address). And after that , I try to sign to some message and then verify the this signature with pub key. But NBitcoin , for the verify using the BitcoinSecret object which is the has private key object. So , why for the verify NBitcoin using this object? And How can I verify signature without private key , just using address(pubKey),signature and message ?
Thanks
static void Main(string[] args)
{
Key Key = new Key(); //Create private key
//We can take private key
var privateKey = Key.GetBitcoinSecret(Network.Main);
//Get the public key, and derive the address on the Main network
BitcoinAddress address = privateKey.PubKey.GetAddress(Network.Main);
For the sign to data , create secret object.
BitcoinSecret secret = new BitcoinSecret(Key, Network.Main);
string message = $"I am Nicolas";
Console.WriteLine("Message:" + message + "\n");
sign message with private key.
string signature = secret.PrivateKey.SignMessage(message);
Console.WriteLine("Signature:" + signature + "\n");
/* Now I dont understand this code. For the verify , I know that we need
to signature message , message and pub or adres value.\n But in this code using
again private secret object which has got private key. How we can verify
signature messaga with pub or address and message (dont include private key)*/
if (secret.PubKey.VerifyMessage(message, signature))
{
Console.WriteLine("thats okey");
}
Console.Read();
}
The public key can not exist without the private key, as the public key is derived from the private key by utilizing some kind of one-way-function. If you want to use the public key without the private key, then generate it from the private key, like you did
var pubKey = privateKey.PubKey;
store the public key to some location the verifyer has access to
File.WriteAllBytes("some/public/location/MyPubKey.key", pubKey.ToBytes());
let the verifyer read the public key without ever knowing of the private key
var pubKeyForVerification = File.ReadAllBytes("some/public/location/MyPubKey.key");
This is all there is to it. It is safe to store the public key anywhere you want, because it's practically impossible to learn the private key from it.
I'm provided a private key (a string). I have to generate a public key by that private key to encrypt data.
I don't know how to do. Please help me. Thank you.
Simply by having the private key you can not generate a public key.
Private and public keys are generated in pair and should be provided to you for encrypting data.
However you still can sign data using private key alone.
var keypair = "Your keypair in xml format";
using (var rsa = new RSACryptoServiceProvider()) {
rsa.FromXmlString(keypair);
var publicKeyInXmlFormat = rsa.ToXmlString(false);
}
I'm trying to asymetrically encrypt a message of arbitrary length with bouncycastle. (1.4+ with C#)
This is the code I have right now. It is supposed to (but doesn't) generate a CMS message where the data itself is encrypted with AES256 with a random key and the key is encrypted with the public key from keyPair.
keyPair is an RSA-Key (RsaKeyParameters)
public static byte[] Encrypt(byte[] input, AsymmetricCipherKeyPair keyPair)
{
CmsEnvelopedDataGenerator generator = new CmsEnvelopedDataGenerator();
// those two lines are certainly wrong.
// I have no idea what the subKeyID parameter does
byte[] subKeyId = new byte[] {};
generator.AddKeyTransRecipient(keyPair.Public, subKeyId);
CmsProcessableByteArray cmsByteArray = new CmsProcessableByteArray(input);
CmsEnvelopedData envelopeData =
generator.Generate(cmsByteArray, CmsEnvelopedDataGenerator.Aes256Cbc);
return envelopeData.GetEncoded();
}
What is the subKeyId parameter in the Encrypt method for and what value does it need to have?
aaronls is being a little unfair to the author of "Beginning cryptography with Java", who after all wrote all the unit tests himself in the first place...
As other commenters have pointed out, CMS works with certificates, you can't just pass a public key; it must be possible to refer to the key either by "SubjectKeyIdentifier" or by "IssuerAndSerialNumber". The two alternatives of AddKeyTransRecipient allow this. If these terms don't mean anything to you, you probably need to do some background reading on X.509.
Look at the function TryKekAlgorithm in the EnvelopedDataTest.cs file of the BouncyCastle source. Instead of doing AddKeyTransRecipient, they are doing AddKekRecipient.
public static byte[] Encrypt(byte[] input, AsymmetricCipherKeyPair keyPair)
{
CmsEnvelopedDataGenerator generator = new CmsEnvelopedDataGenerator();
DerObjectIdentifier algOid = //initialize
//Still trying to figure out kekId here.
byte[] kekId = new byte[] { 1, 2, 3, 4, 5 };
string keyAlgorithm = ParameterUtilities.GetCanonicalAlgorithmName("AES256");
generator.AddKekRecipient(keyAlgorithm, keyPair.Public, kekId);
CmsProcessableByteArray cmsByteArray = new CmsProcessableByteArray(input);
CmsEnvelopedData envelopeData =
generator.Generate(cmsByteArray, CmsEnvelopedDataGenerator.Aes256Cbc);
return envelopeData.GetEncoded();
}
Edit: I think the kekId is just a unique identifier used to reference the key. Just a way to "name" the key. So you can have a book of keys, and each one has an identifier. When you send an encrypted message, the unencrypted key identifier tells you which of the keys was used to encrypt the message.
Here is a pretty good explanation of key identifiers on page 140:
[http://books.google.com/books?id=Pgg-Es2j3UEC&pg=PA140&lpg=PA140&dq=understanding+key+identifiers+encrypt&source=bl&ots=nFg0BzM2ht&sig=Ux5sreXMKyuEEZu0uaxE7cXC1VI&hl=en&ei=JKKJStbHGJivtgffsNznDA&sa=X&oi=book_result&ct=result&resnum=6#v=onepage&q=&f=false][1]
And here is another book that is using BouncyCastleCrypto, but it looks like they did little more than rip off the unit test source code. They have explained it a little:
[http://books.google.com/books?id=WLLAD2FKH3IC&pg=PA343&lpg=PA343&dq=CmsEnvelopedDataGenerator+AddKekRecipient&source=bl&ots=O9HinJm3yB&sig=K5Z99DIVWW4-0abPIFR7x4lzBhU&hl=en&ei=g6aJSrjeDuHktgennNjnDA&sa=X&oi=book_result&ct=result&resnum=6#v=onepage&q=CmsEnvelopedDataGenerator%20AddKekRecipient&f=false][2]
To use AES, it is not enough to use a AsymmetricCipherKeyPair.
You should use a X509 certificate, where the public key is signed by an certificate authority (CA).
the subKeyId is an attribute of the certificate, the subject Key Identifier:
(X509Certificate) cert.getSubjectUniqueID()
To encrypt a message of artrary length, you should use AES only to exchange a symmetric Keypassword and use this key for symmetric encryption.