Crypto.Load reads a key or certificate file without being told its format: PEM or DER, PKCS#8 plain or encrypted, traditional RSA PRIVATE KEY and EC PRIVATE KEY PEM (including the old DEK-Info encryption), PKCS#12 and OpenSSH. Each private key comes back ready to use. The program below loads three keys made by OpenSSL, one of them Ed25519, which .NET can’t load at all.
dotnet new console -o LoadKeys && cd LoadKeys dotnet add package RecursiveAI.OneCrypto --version 0.1.0-preview.1 openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:3072 \ -aes-256-cbc -pass pass:s3cret -out rsa-pkcs8.pem openssl ecparam -name prime256v1 -genkey -noout \ | openssl ec -aes128 -passout pass:s3cret -out ec-legacy.pem openssl genpkey -algorithm ed25519 -out ed25519.pem
OneCrypto is a preview, so dotnet add package needs --version or --prerelease. Put this in Program.cs and run dotnet run:
using System.Text; using RecursiveAI.OneCrypto; using RecursiveAI.OneCrypto.Keys; foreach (string path in new[] { "rsa-pkcs8.pem", "ec-legacy.pem", "ed25519.pem" }) { // The callback is asked for a password only if the file is encrypted using LoadedItems file = Crypto.Load(path, _ => "s3cret"); LoadedPrivateKey key = file.PrivateKeys[0]; Console.WriteLine($"{path,-14} {file.Format} {key.Algorithm,-13} " + $"{key.PublicKey?.KeySizeBits,4} bits can sign: {key.Signing is not null}"); } // Sign with the Ed25519 key and save the signature for OpenSSL using LoadedItems ed = Crypto.Load("ed25519.pem"); ISigningKey signer = ed.PrivateKeys[0].Signing!; byte[] data = Encoding.UTF8.GetBytes("release-1.4.2.tar.gz sha256 9f2c..."); File.WriteAllBytes("data.txt", data); File.WriteAllBytes("data.sig", signer.Sign(data, signer.DefaultSpec)); Console.WriteLine($"Signed with {signer.DefaultSpec}");
rsa-pkcs8.pem PEM Rsa 3072 bits can sign: True ec-legacy.pem PEM EllipticCurve 256 bits can sign: True ed25519.pem PEM Ed25519 256 bits can sign: True Signed with Ed25519
Format names the container that was found. Every private key carries Signing, Decryption or Agreement as its algorithm allows (RSA signs and decrypts, EC signs with ECDSA and agrees with ECDH, X25519 only agrees), plus its PublicKey. Info holds the PKCS#8 form when you need it: Info.ToRsa() and Info.ToECDsa() give .NET’s own key objects, Info.ToEd25519() OneCrypto’s.
Check the signature with OpenSSL
openssl pkeyutl -verify -inkey ed25519.pem -rawin -in data.txt -sigfile data.sigSignature Verified SuccessfullyWrong passwords and bad files
A file that is not a key raises CryptoLoadException. Its Error says why (PasswordRequired, WrongPasswordOrCorrupt, Unrecognized, Malformed…) and Considered lists the formats looked at; Crypto.TryLoad returns false with the same exception instead. The password callback gets a short description of the encrypted item, and a password that opened one item is tried on the next first, so one passphrase for a whole bundle is asked once. OpenPGP armor (-----BEGIN PGP …) is not loaded: OpenPGP comes in a later package.
Limits
- Not audited. This library has not been independently audited. It is a preview: 0.1.0-preview.1, and the API can change.
- What isn’t read. OpenPGP, age and minisign files;
[email protected]key files; RC4-encrypted PKCS#12. DSA keys load, but only asInfo, with nothing to sign. - Writing.
PrivateKeyInfo.Encrypt(password)writes encrypted PKCS#8 (PBES2); the legacyDEK-InfoPEM encryption is read only.
The format decision table and every error: Crypto.Load. PKCS#8 and PKCS#12 details: PKCS.