fix: add pem support for cert sign

This commit is contained in:
sbplat 2024-01-03 18:21:59 -05:00
parent 4b0df4ffd5
commit d96a3db60a

View File

@ -1,21 +1,37 @@
package stirling.software.SPDF.controller.api.security; package stirling.software.SPDF.controller.api.security;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.KeyStoreException; import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Security; import java.security.Security;
import java.security.UnrecoverableKeyException; import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.Calendar; import java.util.Calendar;
import org.apache.pdfbox.examples.signature.CreateSignatureBase; import org.apache.pdfbox.examples.signature.CreateSignatureBase;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.pkcs.PKCSException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@ -76,23 +92,27 @@ public class CertSignController {
throw new IllegalArgumentException("Cert type must be provided"); throw new IllegalArgumentException("Cert type must be provided");
} }
InputStream ksInputStream = null; KeyStore ks = null;
switch (certType) { switch (certType) {
case "PKCS12": case "PKCS12":
ksInputStream = p12File.getInputStream(); ks = KeyStore.getInstance("PKCS12");
ks.load(p12File.getInputStream(), password.toCharArray());
break; break;
case "PEM": case "PEM":
throw new IllegalArgumentException("TODO: PEM not supported yet"); ks = KeyStore.getInstance("JKS");
// ksInputStream = privateKeyFile.getInputStream(); ks.load(null);
// break; PrivateKey privateKey = getPrivateKeyFromPEM(privateKeyFile.getBytes(), password);
Certificate cert = (Certificate) getCertificateFromPEM(certFile.getBytes());
ks.setKeyEntry(
"alias", privateKey, password.toCharArray(), new Certificate[] {cert});
break;
default: default:
throw new IllegalArgumentException("Invalid cert type: " + certType); throw new IllegalArgumentException("Invalid cert type: " + certType);
} }
// TODO: page number // TODO: page number
KeyStore ks = getKeyStore(ksInputStream, password);
CreateSignature createSignature = new CreateSignature(ks, password.toCharArray()); CreateSignature createSignature = new CreateSignature(ks, password.toCharArray());
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
sign(pdf.getBytes(), baos, createSignature, name, location, reason); sign(pdf.getBytes(), baos, createSignature, name, location, reason);
@ -100,12 +120,6 @@ public class CertSignController {
baos, pdf.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_signed.pdf"); baos, pdf.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_signed.pdf");
} }
private static KeyStore getKeyStore(InputStream is, String password) throws Exception {
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(is, password.toCharArray());
return ks;
}
private static void sign( private static void sign(
byte[] input, byte[] input,
OutputStream output, OutputStream output,
@ -129,14 +143,35 @@ public class CertSignController {
} }
} }
// private byte[] parsePEM(byte[] content) throws IOException { private PrivateKey getPrivateKeyFromPEM(byte[] pemBytes, String password)
// PemReader pemReader = throws IOException, OperatorCreationException, PKCSException {
// new PemReader(new InputStreamReader(new ByteArrayInputStream(content))); try (PEMParser pemParser =
// return pemReader.readPemObject().getContent(); new PEMParser(new InputStreamReader(new ByteArrayInputStream(pemBytes)))) {
// } Object pemObject = pemParser.readObject();
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
PrivateKeyInfo pkInfo;
if (pemObject instanceof PKCS8EncryptedPrivateKeyInfo) {
InputDecryptorProvider decProv =
new JceOpenSSLPKCS8DecryptorProviderBuilder().build(password.toCharArray());
pkInfo = ((PKCS8EncryptedPrivateKeyInfo) pemObject).decryptPrivateKeyInfo(decProv);
} else if (pemObject instanceof PEMEncryptedKeyPair) {
PEMDecryptorProvider decProv =
new JcePEMDecryptorProviderBuilder().build(password.toCharArray());
pkInfo =
((PEMEncryptedKeyPair) pemObject)
.decryptKeyPair(decProv)
.getPrivateKeyInfo();
} else {
pkInfo = ((PEMKeyPair) pemObject).getPrivateKeyInfo();
}
return converter.getPrivateKey(pkInfo);
}
}
// private boolean isPEM(byte[] content) { private Certificate getCertificateFromPEM(byte[] pemBytes)
// String contentStr = new String(content); throws IOException, CertificateException {
// return contentStr.contains("-----BEGIN") && contentStr.contains("-----END"); try (ByteArrayInputStream bis = new ByteArrayInputStream(pemBytes)) {
// } return CertificateFactory.getInstance("X.509").generateCertificate(bis);
}
}
} }