Certificate
Represents an OpenSSH certificate as described in
https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys.
using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
using System;
using System.Collections.Generic;
namespace Renci.SshNet.Security
{
public class Certificate
{
private sealed class CertificateData : SshData
{
public string Name { get; set; }
public byte[] Nonce { get; set; }
public Key Key { get; set; }
public SshKeyData KeyData { get; set; }
public ulong Serial { get; set; }
public uint Type { get; set; }
public string KeyId { get; set; }
public List<string> ValidPrincipals { get; set; }
public ulong ValidAfter { get; set; }
public ulong ValidBefore { get; set; }
public Dictionary<string, string> CriticalOptions { get; set; }
public Dictionary<string, string> Extensions { get; set; }
public byte[] SignatureKey { get; set; }
public long ByteCountBeforeSignature { get; set; }
public byte[] Signature { get; set; }
protected override void LoadData()
{
Name = ReadString(null);
Nonce = ReadBinary();
Key = ReadPublicKey(out SshKeyData keyData);
KeyData = keyData;
Serial = ReadUInt64();
Type = ReadUInt32();
KeyId = ReadString(null);
ValidPrincipals = ReadValidPrincipals(ReadBinary());
ValidAfter = ReadUInt64();
ValidBefore = ReadUInt64();
CriticalOptions = ReadExtensionPair(ReadBinary());
Extensions = ReadExtensionPair(ReadBinary());
ReadBinary();
SignatureKey = ReadBinary();
ByteCountBeforeSignature = base.DataStream.Position;
Signature = ReadBinary();
}
private Key ReadPublicKey(out SshKeyData keyData)
{
switch (Name) {
case "ssh-rsa-cert-v01@openssh.com":
keyData = new SshKeyData("ssh-rsa", <ReadPublicKey>g__LoadPublicKeys|61_0(2));
return new RsaKey(keyData);
case "ecdsa-sha2-nistp256-cert-v01@openssh.com":
case "ecdsa-sha2-nistp384-cert-v01@openssh.com":
case "ecdsa-sha2-nistp521-cert-v01@openssh.com":
keyData = new SshKeyData(Name.Substring(0, 19), <ReadPublicKey>g__LoadPublicKeys|61_0(2));
return new EcdsaKey(keyData);
case "ssh-ed25519-cert-v01@openssh.com":
keyData = new SshKeyData("ssh-ed25519", <ReadPublicKey>g__LoadPublicKeys|61_0(1));
return new ED25519Key(keyData);
default:
throw new NotSupportedException("Certificate type '" + Name + "'.");
}
}
private static Dictionary<string, string> ReadExtensionPair(byte[] data)
{
Dictionary<string, string> dictionary = new Dictionary<string, string>();
using (SshDataStream sshDataStream = new SshDataStream(data)) {
while (!sshDataStream.IsEndOfData) {
string key = sshDataStream.ReadString(null);
string value = sshDataStream.ReadString(null);
dictionary.Add(key, value);
}
return dictionary;
}
}
private static List<string> ReadValidPrincipals(byte[] data)
{
List<string> list = new List<string>();
using (SshDataStream sshDataStream = new SshDataStream(data)) {
while (!sshDataStream.IsEndOfData) {
list.Add(sshDataStream.ReadString(null));
}
return list;
}
}
protected override void SaveData()
{
throw new NotImplementedException();
}
}
public enum CertificateType : uint
{
User = 1,
Host
}
private readonly CertificateData _data;
public string Name => _data.Name;
public byte[] Nonce => _data.Nonce;
public Key Key => _data.Key;
internal SshKeyData KeyData => _data.KeyData;
public ulong Serial => _data.Serial;
public CertificateType Type => (CertificateType)_data.Type;
public string KeyId => _data.KeyId;
public IList<string> ValidPrincipals => _data.ValidPrincipals;
public ulong ValidAfterUnixSeconds => _data.ValidAfter;
public DateTimeOffset ValidAfter => DateTimeOffset.FromUnixTimeSeconds((long)_data.ValidAfter);
public ulong ValidBeforeUnixSeconds => _data.ValidBefore;
public DateTimeOffset ValidBefore {
get {
if (_data.ValidBefore != ulong.MaxValue)
return DateTimeOffset.FromUnixTimeSeconds((long)_data.ValidBefore);
return DateTimeOffset.MaxValue;
}
}
public IDictionary<string, string> CriticalOptions => _data.CriticalOptions;
public IDictionary<string, string> Extensions => _data.Extensions;
public byte[] CertificateAuthorityKey => _data.SignatureKey;
public string CertificateAuthorityKeyFingerPrint => Convert.ToBase64String(CryptoAbstraction.HashSHA256(CertificateAuthorityKey)).TrimEnd('=');
public byte[] Signature => _data.Signature;
internal byte[] Bytes { get; }
internal byte[] BytesForSignature => Bytes.Take((int)_data.ByteCountBeforeSignature);
public Certificate(byte[] data)
{
Bytes = data;
_data = new CertificateData();
_data.Load(Bytes);
}
}
}