3

I am able to create a public/private ecdsa key in Rust, how can I store the keys for further use? I can't seem to find a method that will export the key.

use p256::{ecdsa::{SigningKey, Signature, signature::Signer}};
use rand_core::OsRng; // requires 'getrandom' feature

fn main() {
    // Signing
    let signing_key = SigningKey::random(&mut OsRng); // Serialize with `::to_bytes()`
    let message = b"ECDSA proves knowledge of a secret number in the context of a single message";
    let signature = signing_key.sign(message);


    // Verification
    use p256::ecdsa::{VerifyingKey, signature::Verifier};

    let verifying_key = VerifyingKey::from(&signing_key); // Serialize with `::to_encoded_point()`
    assert!(verifying_key.verify(message, &signature).is_ok());
}
Topaco
  • 40,594
  • 4
  • 35
  • 62
Kladskull
  • 10,332
  • 20
  • 69
  • 111

2 Answers2

5

There are many different ways to represent keys. Internally, it's a byte array that can be dumped as such directly. But for compatibility, you probably want a more standardized, human readable format.

The most common one is the PEM format.

To get a PEM, you cannot construct SigningKey and VerifyingKey directly. You need to create a SecretKey and a PublicKey. They can be stored and loaded to/from the PEM format.

You can then convert the SecretKey to a SigningKey and the PublicKey to a VerifyingKey.

Be aware that you need to enable the pem feature for the p256 dependency:

p256 = { version = "0.11", features = ["pem"] }
use p256::{
    ecdsa::{
        signature::{Signer, Verifier},
        SigningKey, VerifyingKey,
    },
    pkcs8::EncodePrivateKey,
    PublicKey, SecretKey,
};
use rand_core::OsRng;

fn main() {
    // Generate secret key
    let secret_key = SecretKey::random(&mut OsRng);

    // Store secret key
    let secret_key_serialized = secret_key
        .to_pkcs8_pem(Default::default())
        .unwrap()
        .to_string();
    println!("Secret Key: \n{}", secret_key_serialized);

    // Load secret key
    let secret_key = secret_key_serialized.parse::<SecretKey>().unwrap();

    // Derive public key
    let public_key = secret_key.public_key();

    // Store public key
    let public_key_serialized = public_key.to_string();
    println!("Public Key: \n{}", public_key_serialized);

    // Load public key
    let public_key = public_key_serialized.parse::<PublicKey>().unwrap();

    // Signing
    let signing_key: SigningKey = secret_key.into();
    let message = b"ECDSA proves knowledge of a secret number in the context of a single message";
    let signature = signing_key.sign(message);

    // Verification
    let verifying_key: VerifyingKey = public_key.into();
    assert!(verifying_key.verify(message, &signature).is_ok());
}
Secret Key: 
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQguKslF8gIC/Fm36Yk
SK2qZ4MXbUg/ZwCJwY9OmzRzkOehRANCAAQrRw99abOvHAPdz79CkuihXfaKZqx+
ZUG6iQRDzZy0c+gw20KeevA4gXKnW0nFK8PDmOgBprCU/uHf4vpMbqgo
-----END PRIVATE KEY-----

Public Key: 
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEK0cPfWmzrxwD3c+/QpLooV32imas
fmVBuokEQ82ctHPoMNtCnnrwOIFyp1tJxSvDw5joAaawlP7h3+L6TG6oKA==
-----END PUBLIC KEY-----
Finomnis
  • 18,094
  • 1
  • 20
  • 27
1

One possible format is the export as raw keys. The private key is 32 bytes for P-256 (aka secp256r1, prime256v1), the public key is 65 bytes (uncompressed) or 33 bytes (compressed), s. e.g. here.

signing_key can be exported as raw private key with to_bytes() and verifying_key can be exported as an uncompressed/compressed public key with to_encoded_point(false/true). false returns an uncompressed key, true a compressed one. Usually the keys are then hex or Base64 encoded, s. here (with sample data):

...
println!("{}", hex::encode(signing_key.to_bytes()));                // 3ee21644150adb50dc4c20e330184fabf12e75ecbf31fe167885587e6ebf2255
println!("{}", hex::encode(verifying_key.to_encoded_point(false))); // 044e6dbeefb3ffbab80281596869352fd4e90353758c3955914be90f0849d16aed5cefb4d5d4e209efaf8eb9349eed862c816cba3f2188e07d101f6ba2e23a69e9
println!("{}", hex::encode(verifying_key.to_encoded_point(true)));  // 034e6dbeefb3ffbab80281596869352fd4e90353758c3955914be90f0849d16aed
...

For completeness: The import of the private key is possible with SigningKey::from_bytes() and that of the uncompressed/compressed public key with VerifyingKey::from_encoded_point().

Topaco
  • 40,594
  • 4
  • 35
  • 62