Vytek
February 10, 2020, 2:51pm
1
Hello everyone,
I have notice that only in JS SDK a user can sign transaction offline and then use external algod to propagate the signed TX.
See: https://developer.algorand.org/docs/javascript-sdk#node-example-sign
What SDK do you use in your Algorand Wallet? We would to create a simple algorand client that can:
Create accout
Create and sign a transaction (offline for security reason!!)
And the transmit transaction to public algo node
Is it correct that in GO, JAVA and PYTHON SDK can’t sign offline, but only using and external kmd?
Thank you for your answer.
Please let me know.
Enrico
JasonW
February 10, 2020, 3:38pm
2
All the SDKs can sign transaction offline and provide the ability to generate accounts that are not managed by the kmd of a node. A new set of docs will be available this week on the developer site that cover using all of the sdks to do offline transactions. What language are you developing in?
Vytek
February 10, 2020, 3:50pm
3
This is the problem! In Android we use Java (for Android) for iOS Swift
Can we use Algorand Java SDK in Android App?
1 Like
fabrice
February 10, 2020, 4:04pm
4
JasonW
February 10, 2020, 4:17pm
5
Here is a java example of doing both unsigned and signed in Java. I have not tried with Android:
package com.algorand.javatest;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Paths;
import com.algorand.algosdk.account.Account;
import com.algorand.algosdk.algod.client.AlgodClient;
import com.algorand.algosdk.algod.client.api.AlgodApi;
import com.algorand.algosdk.algod.client.auth.ApiKeyAuth;
import com.algorand.algosdk.algod.client.model.TransactionID;
import com.algorand.algosdk.algod.client.model.TransactionParams;
import com.algorand.algosdk.crypto.Address;
import com.algorand.algosdk.crypto.Digest;
import com.algorand.algosdk.transaction.SignedTransaction;
import com.algorand.algosdk.transaction.Transaction;
import com.algorand.algosdk.util.Encoder;
public class SaveTransactionOffline {
public AlgodApi algodApiInstance = null;
// utility function to connect to a node
private AlgodApi connectToNetwork(){
// Initialize an algod client
final String ALGOD_API_ADDR = <algod-address>;
final String ALGOD_API_TOKEN = <algod-token>;
AlgodClient client = (AlgodClient) new AlgodClient().setBasePath(ALGOD_API_ADDR);
ApiKeyAuth api_key = (ApiKeyAuth) client.getAuthentication("api_key");
api_key.setApiKey(ALGOD_API_TOKEN);
algodApiInstance = new AlgodApi(client);
return algodApiInstance;
}
// utility function to wait on a transaction to be confirmed
public void waitForConfirmation( String txID ) throws Exception{
if( algodApiInstance == null ) connectToNetwork();
while(true) {
try {
//Check the pending tranactions
com.algorand.algosdk.algod.client.model.Transaction pendingInfo = algodApiInstance.pendingTransactionInformation(txID);
if (pendingInfo.getRound() != null && pendingInfo.getRound().longValue() > 0) {
//Got the completed Transaction
System.out.println("Transaction " + pendingInfo.getTx() + " confirmed in round " + pendingInfo.getRound().longValue());
break;
}
algodApiInstance.waitForBlock(BigInteger.valueOf( algodApiInstance.getStatus().getLastRound().longValue() +1 ) );
} catch (Exception e) {
throw( e );
}
}
}
public void writeUnsignedTransaction(){
// connect to node
if( algodApiInstance == null ) connectToNetwork();
final String DEST_ADDR = <transaction-reciever>;
final String SRC_ADDR = <transaction-sender>;
try {
// Get suggested parameters from the node
TransactionParams params = algodApiInstance.transactionParams();
BigInteger firstRound = params.getLastRound();
String genId = params.getGenesisID();
Digest genesisHash = new Digest(params.getGenesishashb64());
// create transaction
BigInteger amount = BigInteger.valueOf(200000);
BigInteger lastRound = firstRound.add(BigInteger.valueOf(1000));
Transaction tx = new Transaction(new Address(SRC_ADDR),
BigInteger.valueOf(1000), firstRound, lastRound,
null, amount, new Address(DEST_ADDR), genId, genesisHash);
// save as signed even though it has not been
SignedTransaction stx = new SignedTransaction();
stx.tx = tx;
// Save transaction to a file
Files.write(Paths.get("./unsigned.txn"), Encoder.encodeToMsgPack(stx));
System.out.println("Transaction written to a file");
} catch (Exception e) {
System.out.println("Save Exception: " + e);
}
}
public void readUnsignedTransaction(){
try {
// connect to node
if( algodApiInstance == null ) connectToNetwork();
// read transaction from file
SignedTransaction decodedTransaction = Encoder.decodeFromMsgPack(
Files.readAllBytes(Paths.get("./unsigned.txn")), SignedTransaction.class);
Transaction tx = decodedTransaction.tx;
// recover account
String SRC_ACCOUNT = <25-word-passphrase>;
Account src = new Account(SRC_ACCOUNT);
// sign transaction
SignedTransaction signedTx = src.signTransaction(tx);
byte[] encodedTxBytes = Encoder.encodeToMsgPack(signedTx);
// submit the encoded transaction to the network
TransactionID id = algodApiInstance.rawTransaction(encodedTxBytes);
System.out.println("Successfully sent tx with id: " + id);
waitForConfirmation(id.getTxId());
} catch (Exception e) {
System.out.println("Submit Exception: " + e);
}
}
public void writeSignedTransaction(){
// connect to node
if( algodApiInstance == null ) connectToNetwork();
final String DEST_ADDR = <transaction-reciever>;
final String SRC_ADDR = <transaction-sender>;;
try {
// Get suggested parameters from the node
TransactionParams params = algodApiInstance.transactionParams();
BigInteger firstRound = params.getLastRound();
String genId = params.getGenesisID();
Digest genesisHash = new Digest(params.getGenesishashb64());
// create transaction
BigInteger amount = BigInteger.valueOf(200000);
BigInteger lastRound = firstRound.add(BigInteger.valueOf(1000));
Transaction tx = new Transaction(new Address(SRC_ADDR),
BigInteger.valueOf(1000), firstRound, lastRound,
null, amount, new Address(DEST_ADDR), genId, genesisHash);
// recover account
String SRC_ACCOUNT = <25-word-passphrase>;
Account src = new Account(SRC_ACCOUNT);
// sign transaction
SignedTransaction signedTx = src.signTransaction(tx);
// save signed transaction to a file
Files.write(Paths.get("./signed.txn"), Encoder.encodeToMsgPack(signedTx));
} catch (Exception e) {
System.out.println("Save Exception: " + e);
}
}
public void readSignedTransaction(){
try {
// connect to a node
if( algodApiInstance == null ) connectToNetwork();
//Read the transaction from a file
SignedTransaction decodedSignedTransaction = Encoder.decodeFromMsgPack(
Files.readAllBytes(Paths.get("./signed.txn")), SignedTransaction.class);
System.out.println("Signed transaction with txid: " + decodedSignedTransaction.transactionID);
// Msgpack encode the signed transaction
byte[] encodedTxBytes = Encoder.encodeToMsgPack(decodedSignedTransaction);
//submit the encoded transaction to the network
TransactionID id = algodApiInstance.rawTransaction(encodedTxBytes);
System.out.println("Successfully sent tx with id: " + id);
waitForConfirmation(id.getTxId());
} catch (Exception e) {
System.out.println("Submit Exception: " + e);
}
}
public static void main(String args[]) throws Exception {
SaveTransactionOffline mn = new SaveTransactionOffline();
mn.writeUnsignedTransaction();
mn.readUnsignedTransaction();
//mn.writeSignedTransaction();
//mn.readSignedTransaction();
}
}
2 Likes
Every time I try to create a new instance of the Account class like this new Account()
in an android environment, I get the following error
java.security.NoSuchAlgorithmException: Ed25519 KeyPairGenerator not available
I have tried adding BouncyCastle as a provider by doing this
Security.addProvider(new BouncyCastleProvider());
But I still get the same error,
I also get
java.security.NoSuchAlgorithmException: SHA-512/256 MessageDigest not available
when i try to pass in the 25 words mnemonic as a String to the constructor of the Account
class,
please any help or reply is appreciated.
Thanks
@Vytek @JasonW
Thanks, this issue has already been resolved
JasonW
September 29, 2020, 5:01pm
8
What did you have to do to fix the issue?
i am also getting same error
java.security.NoSuchAlgorithmException: Ed25519 KeyPairGenerator not available
Thanks
@JasonW @jesulonimi
will
September 29, 2020, 8:07pm
10
You need to register a crypto provider such as BouncyCastle. Scroll down to section 6.0 for details: https://www.bouncycastle.org/specifications.html
@will i am following your link but still getting the error. Please check the below code. I am consuming this code for android purpose
Security.addProvider(BouncyCastleProvider())
// "BC" is the name of the BouncyCastle provider
val keyGen = KeyGenerator.getInstance("DES", "BC")
keyGen.init(SecureRandom())
key = keyGen.generateKey()
encrypt = Cipher.getInstance("DES/CBC/PKCS5Padding")
encrypt.init(Cipher.ENCRYPT_MODE, key)
val bOut = ByteArrayOutputStream()
val cOut = CipherOutputStream(bOut, encrypt)
val myAccount = Account()
Can this be the issue?
Security.removeProvider("BC");
Security.insertProviderAt(new BouncyCastleProvider(), 0);
What i did above is pretty simple, the problem is that android ships with an outdated version of BouncyCastle, and has been added as a Provider, what we have to do is remove that provider and add our own version of BouncyCastle as a new Provider. Please note that the two lines of code above must be added before any other code that uses public or private Key Cryptographic algorithm.
1 Like
Thank you so much @rfustino with this code I am able to generate my keys. For the reference for other android developers please check the below code.
Security.removeProvider(“BC”)
Security.insertProviderAt(BouncyCastleProvider(), 0)
val myAccount = Account()
Great to hear! @shashank you might want to add these lines too right after the insert. And I give jesulonimi credit as this is how he solved it. Also, some error checking would be good to add.
Preformatted textString providerName = "BC";
if (Security.getProvider(providerName) == null)
{
Log.d("algoDebug",providerName + " provider not installed");
}
else
{
Log.d("algoDebug",providerName + " is installed.");
}
1 Like
Thanks a lot @rfustino , a beginner tutorial explaining more on this can now be found here
1 Like