Je souhaite passer du certificat auto-signé par appareil à la paire de certificats, dont l'un est généré précédemment, placé dans le magasin des autorités de certification racines de confiance, est identique pour tous les périphériques, et fonctionne en tant que CA racine pour le second certificat, qui est généré par périphérique et placé dans le magasin personnel.Exception lors de la tentative de création de certificat non auto-signé
Je ne souhaite pas utiliser makecert, car la création d'un certificat signé affiche une interface utilisateur que je souhaite éviter. En outre, OpenSSL ne peut pas être utilisé en raison de certaines choses liées à la licence (bien que j'ai une solution de travail avec elle). Donc, pour l'instant je travaille avec un petit outil C#, basé sur CertEnroll lib.
Voici comment je crée pfx pour le premier certificat CA racine.
makecert -n "CN=Root CA" -cy authority -r -a sha256 -len 2048 -sv root.pvk root.cer
pvk2pfx -pvk root.pvk -spc root.cer -pfx root.pfx -pi 123 -po 123
Pour créer un certificat à partir du code C#, j'ai des questions How to create self-signed certificate programmatically for WCF service? et référencé C# Generate a non self signed client CX509Certificate Request without a CA using the certenroll.dll.
Jusqu'ici, j'ai le code suivant. Méthode de génération de certificat:
/// <summary>
/// Generates self-signed certificate with specified subject, which will expire after specified timespan.
/// </summary>
public X509Certificate2 CreateCertificate(string subjectName, TimeSpan expirationLength, X509Certificate2 issuer = null)
{
// create DN for subject and issuer
var dn = new CX500DistinguishedName();
dn.Encode("CN=" + subjectName);
var issuerName = new CX500DistinguishedName();
if(issuer != null)
{
issuerName.Encode(issuer.Subject);
}
var privateKey = new CX509PrivateKey
{
ProviderName = "Microsoft Strong Cryptographic Provider",
Length = 2048,
KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE,
KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_DECRYPT_FLAG |
X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_KEY_AGREEMENT_FLAG,
MachineContext = true,
ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG
};
privateKey.Create();
// Use the stronger SHA512 hashing algorithm
var hashobj = new CObjectId();
hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID,
ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY,
AlgorithmFlags.AlgorithmFlagsNone, "SHA512");
var cert = new CX509CertificateRequestCertificate();
cert.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextMachine, privateKey, "");
cert.Subject = dn;
if (issuer != null)
cert.Issuer = issuerName;
else
cert.Issuer = dn;
cert.NotBefore = DateTime.Now.Date;
cert.NotAfter = cert.NotBefore + expirationLength;
cert.HashAlgorithm = hashobj; // Specify the hashing algorithm
if(issuer != null)
{
var signerCertificate = new CSignerCertificate();
signerCertificate.Initialize(true, X509PrivateKeyVerify.VerifyAllowUI, EncodingType.XCN_CRYPT_STRING_HEX, issuer.GetRawCertDataString());
cert.SignerCertificate = signerCertificate;
}
cert.Encode();
// Do the final enrollment process
var enroll = new CX509Enrollment();
enroll.InitializeFromRequest(cert); // load the certificate
enroll.CertificateFriendlyName = subjectName; // Optional: add a friendly name
var csr = enroll.CreateRequest(); // Output the request in base64
// and install it back as the response
enroll.InstallResponse(InstallResponseRestrictionFlags.AllowUntrustedCertificate,
csr, EncodingType.XCN_CRYPT_STRING_BASE64, ""); // no password
// output a base64 encoded PKCS#12 so we can import it back to the .Net security classes
var base64encoded = enroll.CreatePFX("", // no password, this is for internal consumption
PFXExportOptions.PFXExportChainWithRoot);
// instantiate the target class with the PKCS#12 data (and the empty password)
return new X509Certificate2(Convert.FromBase64String(base64encoded), "",
X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
}
application simple pour trouver le certificat existant et en créer un nouveau sur cette base:
static void Main(string[] args)
{
var certificateGenerator = new CertificateGenerator();
X509Certificate2 rootCA;
using (var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
{
store.Open(OpenFlags.ReadWrite);
rootCA = store.Certificates.OfType<X509Certificate2>()
.FirstOrDefault(c => c.Subject.StartsWith("CN=Root CA", StringComparison.Ordinal));
store.Close();
}
if (rootCA == null)
throw new Exception("Can't find root CA certificate");
var testCert = certificateGenerator.CreateCertificate("Test", new TimeSpan(3650, 0, 0, 0), rootCA);
using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
{
store.Open(OpenFlags.ReadWrite);
store.Add(testCert);
store.Close();
}
}
La chose est que cela fonctionne très bien, si je tente de référence du certificat non Autorités de certification racines de confiance, mais en Personal (même si j'ai un mot de passe sur le certificat). Mais si je tente de créer un certificat basé sur le certificat CA de Autorités de certification racine de confiance, je reçois exception sur signerCertificate.Initialize
, en disant
Cannot find object or property. 0x80092004 (-2146885628 CRYPT_E_NOT_FOUND)
Alors, qu'est-ce que je manque?
Quel est le point dans le certificat (même juste sa partie publique) étant dans le magasin CA, si sa version avec clé privée est déjà dans Mon magasin? Je doute que je puisse l'enlever, car le certificat généré référencera le certificat de l'AC à partir de Mon magasin, pas du magasin racine/AC. – lentinant
@lentinant Ce n'est pas tout à fait comme ça que ça fonctionne. Le magasin "Mon" est utilisé lorsqu'il cherche la clé privée pour générer la signature, mais les informations qu'il écrit pour l'émetteur sont juste des identifiants, pas "il était dans le magasin". Le magasin de CA est juste pour accélérer la recherche dans la construction de la chaîne, et Root/TrustedRoot sont pour les décisions de confiance dans la construction de la chaîne. "Un certificat" n'a pas de magasin, ou ne sait pas ce qu'est un magasin ... les magasins ne servent que des fonctions pendant certains chemins d'exécution de code. – bartonjs
l'a fonctionné.Le certificat CA doit également être placé dans le magasin personnel, comme vous l'avez dit, l'application ne doit pas attribuer elle-même la propriété 'Issuer' - elle est définie automatiquement si la propriété' IssuerCertificate' est assignée avant Encode. Si vous le définissez manuellement, vous obtiendrez très probablement «Windows n'a pas assez d'informations pour vérifier ce certificat» affiché sur les informations du certificat. – lentinant