Get alternative names of X.509 certificate objects in java
I tried to use the solution provided in this link .
I am getting the following error when I tried to read X.509 Certificate alternatives
java.lang.NoSuchMethodError: org.bouncycastle.asn1.ASN1InputStream.readObject () Lorg / bouncycastle / asn1 / DERObject;
The bottom line of code
FileASN1InputStream decoder = new ASN1InputStream ((byte []) item.toArray ());
DEREncodable encoded = decoder.readObject ();
.der is used to create a certificate as follows.
X509Certificate cert=null;
fis = new FileInputStream(file.getAbsoluteFile()); //.der file
bis = new BufferedInputStream(fis);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
while (bis.available() > 0) {
try{
cert = cf.generateCertificate(bis);
}
catch (CertificateException e) {
e.printStackTrace();
}
List list=getSubjectAlternativeNames((X509Certificate) cert);
Below is a solution derived from the link mentioned above.
public static List<String> getSubjectAlternativeNames(X509Certificate certificate) {
List<String> identities = new ArrayList<String>();
try {
Collection<List<?>> altNames = certificate.getSubjectAlternativeNames();
// Check that the certificate includes the SubjectAltName extension
if (altNames == null)
return Collections.emptyList();
// Use the type OtherName to search for the certified server name
for (List item : altNames) {
Integer type = (Integer) item.get(0);
if (type == 0)
// Type OtherName found so return the associated value
try {
// Value is encoded using ASN.1 so decode it to get the server identity
ASN1InputStream decoder = new ASN1InputStream((byte[]) item.toArray()[1]);
DEREncodable encoded = decoder.readObject();
encoded = ((DERSequence) encoded).getObjectAt(1);
encoded = ((DERTaggedObject) encoded).getObject();
encoded = ((DERTaggedObject) encoded).getObject();
String identity = ((DERUTF8String) encoded).getString();
// Add the decoded server name to the list of identities
identities.add(identity);
}
catch (UnsupportedEncodingException e) {
e.printStackTrace();
// log.error("Error decoding subjectAltName" + e.getLocalizedMessage(),e);
}
catch (Exception e) {
// log.error("Error decoding subjectAltName" + e.getLocalizedMessage(),e);
e.printStackTrace();
}
// Other types are not good for XMPP so ignore them
//log.warn("SubjectAltName of invalid type found: " + certificate);
}
}
catch (CertificateParsingException e) {
e.printStackTrace();
// log.error("Error parsing SubjectAltName in certificate: " + certificate + "\r\nerror:" + e.getLocalizedMessage(),e);
}
return identities;
}
Is it because I was not using the correct .jar file?
.jar i used is -> bcprov-jdk16-1.45.jar
Suggest me where I went wrong.
source to share
I tried with your code for me, it works, I tested with certificate exported from Internet Explorer
Internet Explorer -> Tools -> Internet Options -> Content -> Certificates -> Untrusted Publishers -> www.google.com
I exported this as ".cer", I made a few changes to your code
public static List<String> getSubjectAlternativeNames(X509Certificate certificate) {
List<String> identities = new ArrayList<String>();
try {
Collection<List<?>> altNames = certificate.getSubjectAlternativeNames();
if (altNames == null)
return Collections.emptyList();
for (List item : altNames) {
Integer type = (Integer) item.get(0);
if (type == 0 || type == 2){
try {
ASN1InputStream decoder=null;
if(item.toArray()[1] instanceof byte[])
decoder = new ASN1InputStream((byte[]) item.toArray()[1]);
else if(item.toArray()[1] instanceof String)
identities.add( (String) item.toArray()[1] );
if(decoder==null) continue;
DEREncodable encoded = decoder.readObject();
encoded = ((DERSequence) encoded).getObjectAt(1);
encoded = ((DERTaggedObject) encoded).getObject();
encoded = ((DERTaggedObject) encoded).getObject();
String identity = ((DERUTF8String) encoded).getString();
identities.add(identity);
}
catch (UnsupportedEncodingException e) {
log.error("Error decoding subjectAltName" + e.getLocalizedMessage(),e);
}
catch (Exception e) {
log.error("Error decoding subjectAltName" + e.getLocalizedMessage(),e);
}
}else{
log.warn("SubjectAltName of invalid type found: " + certificate);
}
}
}
catch (CertificateParsingException e) {
log.error("Error parsing SubjectAltName in certificate: " + certificate + "\r\nerror:" + e.getLocalizedMessage(),e);
}
return identities;
}
I saved the file in the c: \ aa1.cer directory
X509Certificate cert=null;
FileInputStream fis = new FileInputStream("c:\\aa1.cer");
BufferedInputStream bis = new BufferedInputStream(fis);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
if (bis.available() > 0)
try{
cert = (X509Certificate)cf.generateCertificate(bis);
}
catch (CertificateException e) {
e.printStackTrace();
}
System.out.println(CertificateInfo.getSubjectAlternativeNames(cert));
I got the output as [www.google.com, google.com]
Please check your certificate, I think the problem is with your certificate
source to share
Many examples use hardcoded integers. For readability, I prefer to use:
GeneralName.dNSName
=2
GeneralName.iPAddress
=7
- ... etc.
Code:
public static String[] parseHostNames(X509Certificate cert) {
List<String> hostNameList = new ArrayList<>();
try {
Collection<List<?>> altNames = cert.getSubjectAlternativeNames();
if (altNames != null) {
for(List<?> altName : altNames) {
if(altName.size()< 2) continue;
switch((Integer)altName.get(0)) {
case GeneralName.dNSName:
case GeneralName.iPAddress:
Object data = altName.get(1);
if (data instanceof String) {
hostNameList.add(((String)data));
}
break;
default:
}
}
}
System.out.println("Parsed hostNames: " + String.join(", ", hostNameList));
} catch(CertificateParsingException | IOException e) {
System.err.println("Can't parse hostNames from this cert.");
e.printStackTrace();
}
return hostNameList.toArray(new String[hostNameList.size()]);
}
Note: The accepted answer checks byte[]
but does not compile on my system. I found several other examples using byte[]
calling new ASN1InputStream((byte[])data).readObject();
, but I don't have a certificate to verify it, so I removed it from my example.
source to share