How do you set permissions on a certificate in the Windows Certificate Store through PowerShell 7? - powershell

Every resource I've found so far, such as this question, boils down to:
Get the certificate as an X509Certificate2
Cast the PrivateKey property to RSACryptoServiceProvider
Do stuff involving CspKeyContainerInfo property on the private key
This works in PowerShell 5, but in PowerShell 7, the PrivateKey property is an RSACng, not an RSACryptoServiceProvider, and so step 2 doesn't work.

FYI, PrivateKey was deprecated. You are now required to use the GetRSAPrivateKey extension method.
I cannot find any documentation, but it appears that .Net Framework used to return an RSA that also implemented RSACryptoServiceProvider which is where we could get the CspKeyContainerInfo, but now we are required to grab the key RSA derived type RSACng which is returned by the extension method. From here you can grab properties like the $cert.Key.UniqueName

Related

ACM PCA Issue certificate for client authentication and signing data

I'm trying to implement MTLS client authentication using AWS ACM Private CA to issue X.509 client certificates.
The certificate and the correlating private key is supposed to be stored in a password protected PKCS#12 file.
The private key will also be used by the client to sign data.
If I request a new certificate using aws-acm-sdk:
RequestCertificateResult response = acm.requestCertificate(new RequestCertificateRequest()
.withCertificateAuthorityArn(CA_ARN)
.withIdempotencyToken("1234")
.withDomainName("localhost.com"));
return response.getCertificateArn();
And then export the it using the arn, I get a certificate, certificateChain and a privateKey as strings.
ExportCertificateResult response = acm.exportCertificate(new ExportCertificateRequest()
.withCertificateArn(certificateArn)
.withPassphrase(ByteBuffer.wrap(password.getBytes())));
String certificate = response.getCertificate();
String certificateChain = response.getCertificateChain();
String privateKey = response.getPrivateKey();
But I'm not able to add any type of identifier that let's me tie the certificate to a user during authentication (I'm using Java and Spring security x509 authentication, which extracts e.g. the subject CN (Common Name) from the certificate which then can be used to identify a user).
If I want to add custom attributes to the certificate, I need to issue a certificate through the aws-acm-pca-sdk:
IssueCertificateRequest request = new IssueCertificateRequest()
.withCertificateAuthorityArn(CA_ARN)
.withCsr(stringToByteBuffer(getCSR()))
.withTemplateArn("arn:aws:acm-pca:::template/EndEntityClientAuthCertificate_APIPassthrough/V1")
.withSigningAlgorithm(SigningAlgorithm.SHA256WITHRSA)
.withValidity(new Validity()
.withValue(365L)
.withType(ValidityPeriodType.DAYS))
.withIdempotencyToken(userId)
.withApiPassthrough(new ApiPassthrough()
.withSubject(new ASN1Subject()
.withCustomAttributes(List.of(
new CustomAttribute()
.withObjectIdentifier("1.3.6.1.4.1") // CustomOID
.withValue("userId")))));
return acmPca.issueCertificate(request).getCertificateArn();
But if I use the sdk to get the certificate, it doesn't contain any private key.
GetCertificateResult response = acmPca.getCertificate(new GetCertificateRequest()
.withCertificateAuthorityArn(CA_ARN)
.withCertificateArn(certificateArn));
String certificate = response.getCertificate();
String certificateChain = response.getCertificateChain();
So, when I read documentation I found that I need to import it to ACM in order to export it and get the private key.
But in order to import it to ACM I also need to provide the private key..
But as I understand it, the private key should be used when issuing the certificate in the first place?
Should I create a new public/private key pair using KMS or what am I supposed to do?
Im confused.. Please help!
I had misinterpreted the responsibility of the PCA.
The PCA is only responsible for issuing certificates, not keys or even CSR (Certificate Signing Requests).
I assumed that the CSR were created by PCA, so the getCSR() method actually fetched the CA's CSR from PCA, while the actual CSR should be generated internally using a private key which has been generated either programmatically or using KMS.

Unable to access ssl certificate from octopus library as X509Certificate2 object using powershell

I am trying to create a jwt token using a pfx which,I have stored in Octopus library. For this I have to create an object of X509Certificate2, which takes certificate path and password as input. Can someone please suggest a way to do this using powershell?
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certpath,'password')
I have been through some documents as per how to access certificate variables in octopus but how can I use them to create an object of X509Certificate2.
https://octopus.com/docs/deployment-process/variables/certificate-variables
After going through Microsoft and Octopus documentation I have managed to get it to work. Octopus store the certificate as a base64 encoded string in a variable named as Cert.Pfx and constructor of X509Certificate2 takes a byte array as a first parameter. So as a first step I just needed to convert the base64 encoded string to byte array.
$certbytearray=[System.Convert]::FromBase64String($OctopusParameters["Cert.Pfx"])
$CertPassKey="password"
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certbytearray,$CertPassKey)
#Biplab Thanks for posting your solution; it saved me a lot of headache! I had a slightly different situation without a password and I found that the X509Certificate2 constructor interpreted the byte array as a file name when I tried to call without the password even though the documentation indicates it should accept just a byte array.
I got it to work without the password by doing an import instead.
$certbytearray=[System.Convert]::FromBase64String($OctopusParameters["mycert.Pfx"])
$mycert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$mycert.import($certbytearray)
write-host $mycert.ThumbPrint
if ($mycert.HasPrivateKey)
{ write-host "has private key"}

Convert CodeFluent.Runtime.Utilities.Authenticode.FindSuitableCertificate to C#

I'm trying to convert Dim certificate As X509Certificate2 = CodeFluent.Runtime.Utilities.Authenticode.FindSuitableCertificate() from VB.Net to C# and I know that it's X509Certificate2 AuthenticodeCert = CodeFluent.Runtime.Utilities.Authenticode.FindSuitableCertificate();
The problem is that in C# it produces a ArgumentNullException, but works in VB.Net.
X509Certificate2 AuthenticodeCert = Authenticode.FindSuitableCertificate(); // Problem
// Check if there is a Certificate in the Certificate Store that can signs code.
if (Authenticode.CanSignCode(AuthenticodeCert) == true) // Problem ArgumentNullException
{// Bla, Bla}
Any help would be Appreciated. Thanks.
I figured it out.
X509Certificate2 AuthenticodeCert = new X509Certificate2("PFX_Private_Key", "Password");
The only problem is if it returns null then I hit an error. The only remedying it seems for that was to Try Catch. Anyone know a better way to code for the null value?
Thanks.
The CodeFluent.Runtime library is a .Net framework library independant of the calling language. It is itself implemented in C# .
So I suspect your issue is not related to the language you use, but the local machine certificate stores or the executing user that are different.
The default FindSuitableCertificate looks into the My certificate store for Current User.
There is another more advanced signature you can use if the signing certificate installed on the machine is installed somewhere else instead :
/// <summary>
/// Finds a suitable certificate for authenticode signing.
/// </summary>
/// <param name="storeName">The store name.</param>
/// <param name="storeLocation">The store location.</param>
/// <param name="thumbPrint">The optional thumbprint. May be null.</param>
/// <returns>A certificate instance or null if not found.</returns>
public static X509Certificate2 FindSuitableCertificate(StoreName storeName, StoreLocation storeLocation, string thumbPrint)

CryptoAPI - how to extract RSA public key from private

Using windows CryptoAPI, is it possible to get public RSA key from a private key which was imported (not generated)?
If I use CryptGenKey, I can call CryptExportPublicKeyInfo and CryptImportPublicKeyInfo to obtain the public key handle. However, when I try to do the same thing with private key decoded from PEM and imported using:
CryptImportKey(hCSP, pKeyBuf, cbKeyBuf, 0, CRYPT_EXPORTABLE, &hPrivKey)
import of the private key succeeds and I have a valid handle but the subsequent call to CryptExportPublicKeyInfo fails with "Key does not exist" error. It looks like there's another call missing between CryptImportKey and CryptExportPublicKeyInfo, but I can not find that API call.
The problem with exporting/importing the public key was because private key was generated using AT_SIGNATURE, instead of AT_EXCHANGE. See the explanation and the example code

Determine certificate key type (RSA vs EC) in .NET or BouncyCastle?

Currently we have a routine that Signs a byte[] given a certificate (and it's private key). However, the type of certificate/keys is hardcoded as "Certificate with RSA keys". That code is :
public byte[] Sign(byte[] bytesToSign, bool fOAEP, X509Certificate2 certificate)
{
using (RSACryptoServiceProvider provider = new RSACryptoServiceProvider())
{
// HACK: Round-trip the key to XML and back, to get provider type working
// as 'Microsoft Enhanced RSA and AES Cryptographic Provider' (for
// SHA256/SHA512 signing hash) instead of 'Microsoft Enhanced
// Cryptographic Provider v1.0' (that limits us to SHA1)
string publicKeyXml = certificate.PrivateKey.ToXmlString(true);
provider.FromXmlString(publicKeyXml);
// We use the private key to sign.
return provider.SignData(bytesToSign, CryptoConfig.MapNameToOID("SHA512"));
}
}
We would like to make it more flexible where if the certificate uses RSA keys, we process it one way but if it uses EC keys, then we process it differently. Basically the Crypto Service Provider type would be of a different type.
So the core question is:
Given a certificate with public+private keys (for signing) OR a certificate with just public keys (for verifying), how do you determine the types of keys used by the certificate?
I'm open to standard .NET libs or even BouncyCastle.Org libs.
You can check key type (algorithm) via certificate.PublicKey.Oid.
Here you can see supported by Microsoft OIDs: http://msdn.microsoft.com/en-us/library/ff635835.aspx
Other OIDs can be checked at oid-info.com