Storing certificates in pkcs11 keystore - rsa

I am generating an RSA keypair in pkcs11 keystore, it was storing into smartcard and i am generating pkcs10 request. when i download the equivalent certificate how can i store it into smartcard(without privatekey since the key is already stored into smartcard) since i dont have access to the private key in the pkcs11 keystore.
String wdtokenpath = "path to dll file";
String pkcs11ConfigSettings = "name = SmartCard\n" + "library =" + wdtokenpath;
byte[] pkcs11ConfigBytes = pkcs11ConfigSettings.getBytes();
ByteArrayInputStream confStream = new ByteArrayInputStream(pkcs11ConfigBytes);
Provider pkcs11Provider = null;
Class sunPkcs11Class = Class.forName("sun.security.pkcs11.SunPKCS11");
Constructor pkcs11Constr = sunPkcs11Class.getConstructor(
java.io.InputStream.class);
pkcs11Provider = (Provider) pkcs11Constr.newInstance(confStream);
CallbackHandler call = new TextCallbackHandler();
Subject token = new Subject();
AuthProvider aprov = (AuthProvider) pkcs11Provider;
aprov.login(token, call);
System.out.println("Login successfully");
KeyPairGenerator keyGen1 = KeyPairGenerator.getInstance("RSA", aprov);
keyGen1.initialize(2048);
KeyPair pair1 = keyGen1.generateKeyPair();
PublicKey publicKey1 = pair1.getPublic();
String sigAlg = "SHA1withRSA";
PKCS10 pkcs10 = new PKCS10(publicKey1);
Signature signature = Signature.getInstance("SHA1withRSA", pkcs11Provider);
signature.initSign(pair1.getPrivate());

It depends on what kind of smart card you have, or what kind of PKCS#11 device you have. The implementation may differ.
When you are using SunPKCS11, you can do it like this:
public boolean uploadCertificate(X509Certificate cert, String label, String id) {
CK_ATTRIBUTE[] certificate = new CK_ATTRIBUTE[9];
certificate[0] = new CK_ATTRIBUTE(PKCS11Constants.CKA_CLASS, PKCS11Constants.CKO_CERTIFICATE);
certificate[1] = new CK_ATTRIBUTE(PKCS11Constants.CKA_TOKEN, true);
certificate[2] = new CK_ATTRIBUTE(PKCS11Constants.CKA_PRIVATE, false);
certificate[3] = new CK_ATTRIBUTE(PKCS11Constants.CKA_LABEL, label.toCharArray());
certificate[4] = new CK_ATTRIBUTE(PKCS11Constants.CKA_SUBJECT, cert.getSubjectX500Principal().getEncoded());
certificate[5] = new CK_ATTRIBUTE(PKCS11Constants.CKA_ID, HexUtils.hexStringToByteArray(id));
certificate[6] = new CK_ATTRIBUTE(PKCS11Constants.CKA_ISSUER, cert.getIssuerX500Principal().getEncoded());
certificate[7] = new CK_ATTRIBUTE(PKCS11Constants.CKA_SERIAL_NUMBER, cert.getSerialNumber().toByteArray());
try {
certificate[8] = new CK_ATTRIBUTE(PKCS11Constants.CKA_VALUE, cert.getEncoded());
p11.C_CreateObject(hSession, certificate);
} catch (Exception e) {
logger.log(Level.SEVERE, "Upload Certificate Exception", e);
return false;
}
return true;
}
Or with IAIK PKCS#11 Wrapper:
// create certificate object template
X509PublicKeyCertificate pkcs11X509PublicKeyCertificate = new X509PublicKeyCertificate();
pkcs11X509PublicKeyCertificate.getToken().setBooleanValue(Boolean.TRUE);
pkcs11X509PublicKeyCertificate.getPrivate().setBooleanValue(Boolean.FALSE);
pkcs11X509PublicKeyCertificate.getLabel().setCharArrayValue("test".toCharArray());
pkcs11X509PublicKeyCertificate.getSubject().setByteArrayValue(cert.getSubjectX500Principal().getEncoded());
pkcs11X509PublicKeyCertificate.getId().setByteArrayValue(objectId);
pkcs11X509PublicKeyCertificate.getIssuer().setByteArrayValue(cert.getIssuerX500Principal().getEncoded());
// serial number should be an DER encoded ASN.1 integer
/*
INTEGER asn1Integer = new INTEGER(userCertificate.getSerialNumber());
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DerCoder.encodeTo(asn1Integer, buffer);
pkcs11X509PublicKeyCertificate.getSerialNumber().setByteArrayValue(buffer.toByteArray());
*/
// Netscape deviates from the standard here, for use with Netscape rather use
pkcs11X509PublicKeyCertificate.getSerialNumber().setByteArrayValue(cert.getSerialNumber().toByteArray());
pkcs11X509PublicKeyCertificate.getValue().setByteArrayValue(cert.getEncoded());
session.createObject(pkcs11X509PublicKeyCertificate);
The ID of the certificate object should be the same as the ID of generated keys.

Related

Integrate Signed Hash into original PDF

I am integrating a signed hash in an original PDF, and I still have an error on the validity of the signature. it's say that a pdf has been changed after signing.
below the steps: I calculate the hash then I send it for signature and finally I get the hash sign and I proceed to the integration in the original pdf
package com.example.hashdocument;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.security.*;
import com.lexpersona.commons.utils.ProcessLauncher;
import java.io.*;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.util.*;
public class Test2 {
private static final String SRC = "B:/Hash-et-Reconstitution/tmp/Doc_test.pdf";
private static final String DST = "B:/Hash-et-Reconstitution/tmp/Doc_test_DST.pdf";
private static final String HASH = "B:/Hash-et-Reconstitution/tmp/Doc_test_hashed.hash";
private static final String PATH_BAT = "C:/Repo_LP7/lpcommand.bat";
private static final String PIN = "123456";
private static final String CERTIFICATE = "C:/lp7command/tools/certificate.p12";
private static final String SIGNED_HASH = "B:/Hash-et-Reconstitution/tmp/doc_signed.hash";
private static byte[] readFileToByteArray(File file){
FileInputStream fis = null;
byte[] bArray = new byte[(int) file.length()];
try{
fis = new FileInputStream(file);
fis.read(bArray);
fis.close();
}catch(IOException ioExp){
ioExp.printStackTrace();
}
return bArray;
}
public static File bytesToFile(byte[] fileByte,String pathFile) {
File file = new File(pathFile);
try {
OutputStream os = new FileOutputStream(file);
os.write(fileByte);
os.close();
} catch (Exception e) {
e.printStackTrace();
}
return file;
}
public static byte[] signDocument() throws IOException {
ProcessLauncher p = new ProcessLauncher(System.out, System.err);
int exec;
exec = p.exec("cmd.exe /c "+PATH_BAT+" <nul "+ SIGNED_HASH +" "+ PIN+" "
+ HASH+" "+CERTIFICATE, null, null);
byte[] signedHash = readFileToByteArray(new File(SIGNED_HASH));
return signedHash;
}
public static void main(String[] args) throws IOException, GeneralSecurityException, DocumentException {
PdfSignatureAppearance appearance = null;
ByteArrayOutputStream os = null;
String hash_document = "";
InputStream data = null;
int contentEstimated = 8192;
PdfReader reader = new PdfReader(SRC);
reader.unethicalreading = true;
reader.setAppendable(true);
int pdfPagenumber = 1;
pdfPagenumber = reader.getNumberOfPages(); // Sign on last page
os = new ByteArrayOutputStream ();
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0', null, true);
Calendar cal = Calendar.getInstance();
appearance = stamper.getSignatureAppearance();
appearance.setSignDate(cal);
//appearance.setAcro6Layers(false);
appearance.setReason("Signature de contrat");
appearance.setLocation("MAROC");
appearance.setImage(null);
appearance.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED);
Rectangle rect = new Rectangle(300, 300, 20, 20);
appearance.setVisibleSignature(rect, pdfPagenumber, null);
HashMap<PdfName, Integer> exc = new HashMap<PdfName, Integer>();
exc.put(PdfName.CONTENTS, new Integer(contentEstimated * 2 + 2));
PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
dic.setReason(appearance.getReason());
dic.setLocation(appearance.getLocation());
dic.setContact(appearance.getContact());
dic.setDate(new PdfDate(appearance.getSignDate()));
appearance.setCryptoDictionary(dic);
appearance.preClose(exc);
data = appearance.getRangeStream();
MessageDigest messageDigest;
String provider = null;
String hashAlgorithm = DigestAlgorithms.SHA256;
if (provider == null){
messageDigest = MessageDigest.getInstance(hashAlgorithm);
}else {
messageDigest = MessageDigest.getInstance(hashAlgorithm,provider);
}
int read = 0;
byte[] buff = new byte[contentEstimated];
while ((read = data.read(buff, 0, contentEstimated)) > 0)
{
messageDigest.update(buff,0,read);
}
byte[] hashDigest = messageDigest.digest();
byte[] documentHash = org.bouncycastle.util.encoders.Hex.encode(hashDigest);
//eSign Start
hash_document = new String(documentHash, "UTF-8");
System.out.println("Document Hash :"+hash_document);
PrintStream out = new PrintStream(new FileOutputStream(HASH));
out.print(hash_document);
byte[] hashdocumentByte = signDocument();
//////////////////// ADD SIGNED BYTES/HASH TO PDF DOCUMENT.
int contentEstimated2 = 8192;
byte[] paddedSig = new byte[contentEstimated2];
byte[] signedDocByte = hashdocumentByte;
System.arraycopy(signedDocByte, 0, paddedSig, 0, signedDocByte.length);
PdfDictionary dic2 = new PdfDictionary();
dic2.put(PdfName.CONTENTS, new PdfString(paddedSig).setHexWriting(true));
appearance.close(dic2);
try(OutputStream outputStream = new FileOutputStream(DST)) {
os.writeTo(outputStream);
}
os.close();
}
}
what do you think abous this code : First i calculate the hash and send to server A for signature
PdfReader reader = new PdfReader(SRC);
FileOutputStream os = new FileOutputStream(TEMP);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig");
//appearance.setCertificate(chain[0]);
ExternalSignatureContainer external = new
ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
MakeSignature.signExternalContainer(appearance, external, 8192);
InputStream inp = appearance.getRangeStream();
BouncyCastleDigest digest = new BouncyCastleDigest();
byte[] hash = DigestAlgorithms.digest(inp, digest.getMessageDigest("SHA256"));
System.out.println("hash to sign : "+ hash);
bytesToFile(hash, HASH);
byte[] hashdocumentByte = TEST.signed_hash(hash);
PdfReader reader2 = new PdfReader(TEMP);
FileOutputStream os2 = new FileOutputStream(DEST);
ExternalSignatureContainer external2 = new
MyExternalSignatureContainer(hashdocumentByte,null);
MakeSignature.signDeferred(reader2, "sig", os2, external2);
And in the server B where i sign the hash :
BouncyCastleProvider providerBC = new BouncyCastleProvider();
Security.addProvider(providerBC);
// we load our private key from the key store
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(new FileInputStream(CERTIFICATE), PIN);
String alias = (String)ks.aliases().nextElement();
Certificate[] chain = ks.getCertificateChain(alias);
PrivateKey pk = (PrivateKey) ks.getKey(alias, PIN);
PrivateKeySignature signature = new PrivateKeySignature(pk, "SHA256", null);
BouncyCastleDigest digest = new BouncyCastleDigest();
Calendar cal = Calendar.getInstance();
String hashAlgorithm = signature.getHashAlgorithm();
System.out.println(hashAlgorithm);
PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", null, digest, false);
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
byte[] extSignature = signature.sign(sh);
System.out.println(signature.getEncryptionAlgorithm());
sgn.setExternalDigest(extSignature, null, signature.getEncryptionAlgorithm());
return sgn.getEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS);
Your signDocument method apparently does not accept a pre-calculated hash value but seems to calculate the hash of the data you give it, in your case the (lower case) hex presentation of the hash value you already calculated.
In your first example document you have these values (all hashes are SHA256 hashes):
Hash of the byte ranges to sign:
91A9F5EBC4F2ECEC819898824E00ECD9194C3E85E4410A3EFCF5193ED7739119
Hash of "91a9f5ebc4f2ecec819898824e00ecd9194c3e85e4410a3efcf5193ed7739119".getBytes():
2F37FE82F4F71770C2B33FB8787DE29627D7319EE77C6B5C48152F6E420A3242
Hash value signed by the embedded signature container:
2F37FE82F4F71770C2B33FB8787DE29627D7319EE77C6B5C48152F6E420A3242
And in your first example document you have these values (all hashes also are SHA256 hashes):
Hash of the byte ranges to sign:
79793C58489EB94A17C365445622B7F7945972A5A0BC4C93B6444BEDFFA5A5BB
Hash of "79793c58489eb94a17c365445622b7f7945972a5a0bc4c93b6444bedffa5a5bb".getBytes():
A8BCBC6F9619ECB950864BFDF41D1B5B7CD33D035AF95570C426CF4B0405949B
Hash value signed by the embedded signature container:
A8BCBC6F9619ECB950864BFDF41D1B5B7CD33D035AF95570C426CF4B0405949B
Thus, you have to correct your signDocument method to interpret the data correctly, or you have to give it a byte array containing the whole range stream to digest.

AWS cloudhsm with PKCS#11 not able to export RSA public key

I am generating a RSA key pair with AWS cloud HSM with PKCS11Interop c# library on top of AWS vendor PKCS library. Wanted to export a public key from HSM with PKCS 11 getAttributeValue methods.
The response states that attributes cannot be read, I have marked all the attributes values correctly to be able to export a key, can somebody point out what I am doing wrong ?
My sample code
private static void GenerateRSAKeyPair(ISession session, out IObjectHandle publicKeyHandle, out IObjectHandle privateKeyHandle, string keyAlias = null)
{
byte[] ckaId = null;
if (string.IsNullOrEmpty(keyAlias))
ckaId = session.GenerateRandom(20);
else
ckaId = Encoding.UTF8.GetBytes(keyAlias);
// Prepare attribute template of new public key
List<IObjectAttribute> publicKeyAttributes = new List<IObjectAttribute>();
publicKeyAttributes.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_TOKEN, true));
publicKeyAttributes.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_CLASS, CKO.CKO_PUBLIC_KEY));
//publicKeyAttributes.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_PRIVATE, false)); // Throws InvalidAttribute Value
publicKeyAttributes.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_ID, ckaId));
publicKeyAttributes.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_WRAP, true));
//publicKeyAttributes.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_SENSITIVE, true));
publicKeyAttributes.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_MODULUS_BITS, 2048));
publicKeyAttributes.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_PUBLIC_EXPONENT, new byte[] { 0x01, 0x00, 0x01 }));
// Prepare attribute template of new private key
List<IObjectAttribute> privateKeyAttributes = new List<IObjectAttribute>();
privateKeyAttributes.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_TOKEN, true));
//privateKeyAttributes.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_PRIVATE, true));
//publicKeyAttributes.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_SENSITIVE, true));
privateKeyAttributes.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_ID, ckaId));
privateKeyAttributes.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_UNWRAP, true));
// Specify key generation mechanism
IMechanism mechanism = session.Factories.MechanismFactory.Create(CKM.CKM_RSA_X9_31_KEY_PAIR_GEN);
// Generate key pair
session.GenerateKeyPair(mechanism, publicKeyAttributes, privateKeyAttributes, out publicKeyHandle, out privateKeyHandle);
}
private static byte[] GetKeyAttributeValue(ISession session, IObjectHandle keyHandle)
{
var readAttrs = session.GetAttributeValue(keyHandle, new List<CKA>() { CKA.CKA_VALUE });
if (readAttrs[0].CannotBeRead)
throw new Exception("Key cannot be exported");
else
return readAttrs[0].GetValueAsByteArray();
}
RSA public key objects do not have CKA_VALUE attribute. Instead, there are two attributes called CKA_MODULUS and CKA_PUBLIC_EXPONENT that make up the key value.
As suggested by #Homaei
I have created below code to export a public key from c# code.
var modulus = GetKeyAttributeValue(session, publicKey, CKA.CKA_MODULUS);
var exponent = GetKeyAttributeValue(session, publicKey, CKA.CKA_PUBLIC_EXPONENT);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(dwKeySize: 2048);
RSAParameters rsaParam = rsa.ExportParameters(false);
rsaParam.Modulus = modulus;
rsaParam.Exponent = exponent;
rsa.ImportParameters(rsaParam);
var writer = System.IO.File.CreateText("exportedFromCode.txt");
//https://stackoverflow.com/questions/28406888/c-sharp-rsa-public-key-output-not-correct/28407693#28407693
ExportPublicKey(rsa, writer);

keyStore.getKey() returns NULL

I want to sign a PDF with a self generated certificate. In this process I need a keystore and private key. The signing will be made with PDFBox by using the class CreateSignature()
To generate a keyStore with a self generated certificate I am using this:
public KeyStore generateSampleKeyStoreWith509Certificate() throws KeyStoreException, NoSuchAlgorithmException,
CertificateException, IOException, UnrecoverableEntryException {
X509Certificate cert;
PrivateKey caKey;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
keyPairGenerator.initialize(1024, new SecureRandom());
KeyPair keyPair = keyPairGenerator.generateKeyPair();
caKey = keyPair.getPrivate();
Date notBefore = new Date();
Date notAfter = new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 365);
SubjectPublicKeyInfo spkInfo = SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());
X509v3CertificateBuilder newGen = new X509v3CertificateBuilder(new X500Name(issuer), serial, notBefore,
notAfter, new X500Name(subject), spkInfo);
ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider("BC")
.build(caKey);
X509CertificateHolder certHolder = newGen.build(sigGen);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream is1 = new ByteArrayInputStream(certHolder.getEncoded());
cert = (X509Certificate) cf.generateCertificate(is1);
is1.close();
} catch (OperatorCreationException | CertificateException | IOException | NoSuchProviderException
| NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, password);
keyStore.setCertificateEntry("SelfSigned", cert);
return keyStore;
}
The certificate is entered correctly, but shouldn't there be a key, too? Or am I wrong that the keystore should be holding a key?
I'm just figuring this sibject, so I'm thankful of every bit of help.
X509Certificate[] certChain = new X509Certificate[1];
certChain[0] = cert;
keyStore.setKeyEntry("SelfSigned",caKey, password, certChain);
Adding the above code, at the bottom, enters the privateKey previously created to the keystore. It seems most examples on the internet assume loading a keystore with privatekey already entered before.

AES Encryption and decryption using jks file

I have one small doubt as i am new to AES.
I encrypted a string using one certificate with some password lets say , 'xxx'.
Now i duplicated the certificate by changing the password of it.
When i try to decrypt the encrypted string with the duplicated cert, it says Bad padding exception.Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
However, when i use the original cert, it decrypts properly.
Could anyone please guide me on it?
public SecretKey retrieveKey(String password, byte[] certFile) throws Exception {
try {
String alias = null;
certPass = password;
char[] pass = certPass.toCharArray();
KeyStore keyStore = KeyStore.getInstance("jceks");
InputStream inputStream = new ByteArrayInputStream(certFile);
keyStore.load(inputStream, pass);
Enumeration enumeration = keyStore.aliases();
while (enumeration.hasMoreElements()) {
alias = (String) enumeration.nextElement();
}
Certificate cert = keyStore.getCertificate(alias);
Key key = cert.getPublicKey();
aesSecretKey = new SecretKeySpec(key.getEncoded(), algorithm);
byte[] encoded = aesSecretKey.getEncoded();
byte[] encryptionKey = Arrays.copyOfRange(encoded, encoded.length - 16, encoded.length);
aesSecretKey = new SecretKeySpec(encryptionKey, algorithm);
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw e;
}
return aesSecretKey;
}
You should use RSA to wrap / unwrap the AES key. The public key is not identical to the AES key, so the following code is certainly incorrect:
Key key = cert.getPublicKey();
aesSecretKey = new SecretKeySpec(key.getEncoded(), algorithm);

Can BouncyCastle convert a P12 to PEM

A CA gives me a P12 which I want to convert to PEM inside a jvm.
Yes using openssl command works:
openssl pkcs12 -in jack.p12 -out jack.pem -nodes -clcerts
But then it gets ugly getting key and keystore passwords into the openssl program from java not to mention unsecure.
so BouncyCastle seems to be best crypto API for java...
How could it be done (convert P12 to PEM) using bouncy...
Here is an example:
private File createPem(final Certificate certP12, final String name) {
File file = new File(getFileName(name, "pem"));
FileWriter fileWriter;
try {
fileWriter = new FileWriter(file);
PEMWriter pemWriter = new PEMWriter(fileWriter);
pemWriter.writeObject(certP12);
pemWriter.flush();
pemWriter.close();
fileWriter.close();
} catch (IOException e) {
log.error("", e);
}
return file;
}
Load the certificate as Org.BouncyCastle.X509.X509Certificate
Convert to pem.
public static Org.BouncyCastle.X509.X509Certificate ImportCertFromPfx(string path, string password)
{
Pkcs12Store store = new Pkcs12StoreBuilder().Build();
store.Load(File.OpenRead(path), password.ToCharArray());
string alias = null;
foreach (string str in store.Aliases)
{
if (store.IsKeyEntry(str))
alias = str;
}
if (alias == null)
{
Console.WriteLine("alias is null");
}
else
Console.WriteLine(alias);
X509CertificateEntry certEntry = store.GetCertificate(alias);
Org.BouncyCastle.X509.X509Certificate x509cert = certEntry.Certificate;
return x509cert;
}
Org.BouncyCastle.X509.X509Certificate x509cert = ImportCertFromPfx(p12path, p12password);
StringBuilder CertPem = new StringBuilder();
PemWriter CSRPemWriter = new PemWriter(new StringWriter(CertPem));
CSRPemWriter.WriteObject(x509cert);
CSRPemWriter.Writer.Flush();
//get Cert text
var CertPemText = CertPem.ToString();
Console.WriteLine(CertPemText);