I have an integration where I validate a JSON created by another service. They provide a public endpoint to fetch the public certificates to validate against.
But I am setting up a test for this and would like to create the same JWT with Nimbus to sign it with my own private key. So I do this like this (it's a nested and encrypted JWT):
https://connect2id.com/products/nimbus-jose-jwt/examples/signed-and-encrypted-jwt
Then I would like to simulate the public endpoint with a MockServer (https://www.mock-server.com/) endpoint in tests. The problem is that I try to create a PEM certificate from the public key from the senderJWK from the example like this:
var encoded = senderJWK.toPublicKey().getEncoded();
var base64Encoded = Base64.getEncoder().encode(encoded);
return new String(base64Encoded, StandardCharsets.UTF_8);
(I have also tested senderJWK.toRSAPublicKey().getEncoded().)
The code that works with the real certificate does not work to parse it. The code to parse it look like this:
private static RSAPublicKey readPublicKey(String publicKey) throws CertificateException {
var bytes = Base64.getDecoder().decode(publicKey);
var inStream = new ByteArrayInputStream(bytes);
var certificateFactory = CertificateFactory.getInstance(X_509_CERTIFICATE_FACTORY);
var certificate = (X509Certificate) certificateFactory.generateCertificate(inStream);
return (RSAPublicKey) certificate.getPublicKey();
}
The error I am getting is:
java.io.IOException: Too short
at java.base/sun.security.util.DerValue.<init>(DerValue.java:333)
at java.base/sun.security.util.DerInputStream.getDerValue(DerInputStream.java:109)
at java.base/sun.security.x509.X509CertImpl.parse(X509CertImpl.java:1771)
at java.base/sun.security.x509.X509CertImpl.<init>(X509CertImpl.java:183)
... 100 common frames omitted
Wrapped by: java.security.cert.CertificateException: Unable to initialize, java.io.IOException: Too short
at java.base/sun.security.x509.X509CertImpl.<init>(X509CertImpl.java:186)
at java.base/sun.security.provider.X509Factory.engineGenerateCertificate(X509Factory.java:105)
at java.base/java.security.cert.CertificateFactory.generateCertificate(CertificateFactory.java:355)
... 95 common frames omitted
Ok, I think what I need to do is create a X509 certificate from java, and then use the private and public keys from that in the signing and verification.
Found these resources on how to use bouncy castle to do that:
Self signed X509 Certificate with Bouncy Castle in Java
How to create a X509 certificate using Java?
Edit: I got it working fine with that.
Related
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.
I am developing a backend application using Spring Boot with Java. I have to fulfill this requirement: when a specific controller of my application is called I have to return a JWT (created by me) to the client.
My application has a certificate that I created using an openssl command.
The command I used generated a .pem certificate which I converted to a .crt certificate (still using openssl).
This is the certificate (.pem version):
-----BEGIN CERTIFICATE-----
MIID3zCCAsegAwIBAgIUaQYkHJTPg5Xl29W18fl1FK3F034NFVDWòEMChvcNAQEL
BQAwfjELMAkGA1UEBhMCSVQxEDAOBgNVBAgMB0JvbG9nbmExEDAOBgNVBAcMB0Jv
bG9nbmExEjAQBgNVBAoMCVVuaXNhbHV0ZTEdMBsGA1UECwwUU3ZpbHVwcG8tTXVs
dGljYW5hbGUxGDAWBgNVBAMMD2JlLXByZW5vdGF6aW9uaTAgFw0yMTEwMTgxNjIx
MDJaGA8yMTIxMDkyNDE2MjEwMlowfjELMAkGA1UEBhMCSVQxEDAOBgNVBAgMB0Jv
bG9nbmExEDAOBgN5KLT56L5KJ6L5JK3L434NREDFEFEL3WECN4CXaqre35EdMBsG
A1UECwwUU3ZpbHVwcG8tTXVsdGljYW5hbGUxGDAWBgNVBAMMD2JlLXByZW5vdGF6
aW9uaTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALrM4q5Azkjm32Ep
MvtcxnA0Ri+6m2DOOjNvJE5rn392hfiZFZcf4K/NCR8F1uM4thru3RsU5mAuhSrK
Nn58fP5KFKVJFLFJDEHWEB3R4RRIMom7PBusRXPbWM+xr0mGdP3SdXr2c71SvSBQ
KVCEBWfR86qSDSDSDSDXCXs4534rI95R7PYXSOrrH7z89u6p9unSI0OKlEwoVOhM
90JdwhVkc6TSSuFBcsFoXlUjcBDYIkZVIgXpj2+EqhDxUSKIlQr6WKWb3DYaGLyG
/9UUPAbLpMTd+D/T9d+cmM3X0WtoNSSvB30qthMFPs9Pdv5Keba7jbfYyYhpECVu
HVCujSkCAwEAAaNTMFEwHQYDVR0OBBYEFCh3SmbuLXhoCtKEq4wSqlBsPQPOMB8G
A1UdIwQYMBaAFCh3SmbuLXhoCtKEq4wSqlBsPQPOMA8GA1UdEwEB/wQFMAMBAf8w
DQYJKoZIhvcNAQELBQADggEBAA38HsRF4NPEITje0hzQmZG0Zhz2C+2EWH9Z+3ow
JfGc/y40I1WYja23zCwyUndQ5M8QXo4OYt8/yNiuGDPQf/n0QqDQ6ynwfkLkmr6m
3a7ocDT8eUFbisEZGWuYqInAi6oiRNc//NYEQOvapNkasu01p/mLhlrINoRIMwX0
JC5EovSivp05xSB6LHuT454545wddsfcwejh23jkashaksaslasLAF9348D8FlXU
X+cD5wvsGQcwmEu64BxEauDGOSybRDD6ppfSPtTivhR0UyLK9hAQB76Ns6oRbBCc
aLEzzgYhOTKHjkmI27qqUa16Jf6TFJe22Q6wbtOrEOnxDiw=
-----END CERTIFICATE-----
I need to create a JWT token (to be returned to the client) starting from this specific certificate that I have saved within my application. What is the easiest way to do this using Java (possibly with standard Java libraries)?
Unfortunately I am not an expert on tokens, public keys, private keys etc ... I just know that the JWT token I create must allow the client to authenticate to me when it calls new endpoints (managed by other controllers in my application, which will validate the token!)
I will suggest the following way to proceed further:
Follow the blog and resurrect a minimalistic running version that has JWT authentication integrated.
https://medium.com/wolox/securing-applications-with-jwt-spring-boot-da24d3d98f83
Now change AuthenticationFilter class and instead of using the hardcoded key in successfulAuthentication method use the private key of your certificate. You can retrieve the private key using bouncy castle libraries and the method defined in blog section 4.2: https://www.baeldung.com/java-read-pem-file-keys
public RSAPrivateKey readPrivateKey(File file) throws Exception {
KeyFactory factory = KeyFactory.getInstance("RSA");
try (FileReader keyReader = new FileReader(file);
PemReader pemReader = new PemReader(keyReader)) {
PemObject pemObject = pemReader.readPemObject();
byte[] content = pemObject.getContent();
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(content);
return (RSAPrivateKey) factory.generatePrivate(privKeySpec);
}
}
Once you retrieve the RSAKey use the get the getEncoded() (https://docs.oracle.com/javase/8/docs/api/java/security/Key.html#getEncoded--)
method of retrieved RSAPRivateKey instead of KEY.getBytes() in line 54 of AuthenticationFilter class
I'm trying to verify a certificate , that was given to me using Java, in the following manner
try{
FileInputStream fr = new FileInputStream(pathtoCertificate);
CertificateFactory cf = CertificateFactory.getInstance("X509");
X509Certificate c = (X509Certificate)
cf.generateCertificate(fr);
System.out.println("++++Certificate Verification++++++++");
PublicKey pk = c.getPublicKey();
c.verify(pk);....//63
System.out.println("++This certificate is VALID++");
.....
}
catch(CertificateException e){
e.printStackTrace();
System.out.println("Certificate is Invalid");
}
However, I keep getting the following error
java.security.SignatureException: Signature does not match.
at sun.security.x509.X509CertImpl.verify(Unknown Source)
at sun.security.x509.X509CertImpl.verify(Unknown Source)
at Main.printCertificate(Main.java:63)
at Main.main(Main.java:41)
I've only been provided with a certificate file(which I need to verify), a private key(corresponding to the above file ) and a certificate from a CA.
I'm quite new to Java Security. So any thoughts on how I could verify the certificate file would be great.
Well I finally figured it out. For folks who may reach here in the future.
The reason the error was being thrown is that the certificate in my case was not self signed . Therefore I merely had to use the certificate issued by the CA(as mentioned in my Q) to verify the public key.
The rest remained the same.
I use a third party tool to verify signature and to get certificate detail(like serial number, CA etc..) from signature. The problem with this utility is it is licensed and works on certain machines only.
Can i validate the signature against the data using simple java or .net code?(instead of using paid application). I dont have private key to extract certificate information from signed data.
Or if someone can suggest sample code in java or .net to extract certificate detail if i have pfx file. Of from signed data.
Data is signed with asymmetric encryption.
To extract detail from certificate:
Make a string which keeps certificate data. Just ensure it has -----BEGIN CERTIFICATE----- in starting and -----END CERTIFICATE----- in end.
Now use the following code in Java to extract certificate detail.
InputStream inStream = new ByteArrayInputStream(certString.toString().getBytes("UTF-8"));
BufferedInputStream bis = new BufferedInputStream(inStream);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(bis);
X509Certificate xCert = (X509Certificate)cert;
System.out.println("Certificate Type: "+cert.getType());
System.out.println("Public Key: \n"+cert.getPublicKey());
try{
System.out.println("Signature Algorithm"+xCert.getSigAlgName());
System.out.println("IssuerDN : "+xCert.getIssuerDN());
System.out.println("Serial Number : "+xCert.getSerialNumber());
System.out.println("SubjectDN : "+xCert.getSubjectDN());
}catch(Exception exp){
:
}
If you are having the PFX file, then that may contain the public key certificate which will be required to verify the signature.
Alternatively, if your signature is a PKCS#7 signature, then the signature itself will hold the data, signature and the certificate. Assuming PKCS#7 is not detached.
You need to ask your signer, how is he transferring his certificate for validation.
I have PEM format file, How can verify the signature in Java, as I followed http://download.oracle.com/javase/tutorial/security/apisign/versig.html but found that Java doesnt support PEM
You can read a certificate in a PEM file using BouncyCastle's PEMReader. If the content is an X.509 certificate, you should get an instance of X509Certificate and verify it as you want from there.
EDIT: Here is what the code should look like (not tried):
// The key with which you want to verify the cert.
// This is probably a CA certificate's public key.
PublicKey publicKey = ...;
PEMReader reader = new PEMReader(new FileReader("/path/to/file.pem"));
Object pemObject = reader.readObject();
if (pemObject instanceof X509Certificate) {
X509Certificate cert = (X509Certificate)pemObject;
cert.checkValidity(); // to check it's valid in time
cert.verify(publicKey); // verify the sig. using the issuer's public key
}
(Of course, as with any I/O operations, you'll need to close the reader perhaps with try/finally.)
Note that checkValidity and verify don't return anything: instead, they throw exceptions if when they fail.