Create x.509 certificate using bouncycastle with certificate path (cert chain) - java

Hy Guys! I'm trying to create x.509 certificate using bouncycastle, which should be signed by another certificate and store it PEM base 64 format.
I've already have self-signed certificate (public and private key). Now I want to create new one and sign it with existing self-signed certificate.
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
keyPairGenerator.initialize(1024, new SecureRandom());
KeyPair keyPair = keyPairGenerator.generateKeyPair();
X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
X500Principal dnName = new X500Principal("CN=Sergey");
certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
certGen.setSubjectDN(dnName);
certGen.setIssuerDN(caCert.getSubjectX500Principal());
certGen.setNotBefore(validityBeginDate);
certGen.setNotAfter(validityEndDate);
certGen.setPublicKey(keyPair.getPublic());
certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(caCert));
certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(keyPair.getPublic()));
X509Certificate cert = certGen.generate(caCertPrivateKey, "BC");
Verification passed without exceptions, which means from my point of view that it was successfully signed by caCert:
cert.verify(caCert.getPublicKey());
Then I decode it to the PEM base 64:
PEMWriter pemWriter = new PEMWriter(new PrintWriter(System.out));
pemWriter.writeObject(cert);
pemWriter.flush();
I get something like this in the output:
-----BEGIN CERTIFICATE-----
MIIDDjCCAnegAwIBAgIBFDAN........
-----END CERTIFICATE-----
When I open it, I see the next:
Why there is no certification chain if it was successfully signed by caCert?
What need to be changed in my code to see certification chain as I expected?

I was able to find solution. Actually code works as expected. I didn't see chain of certificates because my caRoot certificate wasn't added to the trusted store. After I add my sel-signed certificate to the trusted root certified centers I see the whole certification chain as I expected.

Related

How do you convert PKCS#12 String to Certificate and PrivateKey?

I am receiving the following String from a certificate stored in Azure Key Vault. I am using the Secret API in order to retrieve both the certificate and the private key related to this cert.
Initially the certificate was uploaded using a .pfx file to Azure Key vault. Now I need to create a Certificate and a PrivateKey to allow client authentication to a 3rd party system and I am using the given String retrieved from the API, however I am note sure how to get around that in Java.
I took some hints from this link in C# however I am pretty certain that this method doesn't work like that in Java. In particular an X509Certificate or a Certificate in general doesn't hold any information about the PrivateKey in Java, unlike C#, and I am not sure how to extract that information from given String in Java.
This works as expected to retrieve the certificate from the String retrieved from the API
String secret = azureSecret.getValue();
byte[] certkey = Base64.getDecoder().decode(secret);
ByteArrayInputStream inputStream = new ByteArrayInputStream(certkey);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(inputStream);
The azureSecret.getValue() format is like the following however I am not sure how to get PrivateKey out of the given String
MIIKvgIBaaZd6Euf3EYwYdHrIIKYzCC...
YES, Java X509Certificate and Certificate is only the certificate. Instead use KeyStore which can contain multiple entries each of which is either a 'trusted' certificate (for someone else), or a privatekey plus certificate plus other chain cert(s) (if applicable) for yourself, or (not relevant here) a 'secret' (symmetric) key. PKCS12 is supported as one type of KeyStore along with others not relevant here, so after the base64-decoding you already have do something like:
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(inputstreamfromvaultvalue, password);
// then
PrivateKey pkey = (PrivateKey) ks.getKey(alias, password);
// and
Certificate cert = ks.getCertificate(alias); // if you only need the leaf cert
// or
Certificate[] chain = ks.getCertificateChain(alias); // usually
But if you want to do client authentication in TLS/SSL (including HTTPS), you give the JSSE KeyManager the whole keystore object not the individual pieces (privatekey and certificates). Similarly to verify the peer in TLS/SSL, you give TrustManager a keystore containing trusted certificates, usually root CAs and often defaulted to a built-in set of public root CAs.

Convert java cipher decrypt to python equivalent

I'm trying to convert the below java code to python:
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
serverCertificate = (X509Certificate) certFactory.generateCertificate(new FileInputStream(certFile));
Cipher decryptCipher = Cipher.getInstance("RSA");
decryptCipher.init(Cipher.DECRYPT_MODE, serverCertificate.getPublicKey());
byte[] decryptedClientChallengeBytes = decryptCipher.doFinal(encryptedClientChallengeBytes);
The first part I think it's resolved with this:
from cryptography import x509
from cryptography.hazmat.backends import default_backend
cert = x509.load_pem_x509_certificate(key,default_backend())
cert = cert.public_key()
But I can't convert the decrypt part. I´ve tried various methods like PKCS1_v1_5.
I'm trying to implement this https://docs.oracle.com/en/cloud/saas/marketing/responsys-rest-api/RequestUserAuthorization.html
And the step i'm stacked says: "You should receive the following response from the server. Decrypt (using the RSA algorithm) the encrypted clientChallenge using server certificate's public key (which you should have downloaded and stored on your system)."
The clientChallenge received is like this:
"hc8w8gElQJQa45WGofZwsOAi4wwEIaFClI1A2PvvCw4YUc4rbnOpedkBgrNsr0FpZ6DVV_vnZNsAGg_ph75VqeKD-R-cy_RkQIdvn0W_nb0RvPGZI7zA98_t9-rJTiTSsrJ6j8KzlWV_MLydUcBBR1iotL6kXXuFKuXtTAniPx_b6do4D5p9iI7yhTfxp9knRRG6XoItgNGJwm8AnGsThIoZw9lSgUm3Dy9yzYvhXQDYKAUNc3tqd8WcSswlb5coN18Gc-We2E0xE7xehq15Ns4QNwX6WdRrfehSgOObdckUYAx7MyLT-vyfJ-APVEVrZmC18wo-Qr2A-n1NT0ARVQ"
And the server certificate is a .CER with this format:
-----BEGIN CERTIFICATE-----
.....
-----END CERTIFICATE-----
Any help would be awesome!

How can i match a CERTIFICATE against CSR in JAVA

I don't have idea how to achieve this. I refereed this link where i can match my csr with certificate.
In this tool they are getting the hash from certificate and csr and matching them.
So I also tried to get hash but i am not able to get proper value.
Here is my implementation:
BufferedInputStream bufferedIS = new BufferedInputStream(mulitpartFile.getInputStream());
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Certificate certificate = certificateFactory.generateCertificate(bufferedIS);
X509Certificate x509Certificate = (X509Certificate) certificate;
String thumbprint = DigestUtils.sha512Hex(x509Certificate.getEncoded());
But i am not getting the exact value what i am getting in there tool.
Here is my CSR and CERTIFICATE.
CSR
-----BEGIN NEW CERTIFICATE REQUEST-----
MIIC/jCCAeYCAQAwdDENMAsGA1UEAxMEMkdSSTEMMAoGA1UECxMDQUlCMRAwDgYD
VQQLEwdEZXZpY2VzMTYwNAYDVQQKEy1FdXJvcGVhbiBBZXJvbmF1dGljIERlZmVu
Y2UgYW5kIFNwYWNlIENvbXBhbnkxCzAJBgNVBAYTAkRFMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEA8yh1MlHMF4Q8WJAUDjaQ6pnEKEUdl85Dhyg/STF2
uxHnasR9aBavd5he20IVmzHX3X8rG2RM3lN2QP/3j0zObpPTPHvNhp/CuTFw6qrX
JovktZv0VNIZhAWbZHl4Ar+2zX44c0VbuHOZDbrYlteREdbg0x9hNj3AObQe37TQ
33rA4jABbMEODtGlPprMSsSzUCiHJ6KRSVGGuIUAIHjgIqciLOvqfgICD6mgHi58
yeM1hi3MfXIm8BAlIFMCJZgO+IDDBFkfvCPAdDmnTy9O8e6eXFQ2etFlAgYN2l16
3NqBQw5cJ5Oyg8By4xPcreRDgmqzz2YoIZnqJGW0Y46tCwIDAQABoEUwQwYJKoZI
hvcNAQkOMTYwNDAOBgNVHQ8BAf8EBAMCBsAwIgYDVR0RAQH/BBgwFogOKwYBBAHr
EIZKAv8wLg+HBMLOFp4wDQYJKoZIhvcNAQELBQADggEBALN3kYMVHlrPWO/8yv3n
imKEgXZZRPK9rVnPtkxVKJFAnnLwMFLx8rRCxHO72FwaO+O/IXXrsUSzpOsw+QOa
+PmKDDoxNwrv66ZjKtmUItBcmT4nJHRQZzvtW1vX6m7OjB68tKeDuqoWNpbObcJv
D3TMK9XWW9NqS1sCcGF8l/xjvsWYvEOv9M3eZzq+quWb6gROQT6oFajjjpLAsfDU
/yh9Ax2bHqIjpPa8WGXkST+2Eq06SUZOBsuUae3USoEmCPNJ6pAGpIeU+HTNygmS
s9UYyMMEo8dX+CYsRk+U65PfYbXvyi0LfPAhWeEucgAcxfOVtePsSaVLDMG+4Ysi
e2M=
-----END NEW CERTIFICATE REQUEST-----
CERTIFICATE
-----BEGIN CERTIFICATE-----
MIIDyTCCArGgAwIBAgIUewyWehvM8zXxeNHiCw6jnGNj+hQwDQYJKoZIhvcNAQEL
BQAwdDENMAsGA1UEAxMEMkdSSTEMMAoGA1UECxMDQUlCMRAwDgYDVQQLEwdEZXZp
Y2VzMTYwNAYDVQQKEy1FdXJvcGVhbiBBZXJvbmF1dGljIERlZmVuY2UgYW5kIFNw
YWNlIENvbXBhbnkxCzAJBgNVBAYTAkRFMB4XDTE5MDMyMDA1NDIzOVoXDTIwMDMx
OTA1NDIzOVowdDENMAsGA1UEAxMEMkdSSTEMMAoGA1UECxMDQUlCMRAwDgYDVQQL
EwdEZXZpY2VzMTYwNAYDVQQKEy1FdXJvcGVhbiBBZXJvbmF1dGljIERlZmVuY2Ug
YW5kIFNwYWNlIENvbXBhbnkxCzAJBgNVBAYTAkRFMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEA8yh1MlHMF4Q8WJAUDjaQ6pnEKEUdl85Dhyg/STF2uxHn
asR9aBavd5he20IVmzHX3X8rG2RM3lN2QP/3j0zObpPTPHvNhp/CuTFw6qrXJovk
tZv0VNIZhAWbZHl4Ar+2zX44c0VbuHOZDbrYlteREdbg0x9hNj3AObQe37TQ33rA
4jABbMEODtGlPprMSsSzUCiHJ6KRSVGGuIUAIHjgIqciLOvqfgICD6mgHi58yeM1
hi3MfXIm8BAlIFMCJZgO+IDDBFkfvCPAdDmnTy9O8e6eXFQ2etFlAgYN2l163NqB
Qw5cJ5Oyg8By4xPcreRDgmqzz2YoIZnqJGW0Y46tCwIDAQABo1MwUTAdBgNVHQ4E
FgQUXMHQnnIrkSavE10n8wS/ZD9yz8wwHwYDVR0jBBgwFoAUXMHQnnIrkSavE10n
8wS/ZD9yz8wwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAujxz
tDjgy5XIJLSbBdBQorwYa51rWaA1zlRwDW9j690JncoIsNeiuWA1MzlpcWFEwcYz
KYciJcUZSwtpao9mioGrTDrChPkfeJrpaKhUNQkQbc59YpiKyShE05KKpFjSZJzZ
p5K8mQfq6OHlee4khxVJA/VirtoWlmtzzV5XyqbCDLIBZhOj005D1eu8KBq+qN09
KKiiUC16Lgj6UdBAfQWf79yN6ymxH9mex5T2HAJQir4rdx1nc5nFpC2V1pJpS8fW
EJNQgipn6CsufB58j4eWu1QAfceRvq7C7k3MbQk3wlW67m/LVQWSu49S6MmRb0EW
a9FacTxnmJFElBO5Gg==
-----END CERTIFICATE-----
Anyone having idea to achieve this please help me.
Your code creates the hash from the complete certificate x509Certificate.getEncoded() and not the public key.

How to load public certificate from pem file?

I was trying to extract RES public key from the file below
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----
Here is the code i did..
public static PublicKey loadPublicKeyFromFile(File publicKeyFile) throws Exception {
FileReader file = new FileReader(publicKeyFile);
PemReader reader = new PemReader(file);
X509EncodedKeySpec caKeySpec = new X509EncodedKeySpec(reader.readPemObject().getContent());
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey caKey = kf.generatePublic(caKeySpec);
return caKey;
}
But It throws out
java.security.InvalidKeyException: IOException: ObjectIdentifier() -- data isn't an object ID
What's the appropriate way to extract RES Public key from a file..
An X.509 certificate and an X509EncodedKeySpec are quite different structures, and trying to parse a cert as a key won't work.
Java's X509EncodedKeySpec is actually SubjectPublicKeyInfo from X.509 or equivalent and more convenient PKIX also linked from Key, which is only a small part of a certificate.
What you need to do is read and parse the cert and then extract the pubkey from the cert.
Standard SunJCE CertificateFactory can do it
(and can read either PEM or DER to boot) like this:
CertificateFactory fact = CertificateFactory.getInstance("X.509");
FileInputStream is = new FileInputStream (args[0]);
X509Certificate cer = (X509Certificate) fact.generateCertificate(is);
PublicKey key = cer.getPublicKey();
is.close();
// add error handling as appropriate, try-with-resources is often good
If you have BouncyCastle you can use its provider the same way (just add a second argument to .getInstance or set the default provider list order), or you can use PEMParser with JcaX509CertificateConverter -- which effectively does the same thing, internally running the data through a CertificateFactory.
With Sun JVM it is also possible with
import java.security.cert.X509Certificate;
import sun.security.x509.X509CertImpl;
InputStream is = ...
X509Certificate crt = new X509CertImpl(is);
, although I'd prefer the accepted answer as JVM implementation-independent one.
Under the hood, in Sun JVM, CertificateFactory(more precisely, X509Factory) does instantiate X509CertImpl , but there is very subtle difference between the two approaches: CertificateFactory caches X509 Certificate instances by binary content of the input stream, the one that can be retrieved by cer.getEncoded().
The cache can be cleared by
fact.generateCertificate(null);

How can I establish trust in a X.509 certificate issued by an intermediary?

I have an XML document that is digitally signed. I use the XML Digital Signature API to verify the signature. But this document is a SAML 2.0 Assertion that will be used for single sign-on into our web application. As such I need to establish trust in the X.509 certificate used to sign the XML document.
The code I am using to try and establish this trust is:
String filename = System.getProperty("java.home") + "/lib/security/cacerts".replace('/', File.separatorChar);
FileInputStream is = new FileInputStream(filename);
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
String password = "changeit";
keystore.load(is, password.toCharArray());
PKIXParameters params = new PKIXParameters(keystore);
params.setRevocationEnabled(false);
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
CertPath certPath = certFactory.generateCertPath(Arrays.asList(signatureCertificate));
CertPathValidator certPathValidator = CertPathValidator.getInstance(CertPathValidator.getDefaultType());
CertPathValidatorResult result = certPathValidator.validate(certPath, params);
PKIXCertPathValidatorResult pkixResult = (PKIXCertPathValidatorResult) result;
TrustAnchor ta = pkixResult.getTrustAnchor();
X509Certificate cert = ta.getTrustedCert();
When run the call to certPathValidator.validate() throws a CertPathValidatorException with the message Path does not chain with any of the trust anchors.
Examining the certificate, it says it has been issued by OU=www.verisign.com/CPS Incorp.by Ref. LIABILITY LTD.(c)97 VeriSign,OU=VeriSign International Server CA - Class 3,OU=VeriSign\, Inc.,O=VeriSign Trust Network.
This is not one of the trust anchors in the JDK's cacerts key store.
However, using IE to inspect a similar certificate's trust chain I see that www.verisign.com/CPS Incorp.'s was issued by VeriSign Class 3 Public Primary CA, which does appear to be one of the trust anchors in the JDK's cacerts key store.
My question: how can I get Java to validate this certificate?
Insert the public cert of the issuing CA into the cacert keystore.
edit: You can use keytool or one of the other tools out there. Article describes keytool use: keytool-Key and Certificate Management Tool
Exactly what xelco said - add the intermediate CA:
OU=www.verisign.com/CPS Incorp.by Ref. LIABILITY LTD.(c)97 VeriSign,OU=VeriSign International Server CA - Class 3,OU=VeriSign\, Inc.,O=VeriSign Trust Network
To the JDK's key store. You can use keytool to do it.
Reason: Generally when an XML message is signed, the signature includes only the signing cert. X509 Certificates are like singly linked lists. The End Entity points to its issuer. The issuer points to its issuer until you get to a self-signed Root CA, which points to itself. To verify a certificate according to PKIX, the validator needs to be able to build the whole CA chain from end entity to self signed root, so every part of the chain (except for the end entity) must be in your certificate store.
I've got better solution. I've found java service that can be run and do everything for us.
Java:
http://code.google.com/p/java-use-examples/source/browse/trunk/src/com/aw/ad/util/InstallCert.java

Categories