Как подписать файл в Rust?

Рейтинг: 0Ответов: 1Опубликовано: 18.03.2023

Надо подписать файл на Rust. Пытаюсь делать это так:

extern crate openssl;

use std::env;
use std::string::String;
use std::fs::File;
use std::path::Path;                                                                             use openssl::pkey::PKey;
use openssl::sign::Signer;                                                                       use openssl::hash::MessageDigest;
use std::io::Write;
use std::io::Read;

fn sign(file: String, key: String, sigfile: String) {
    if Path::new(&file).exists() {
        if Path::new(&key).exists() {
            let kd = File::open(key).expect("File not found!");
            let mut strdata = String::new();
            kd.read_to_string(&mut strdata).expect("Error reading of file!");
            let data: &[u8] = strdata.as_bytes();
            let pkd = File::open(key).expect("Private key not found!");
            let mut pkdata = String::new();
            pkd.read_to_string(&mut pkdata).expect("Error reading of private key!");
            let pkb: Vec<u8> = pkdata.as_bytes().to_vec();
            let privk = PKey::private_key_from_pem(&pkb);

            let mut signer = Signer::new(MessageDigest::sha512(), &privk).unwrap();
            signer.update(data).unwrap();

            let signature = signer.sign_to_vec().unwrap();
            let sigdump = signature.as_slice();
            let fd = match File::create(sigfile) {
                    Err(why) => panic!("Cant open signature file {}: {}", sigfile, why),
                    Ok(fd) => fd
            };
            fd.write_all(&sigdump);
        } else {
            eprintln!("Keyfile {} is not found!", key);
            panic!();
        }
    } else {
        eprintln!("File {} is not found!", file);
        panic!();
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() == 4 {
        sign(args[1].clone(), args[2].clone(), args[3].clone());
    } else {
        println!("Usage: signfile (file) (private.key) (out.sig)");
    }
}

При сборке выдаёт ошибку:

 ~/signfile $ cargo build
error[E0308]: mismatched types
   --> src/main.rs:26:61
    |
26  |             let mut signer = Signer::new(MessageDigest::sha512(), &privk).unwrap();
    |                              -----------                          ^^^^^^ expected `&PKeyRef<_>`, found `&Result<PKey<...>, ...>`
    |                              |
    |                              arguments to this function are incorrect
    |
    = note: expected reference `&PKeyRef<_>`
               found reference `&Result<PKey<Private>, ErrorStack>`
note: associated function defined here
   --> /root/.cargo/registry/src/github.com-1ecc6299db9ec823/openssl-0.10.46/src/sign.rs:142:12
    |
142 |     pub fn new<T>(type_: MessageDigest, pkey: &'a PKeyRef<T>) -> Result<Signer<'a>, Erro...
    |            ^^^

For more information about this error, try `rustc --explain E0308`.

Во1, я пока ещё слабо знаю Rust, во2, во всех примерах, которые я находил в Интернете, в одном исполняемой файле генерировались ключи и сразу ими подписывались файлы. Но беда в том, что я ключ (приватный) беру из файла, а как мне сконвертировать Result<PKey<...>> в PKeyRef<...>, я понятия не имею...

Ответы

▲ 2Принят

Смотрите - у вас везде написаны expect-ы, которые паникуют, если операция не удалась, а в строке, где приватный ключ извлекается из PEM, нет ни expect, ни unwrap: let privk = PKey::private_key_from_pem(&pkb);

Измените её на что-нибудь вроде

let privk = PKey::private_key_from_pem(&pkb).expect("Failed to parse private key");

и в точке вызова вместо pkey подставьте pkey.as_ref(), так как вам нужен тип PKeyRef

После того, как вы поправите эту ошибки, rustc вас осчастливит ещё кучей уведомлений об использовании moved значений и вызове неконстантных методов для не-мутабельных значений. Поправить их можно как-то так:

extern crate openssl;

use std::env;
use std::string::String;
use std::fs::File;
use std::path::Path;
use openssl::pkey::PKey;
use openssl::sign::Signer;
use openssl::hash::MessageDigest;
use std::io::Write;
use std::io::Read;

fn sign(file: String, key: String, sigfile: String) {
    if Path::new(&file).exists() {
        if Path::new(&key.clone()).exists() {
            let mut kd = File::open(key.clone()).expect("File not found!");
            let mut strdata = String::new();
            kd.read_to_string(&mut strdata).expect("Error reading of file!");
            let data: &[u8] = strdata.as_bytes();
            let mut pkd = File::open(key).expect("Private key not found!");
            let mut pkdata = String::new();
            pkd.read_to_string(&mut pkdata).expect("Error reading of private key!");
            let pkb: Vec<u8> = pkdata.as_bytes().to_vec();
            let privk = PKey::private_key_from_pem(&pkb).expect("Failed to parse PEM");

            let mut signer = Signer::new(MessageDigest::sha512(), &privk.as_ref()).unwrap();
            signer.update(data).unwrap();

            let signature = signer.sign_to_vec().unwrap();
            let sigdump = signature.as_slice();
            
            let mut fd = match File::create(sigfile) {
                    Err(why) => panic!("Cant open signature file: {}", why),
                    Ok(fd) => fd
            };
            fd.write_all(&sigdump).expect("Failed to write signature");
        } else {
            eprintln!("Keyfile {} is not found!", key);
            panic!();
        }
    } else {
        eprintln!("File {} is not found!", file);
        panic!();
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() == 4 {
        sign(args[1].clone(), args[2].clone(), args[3].clone());
    } else {
        println!("Usage: signfile (file) (private.key) (out.sig)");
    }
}

Компилируется, запускается, но openssl не может верифицирвать подпись.