18

I'm trying to read a custom extension from a digital certificate. I know the value is a GeneralString encoded in DER. Is there an easy way to correctly decode it and get a Java String? I tried the following, but 's' includes some of the encoding metadata as junk characters at the start of the string.

byte[] ext = cert.getExtensionValue("1.2.3.4");
String s= new String(ext);
System.out.println(s);

Is there a quick and easy way to do this? Or do I really need to use some full fledged ASN.1 library?

Thanks!

Ragesh
  • 2,800
  • 2
  • 25
  • 36

4 Answers4

17

Using instructions contained on the following page I've made some changes and the code worked fine with me.

Porting from earlier BC releases to 1.47 and later - The Legion of the Bouncy Castle http://www.bouncycastle.org/wiki/display/JA1/Porting+from+earlier+BC+releases+to+1.47+and+later

private String getExtensionValue(X509Certificate X509Certificate, String oid) throws IOException
{
    String decoded = null;
    byte[] extensionValue = X509Certificate.getExtensionValue(oid);

    if (extensionValue != null)
    {
        ASN1Primitive derObject = toDERObject(extensionValue);
        if (derObject instanceof DEROctetString)
        {
            DEROctetString derOctetString = (DEROctetString) derObject;

            derObject = toDERObject(derOctetString.getOctets());
            if (derObject instanceof ASN1String)
            {
                ASN1String s = (ASN1String)derObject;
                decoded = s.getString();
            }

        }
    }
    return decoded;
}

/**
 * From http://stackoverflow.com/questions/2409618/how-do-i-decode-a-der-encoded-string-in-java
 */
private ASN1Primitive toDERObject(byte[] data) throws IOException
{
    ByteArrayInputStream inStream = new ByteArrayInputStream(data);
    ASN1InputStream asnInputStream = new ASN1InputStream(inStream);

    return asnInputStream.readObject();
}
agnoldo
  • 171
  • 1
  • 4
8

This turns out to be quite straightforward with BouncyCastle:

private String getExtensionValue(X509Certificate X509Certificate, String oid) throws IOException
{
    String decoded = null;
    byte[] extensionValue = X509Certificate.getExtensionValue(oid);

    if (extensionValue != null)
    {
        DERObject derObject = toDERObject(extensionValue);
        if (derObject instanceof DEROctetString)
        {
            DEROctetString derOctetString = (DEROctetString) derObject;

            derObject = toDERObject(derOctetString.getOctets());
            if (derObject instanceof DERUTF8String)
            {
                DERUTF8String s = DERUTF8String.getInstance(derObject);
                decoded = s.getString();
            }

        }
    }
    return decoded;
}

private DERObject toDERObject(byte[] data) throws IOException
{
    ByteArrayInputStream inStream = new ByteArrayInputStream(data);
    ASN1InputStream asnInputStream = new ASN1InputStream(inStream);

    return asnInputStream.readObject();
}
Ragesh
  • 2,800
  • 2
  • 25
  • 36
  • 2
    Hi looks like DERObject has been deprecated. Any chance you can update you code example. Think you now have to use org.bouncycastle.sasn1.Asn1Object instead, but I have not been able to get it to work yet. – user1513388 Nov 04 '12 at 22:26
  • @user1513388 Sorry, this was over 2 years ago. I'm not even using Java in anything right now so I'm not sure if/when I might get around to trying this again. – Ragesh Nov 05 '12 at 07:26
  • 2
    its late but, use ASN1Primitive instead of DerObject. Please follow think link: http://www.bouncycastle.org/wiki/display/JA1/Porting+from+earlier+BC+releases+to+1.47+and+later – Saqib Rezwan Sep 16 '15 at 05:07
5

JcaX509ExtensionUtils does what the answers above do in a much simpler way.

X509Certificate certificate;
byte[] encodedExtensionValue = certificate.getExtensionValue(oid);
if (encodedExtensionValue != null) {
    ASN1Primitive extensionValue = JcaX509ExtensionUtils
            .parseExtensionValue(encodedExtensionValue);
    String values = extensionValue.toString();          
}
varrunr
  • 845
  • 1
  • 11
  • 19
2

In Oracle VM (JDK 7):

    DerValue val = new DerValue(ext);
    String s = val.getGeneralString();

http://www.docjar.com/docs/api/sun/security/util/DerValue.html

NOTE: The original question called for a "quick-and-dirty" solution, so I think this was valid back then, but since it relies on the Sun internal API, it shouldn't be used anymore especially since JDK 9 onwards.

Bouncy Castle is the proper solution for this.

Jouni Aro
  • 2,099
  • 14
  • 30