ganache-cli how to read private key from account json file - ethereum

I am running ganache-cli through a node application:
const ganache = require('ganache-core');
const ethers = require('ethers');
const provider = new ethers.providers.Web3Provider(
ganache.provider({
total_accounts: 5,
account_keys_path: './accounts.json',
gasPrice: 20000000000,
gasLimit: 20000000000,
default_balance_ether: 100
})
);
This runs the ganache-cli and output acocunt details in accounts.json. The file looks like this:
{
"addresses":{
"0x73f5b3f74db1b37927696c280c04d544f4e9ff64":{
"secretKey":{
"type":"Buffer",
"data":[88, 17, .....]
},
"publicKey":{
"type":"Buffer",
"data":[13, 52, .....]
},
"address":"0x73f5b3f74db1b37927696c280c04d544f4e9ff64",
"account":{
"nonce":"0x",
"balance":"0x056bc75e2d63100000",
"stateRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"codeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
}
}
}
}
I can see the account address, but how can I decode/get the private key from this information?

You cannot get the private key from accounts directly, but there's a few workarounds to do this with ganache-cli.
Specify a mnemonic phrase with the -m option, e.g. ganache-cli -m "stereo consider quality wild fat farm symptom bundle laundry side one lemon", this will derive private keys from the mnemonic phrase (with the derivation path m/44'/60'/0'/0/n.
Use the --account_keys_path option to save all private keys to a file, e.g. ganache-cli --account_keys_path keys.json. This will result in a JSON file with all addresses, private keys and public keys.
Use the --account option to manually specify a private key and balance, e.g. ganache-cli --account "0x31c354f57fc542eba2c56699286723e94f7bd02a4891a0a7f68566c2a2df6795,1000000000000000000". This will assign 1 ETH (= 1000000000000000000 Wei) to the address corresponding to this private key. You can use the --account option multiple times, with different private keys.

Related

How to generate arbitrary wallet seeded with eth in hardhat tests using ethers.js?

I'm currently trying to run a test in hardhat/waffle that requires hundreds of unique wallets to call a contract, using new ethers.Wallet.createRandom(). However, none of these wallets are supplied with eth, so I can't call/send transactions with them.
What would be the simplest/most effective way to supply an arbitrary amount of randomly generated wallets with eth? Thanks!
I think you'll probably want to use the hardhat network method hardhat_setBalance, the docs use an example like this:
await network.provider.send("hardhat_setBalance", [
"<ACCOUNT ADDRESS>",
"0x1000", # 4096 wei
]);
I'm not developing in javascript though, but I've been doing something similar in python using web3.py and eth-account with code like this:
from web3 import Web3
from eth_account import Account
chain = Web3(HTTPProvider("http://127.0.0.1:8545"))
acct = Account.create('<RANDOM VALUE>')
address = Web3.toChecksumAddress(acct.address)
print(chain.eth.get_balance(address)) # 0 wei
# add a balance of eth tokens to the address
chain.provider.make_request("hardhat_setBalance", [address, "0x1000"])
print(chain.eth.get_balance(address)) # 4096 wei
Hardhat allows to seed a custom amount of prefunded addresses using the accounts.count config option of the built-in hardhat network.
Example:
module.exports = {
networks: {
hardhat: {
accounts: {
count: 1000
}
}
}
}
Docs: https://hardhat.org/hardhat-network/reference/#config
The new ethers.Wallet.createRandom() function only creates an account from a random private key in the context of the JS application, but it doesn't communicate with the provider (Hardhat in your case).
saw this answer, and i think it solves the problem:
https://github.com/ethers-io/ethers.js/issues/686
const wallet = ethers.Wallet.createRandom().connect(provider);

Can I import NFT's metadata into a smart contract?

When we want to mint an NFT with its own metadata, the asset file and JSON file should be uploaded to IPFS before the minting.
And we mint a new NFT by sending transactions to the smart contract and within the transaction, the token URI(hashed URI from IPFS) would be set as JSON metadata URI of the token.
Now I am wondering whether there is a way to import the attributes from JSON on IPFS into a smart contract and use the data like const variables in the contract.
You can do it but it is going to cost you alot. You need to create a mapping in smart contract.
mapping(string ->Metadata) private tokenURIToMetadata
Since it is mapped from string, it cannot be public. so create a function
function getMetadata(string tokenUri) public returns(Metadata){
return tokenURIToMetadata[tokenUri]
}
also create Metadata struct:
struct Metadata{
string name;
string description;
string image;
}
And then you have to add new medata to the mapping:
function addMetada(string memory tokenUri,?????????){}
Since we cannot pass json object as an argument to the addMetada, now you have to create a separate mapping for each property of the metadata object. So the cost will be crazy amount.
Instead, you could write the metadata to the filesystem or database but this would not be a good option.

JSON Web Signature (Ninbus-JOSE-JWT)

I want to do a project with JSON Web Signature (JWS) and i want to send the public key of the certificate used for the signature so that the message can be validated once received with this public key. I am using the Ninbus JOS JWT library. I can sign the JSON object and I can see the public key, but i can not validate it correctly.
This is the code:
// Create RSA-signer with the private key
JWSSigner signer = new RSASSASigner(_signatureKey_); // PrivateKey
com.nimbusds.jose.util.Base64 b64 = new com.nimbusds.jose.util.Base64(_x509certificate.toString()); // X509Certificate
ArrayList<com.nimbusds.jose.util.Base64> certificados = new ArrayList<com.nimbusds.jose.util.Base64>();
certificados.add(b64);
RSAPublicKey _rsaPublicKey = (RSAPublicKey)_x509certificate.getPublicKey(); // Get the public key of the X509Certificate
RSAKey jwk = new com.nimbusds.jose.jwk.RSAKey.Builder( new Base64URL( _rsaPublicKey.getModulus().toString()), new Base64URL( _rsaPublicKey.getPublicExponent().toString()))
.x509CertChain(certificados)
.build();
JWSHeader _jwsHeader = new JWSHeader.Builder(JWSAlgorithm.RS256).
x509CertChain(certificados).
jwk(jwk).
build();
// Prepare JWS object with simple string as payload
JWSObject jwsObject = new JWSObject(_jwsHeader, new Payload(_jsonObject));
// Compute the RSA signature
jwsObject.sign(signer);
// Validation OK : This validation works
JWSVerifier verifier = new RSASSAVerifier(_rsaPublicKey);
boolean signatureValid = jwsObject.verify(verifier); // ---> True, OK
// Now I want to validate the JWSObject getting the public key from the same JWSObject. This validation Fails
JWK _jwk = jwsObject.getHeader().getJWK();
RSAKey _rsakey = (RSAKey)_jwk;
RSAPublicKey _rsaPublicKey2 = _rsakey.toRSAPublicKey();
JWSVerifier verifier2 = new RSASSAVerifier(_rsakey.toRSAPublicKey());
boolean verificado2 = jwsObject.verify(verifier2); // False!
// Another option, this fails too
RSAKey __rsaKey2 = new com.nimbusds.jose.jwk.RSAKey.Builder( _rsakey.toRSAPublicKey() ).x509CertChain(_jwk.getX509CertChain()).build();
JWSVerifier verifier3 = new RSASSAVerifier(__rsaKey2);
boolean verificado3 = jwsObject.verify(verifier3); // False!
The _rsaPublicKey is: "Sun RSA public key, 2048 bits", but when i get it from the JWK (_rsaPublicKey2), I get "Sun RSA public key, 3696 bits" and i don't know why.
Thanks!
On the recipient side, do you validate the X.509 certificate issuer, subject and chain before trusting the key? Signature validation must not be attempted before the recipient is certain that it can trust the certificate included in the JWS.
Another note: Do not include the public JWK in the JWS header. This should only be used for ephemeral public keys in ECDH (a different alg used for JWE). Passing the certificate chain in the JWS header is sufficient, but you must validate it / find out if the certificate can be trusted, before using its public key.
The library will not validate / find out if the certificate can be trusted for you!
If the second signature validation fails, then probably the key used to sign the JWS and the one that came with the X.509 certificate are not the same (as suggested by the different reported lengths - 2048 bits vs. 3696 bits).

Bare Metal Cloud - How to set authorized ssh keys for compute instances?

I have successfully provisioned Bare Metal Cloud compute instances using the following code:
public static Instance createInstance(
ComputeClient computeClient,
String compartmentId,
AvailabilityDomain availabilityDomain,
String instanceName,
Image image,
Shape shape,
Subnet subnet
) {
LaunchInstanceResponse response = computeClient.launchInstance(
LaunchInstanceRequest.builder()
.launchInstanceDetails(
LaunchInstanceDetails.builder()
.availabilityDomain(availabilityDomain.getName())
.compartmentId(compartmentId)
.displayName(instanceName)
.imageId(image.getId())
.shape(shape.getShape())
.subnetId(subnet.getId())
.build())
.build());
return response.getInstance();
}
However, I can't SSH into any instances I create via the code above, because there's no parameter on launchInstance to pass in the public key of my SSH keypair.
How can I tell the instance what SSH public key to allow? I know it must be possible somehow since the console UI allows me to provide the SSH public key as part of instance creation.
According to the launch instance API documentation, you need to pass your SSH public key via the ssh_authorized_keys field of the metadata parameter:
Providing Cloud-Init Metadata
You can use the following metadata key names to provide information to Cloud-Init:
"ssh_authorized_keys" - Provide one or more public SSH keys to be
included in the ~/.ssh/authorized_keys file for the default user on
the instance. Use a newline character to separate multiple keys. The
SSH keys must be in the format necessary for the authorized_keys file
The code for this in the Java SDK looks like this:
public static Instance createInstance(
ComputeClient computeClient,
String compartmentId,
AvailabilityDomain availabilityDomain,
String instanceName,
Image image,
Shape shape,
Subnet subnet
) {
String sshPublicKey = "ssh-rsa AAAAB3NzaC1y...key shortened for example...fdK/ABqxgH7sy3AWgBjfj some description";
Map<String, String> metadata = new HashMap<>();
metadata.put("ssh_authorized_keys", sshPublicKey);
LaunchInstanceResponse response = computeClient.launchInstance(
LaunchInstanceRequest.builder()
.launchInstanceDetails(
LaunchInstanceDetails.builder()
.availabilityDomain(availabilityDomain.getName())
.compartmentId(compartmentId)
.displayName(instanceName)
.imageId(image.getId())
.metadata(metadata)
.shape(shape.getShape())
.subnetId(subnet.getId())
.build())
.build());
return response.getInstance();
}
Then the instance will allow you to SSH to it using the SSH keypair for that public key.

Can't DER encode and BER decode RSA public key

I have problems using Crypto++ to save a RSA public key (that I obtained loading a private key file in PKCS#8 format). When decoding the key, I always get a BERDecodeErr exception.
Here is the code I am using:
CryptoPP::RSASSA_PKCS1v15_SHA_Signer _signer;
CryptoPP::RSASSA_PKCS1v15_SHA_Verifier _verifier;
CryptoPP::ByteQueue bytes;
//_signer.AccessPublicKey().Save(bytes); // seem to save private key instead
_signer.AccessKey().DEREncodePublicKey(bytes);
//_verifier.AccessKey().Load(bytes);
//_verifier.AccessKey().BERDecodePublicKey(bytes, 0, 0);
_verifier.AccessPublicKey().Load(bytes);
I also tried with the instructions commented above, without success.
How do you do to save or open the public key?
The public key looks like this in hex format, is there a tool to check its format / validity (regarding what crypto++ supports) ?
3081890281810097e24f2e95504a397e90fbc56d1b330ab2ab97a0d326007b890e40013f9e1d9bd9
f54b0c0840782ddae19b5b4595d8f8b9ffe0d2120174fcbc39585c5867cd2dfba69f8e540caa2c52
de8f08278a34e9249120500117f0ba756c5bb2be660013160db9f82f75deb7ccf63742a9e945da6c
cf30c2b109b73342daaabd02b872e50203010001
I'm not sure I understand your problem completely. But you look like you are on the right track with using either Load/Save or BERDecodePublicKey/DEREncodePublicKey.
Here's how I would approach it given you have a PKCS#8 encoded private key.
FileSource privateKey("<private key>", true);
RSASSA_PKCS1v15_SHA_Signer signer;
signer.AccessKey().Load(privateKey);
AutoSeededRandomPool prng;
bool valid = signer.AccessKey().Validate(prng, 3);
...
RSASSA_PKCS1v15_SHA_Verifier verifier(signer);
FileSink publicKey("<public key>", true);
verifier.AccessKey().Save(publicKey);
Then, you can use Gutmann's dumpasn1 to print it:
$ dumpasn1 <public key>
...
I believe you can also convert a private key/signer to a public key/verifier with:
RSASSA_PKCS1v15_SHA_Signer signer;
signer.AccessKey().Load(privateKey);
RSASSA_PKCS1v15_SHA_Verifier verifier;
signer.MakePublic(verifier);
There's also a page on the Crypto++ wiki that talks about it in greater detail: Keys and Formats. And there's a page dedicated to PEM encoding, if interested: PEM Pack. If you want the PEM encoding, you have to compile the library yourself, though.
Here's the code I used with the public key you posted. It had no problems.
string key = "3081890281810097e24f2e95504a397e90fbc56d1b330ab2ab97a0d326007b890e40013f9e1d9bd9 \
f54b0c0840782ddae19b5b4595d8f8b9ffe0d2120174fcbc39585c5867cd2dfba69f8e540caa2c52 \
de8f08278a34e9249120500117f0ba756c5bb2be660013160db9f82f75deb7ccf63742a9e945da6c \
cf30c2b109b73342daaabd02b872e50203010001";
ByteQueue queue;
StringSource ss(key, true, new HexDecoder(new Redirector(queue)));
RSASSA_PKCS1v15_SHA_Verifier verifier;
verifier.AccessKey().BERDecodePublicKey(queue, false, 0);
AutoSeededRandomPool prng;
bool result = verifier.AccessKey().Validate(prng, 3);
if(!result)
throw Exception(Exception::OTHER_ERROR, "Failed to validate public key");
If you install the PEM Pack then you can add the following:
FileSink sink("public-key.pem", true);
PEM_Save(sink, verifier.GetKey());
That will get you:
$ cat public-key.pem
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCX4k8ulVBKOX6Q+8VtGzMKsquX
oNMmAHuJDkABP54dm9n1SwwIQHgt2uGbW0WV2Pi5/+DSEgF0/Lw5WFxYZ80t+6af
jlQMqixS3o8IJ4o06SSRIFABF/C6dWxbsr5mABMWDbn4L3Xet8z2N0Kp6UXabM8w
wrEJtzNC2qq9Arhy5QIDAQAB
-----END PUBLIC KEY-----
And:
$ openssl rsa -in public-key.pem -pubin -text -noout
Public-Key: (1024 bit)
Modulus:
00:97:e2:4f:2e:95:50:4a:39:7e:90:fb:c5:6d:1b:
33:0a:b2:ab:97:a0:d3:26:00:7b:89:0e:40:01:3f:
9e:1d:9b:d9:f5:4b:0c:08:40:78:2d:da:e1:9b:5b:
45:95:d8:f8:b9:ff:e0:d2:12:01:74:fc:bc:39:58:
5c:58:67:cd:2d:fb:a6:9f:8e:54:0c:aa:2c:52:de:
8f:08:27:8a:34:e9:24:91:20:50:01:17:f0:ba:75:
6c:5b:b2:be:66:00:13:16:0d:b9:f8:2f:75:de:b7:
cc:f6:37:42:a9:e9:45:da:6c:cf:30:c2:b1:09:b7:
33:42:da:aa:bd:02:b8:72:e5
Exponent: 65537 (0x10001)
Finally, the difference between:
verifier.AccessKey(): gets the RSA::Public key, the key is non-const
verifier.GetKey(): gets the RSA::Public key, the key is const