The error message says:
java.lang.ClassCastException: sun.security.x509.X509CertImpl cannot be cast to ca.carillon.crypto.cert.MyX509Certificate
It's basically saying that one type (sun.security.x509.X509CertImpl
) can't be cast to another type (your MyX509Certificate
class).
The sun.security.x509.X509CertImpl
is a subclass of java.security.cert.X509Certificate
and it's being returned by keystore.getCertificate
method.
What happens is that X509Certificate
is an abstract class, and keystore.getCertificate
will return an internal implementation (a subclass) of it. And this subclass can be different, depending on your environment (JVM, provider, etc). In this case, it's returning a sun.security.x509.X509CertImpl
- you don't see this class anywhere because it's internal to the JVM, and in most cases you actually don't need to know it. All you need to know is that it's a X509Certificate
.
When you create your own subclass of X509Certificate
, the hierarchy will be like this:
X509Certificate <- super class
|
-------------------------------------
| |
sun.security.x509.X509CertImpl MyX509Certificate <- subclasses of X509Certificate
Both the class returned by the kesytore (X509CertImpl
) and your custom class (MyX509Certificate
) are subclasses of X509Certificate
.
When the keystore returns a X509CertImpl
, it's ok to cast it to a X509Certificate
(a subclass instance can be assigned to a variable with the superclass's type).
But if you try to do:
MyX509Certificate my = (MyX509Certificate) keystore.getCertificate(alias);
The keystore is returning a X509CertImpl
, which is a X509Certificate
, but it's not a MyX509Certificate
- you can't cast a "sibling" class to another. The X509CertImpl
only knows about its superclass (X509Certificate
), but it doesn't know about MyX509Certificate
.
If you want to add functionality to a X509Certificate
, perhaps extending it is not the best solution and you should go for the "Composition over Inheritance" approach.
You can create a class that has a X509Certificate
(AKA "a wrapper") and adds functionality by using it:
public class MyCustomCertificate {
// wraps a X509Certificate
private X509Certificate cert;
public MyCustomCertificate(X509Certificate cert) {
this.cert = cert;
}
public void myCustomFunctionality1() {
// do something with this.cert
}
public void myCustomFunctionality2() {
// do something with this.cert
}
// and so on...
}
Then you create this class using the certificate returned by the keystore:
X509Certificate x509Cert = (X509Certificate) keystore.getCertificate(alias);
MyCustomCertificate myCert = new MyCustomCertificate(x509Cert);
X509Certificate
has lots of unimplemented methods, and extending it will require you to implement them (which is not worth doing, IMO). Instead, you can use the certificate returned by the keystore (that already implements all those methods) and add the functionalities you want.
If you really want to extend X509Certificate
, though, you could delegate the methods to the wrapped certificate:
public class MyCustomCertificate extends X509Certificate {
// wraps a X509Certificate
private X509Certificate cert;
public MyCustomCertificate(X509Certificate cert) {
this.cert = cert;
}
public void myCustomFunctionality1() {
// do something with this.cert
}
public void myCustomFunctionality2() {
// do something with this.cert
}
// delegate all X509Certificate methods to the wrapped certificate
public PublicKey getPublicKey() {
return this.cert.getPublicKey();
}
public Principal getSubjectDN() {
return this.cert.getSubjectDN();
}
// and so on...
}