/***** BEGIN LICENSE BLOCK ***** * Version: EPL 1.0/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Eclipse Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.eclipse.org/legal/epl-v10.html * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * Copyright (C) 2006, 2007 Ola Bini <ola@ologix.com> * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the EPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the EPL, the GPL or the LGPL. ***** END LICENSE BLOCK *****/ package org.jruby.ext.openssl; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.math.BigInteger; import java.text.ParseException; import java.util.Collections; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.WeakHashMap; import org.bouncycastle.asn1.ASN1Boolean; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1Enumerated; import org.bouncycastle.asn1.ASN1GeneralizedTime; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1Set; import org.bouncycastle.asn1.ASN1String; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1Null; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.ASN1UTCTime; import org.bouncycastle.asn1.DERBitString; import org.bouncycastle.asn1.BEROctetString; import org.bouncycastle.asn1.BERSequenceGenerator; import org.bouncycastle.asn1.BERSet; import org.bouncycastle.asn1.BERTags; import org.bouncycastle.asn1.DERApplicationSpecific; import org.bouncycastle.asn1.DERBMPString; import org.bouncycastle.asn1.DERBoolean; import org.bouncycastle.asn1.DEREnumerated; import org.bouncycastle.asn1.DERGeneralString; import org.bouncycastle.asn1.DERGeneralizedTime; import org.bouncycastle.asn1.DERIA5String; import org.bouncycastle.asn1.DERInteger; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.DERNumericString; import org.bouncycastle.asn1.DERObjectIdentifier; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DERPrintableString; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERT61String; import org.bouncycastle.asn1.DERTaggedObject; import org.bouncycastle.asn1.DERUTCTime; import org.bouncycastle.asn1.DERUTF8String; import org.bouncycastle.asn1.DERUniversalString; import org.bouncycastle.asn1.DERVisibleString; import org.bouncycastle.asn1.DLSet; import org.jruby.Ruby; import org.jruby.RubyArray; import org.jruby.RubyBignum; import org.jruby.RubyClass; import org.jruby.RubyInteger; import org.jruby.RubyModule; import org.jruby.RubyNumeric; import org.jruby.RubyObject; import org.jruby.RubyString; import org.jruby.RubySymbol; import org.jruby.RubyTime; import org.jruby.anno.JRubyMethod; import org.jruby.exceptions.RaiseException; import org.jruby.runtime.Block; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.Visibility; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.util.ByteList; import org.jruby.ext.openssl.impl.ASN1Registry; import static org.jruby.ext.openssl.OpenSSL.*; import org.jruby.ext.openssl.util.ByteArrayOutputStream; /** * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a> */ public class ASN1 { private static Map<Ruby, Map<String, ASN1ObjectIdentifier>> SYM_TO_OID = new WeakHashMap<Ruby, Map<String, ASN1ObjectIdentifier>>(8); private static Map<Ruby, Map<ASN1ObjectIdentifier, String>> OID_TO_SYM = new WeakHashMap<Ruby, Map<ASN1ObjectIdentifier, String>>(8); private static Map<Ruby, Map<ASN1ObjectIdentifier, Integer>> OID_TO_NID = new WeakHashMap<Ruby, Map<ASN1ObjectIdentifier, Integer>>(8); private static Map<Ruby, Map<Integer, ASN1ObjectIdentifier>> NID_TO_OID = new WeakHashMap<Ruby, Map<Integer, ASN1ObjectIdentifier>>(8); private static Map<Ruby, Map<Integer, String>> NID_TO_SN = new WeakHashMap<Ruby, Map<Integer, String>>(8); private static Map<Ruby, Map<Integer, String>> NID_TO_LN = new WeakHashMap<Ruby, Map<Integer, String>>(8); @SuppressWarnings("unchecked") private static synchronized void initMaps(final Ruby runtime) { final int size = 200; final float fact = 1.0f; SYM_TO_OID.put(runtime, new HashMap<String, ASN1ObjectIdentifier>(size, fact)); OID_TO_SYM.put(runtime, new HashMap<ASN1ObjectIdentifier, String>(size, fact)); OID_TO_NID.put(runtime, new HashMap<ASN1ObjectIdentifier, Integer>(size, fact)); NID_TO_OID.put(runtime, new HashMap<Integer, ASN1ObjectIdentifier>(size, fact)); NID_TO_SN.put(runtime, new HashMap<Integer, String>(size, fact)); NID_TO_LN.put(runtime, new HashMap<Integer, String>(size, fact)); defaultObjects(runtime); } private static void defaultObjects(final Ruby runtime) { addObject(runtime, 0, null, null,"1.2.840.113549.1.12.1"); addObject(runtime, 1, null, "rsadsi","1.2.840.113549"); addObject(runtime, 2, null, "pkcs","1.2.840.113549.1"); addObject(runtime, 3, "MD2", "md2","1.2.840.113549.2.2"); addObject(runtime, 4, "MD5", "md5","1.2.840.113549.2.5"); addObject(runtime, 5, "RC4", "rc4","1.2.840.113549.3.4"); addObject(runtime, 6, null, "rsaEncryption","1.2.840.113549.1.1.1"); addObject(runtime, 7, "RSA-MD2", "md2WithRSAEncryption","1.2.840.113549.1.1.2"); addObject(runtime, 8, "RSA-MD5", "md5WithRSAEncryption","1.2.840.113549.1.1.4"); addObject(runtime, 9, "PBE-MD2-DES", "pbeWithMD2AndDES-CBC","1.2.840.113549.1.5.1"); addObject(runtime, 10, "PBE-MD5-DES", "pbeWithMD5AndDES-CBC","1.2.840.113549.1.5.3"); addObject(runtime, 11, null, "X500","2.5"); addObject(runtime, 12, null, "X509","2.5.4"); addObject(runtime, 13, "CN", "commonName","2.5.4.3"); addObject(runtime, 14, "C", "countryName","2.5.4.6"); addObject(runtime, 15, "L", "localityName","2.5.4.7"); addObject(runtime, 16, "ST", "stateOrProvinceName","2.5.4.8"); addObject(runtime, 17, "O", "organizationName","2.5.4.10"); addObject(runtime, 18, "OU", "organizationalUnitName","2.5.4.11"); addObject(runtime, 19, "RSA", "rsa","2.5.8.1.1"); addObject(runtime, 20, null, "pkcs7","1.2.840.113549.1.7"); addObject(runtime, 21, null, "pkcs7-data","1.2.840.113549.1.7.1"); addObject(runtime, 22, null, "pkcs7-signedData","1.2.840.113549.1.7.2"); addObject(runtime, 23, null, "pkcs7-envelopedData","1.2.840.113549.1.7.3"); addObject(runtime, 24, null, "pkcs7-signedAndEnvelopedData","1.2.840.113549.1.7.4"); addObject(runtime, 25, null, "pkcs7-digestData","1.2.840.113549.1.7.5"); addObject(runtime, 26, null, "pkcs7-encryptedData","1.2.840.113549.1.7.6"); addObject(runtime, 27, null, "pkcs3","1.2.840.113549.1.3"); addObject(runtime, 28, null, "dhKeyAgreement","1.2.840.113549.1.3.1"); addObject(runtime, 29, "DES-ECB", "des-ecb","1.3.14.3.2.6"); addObject(runtime, 30, "DES-CFB", "des-cfb","1.3.14.3.2.9"); addObject(runtime, 31, "DES-CBC", "des-cbc","1.3.14.3.2.7"); addObject(runtime, 32, "DES-EDE", "des-ede","1.3.14.3.2.17"); addObject(runtime, 33, "DES-EDE3", "des-ede3",null); addObject(runtime, 34, "IDEA-CBC", "idea-cbc","1.3.6.1.4.1.188.7.1.1.2"); addObject(runtime, 35, "IDEA-CFB", "idea-cfb",null); addObject(runtime, 36, "IDEA-ECB", "idea-ecb",null); addObject(runtime, 37, "RC2-CBC", "rc2-cbc","1.2.840.113549.3.2"); addObject(runtime, 38, "RC2-ECB", "rc2-ecb",null); addObject(runtime, 39, "RC2-CFB", "rc2-cfb",null); addObject(runtime, 40, "RC2-OFB", "rc2-ofb",null); addObject(runtime, 41, "SHA", "sha","1.3.14.3.2.18"); addObject(runtime, 42, "RSA-SHA", "shaWithRSAEncryption","1.3.14.3.2.15"); addObject(runtime, 43, "DES-EDE-CBC", "des-ede-cbc",null); addObject(runtime, 44, "DES-EDE3-CBC", "des-ede3-cbc","1.2.840.113549.3.7"); addObject(runtime, 45, "DES-OFB", "des-ofb","1.3.14.3.2.8"); addObject(runtime, 46, "IDEA-OFB", "idea-ofb",null); addObject(runtime, 47, null, "pkcs9","1.2.840.113549.1.9"); addObject(runtime, 48, null, "emailAddress","1.2.840.113549.1.9.1"); addObject(runtime, 49, null, "unstructuredName","1.2.840.113549.1.9.2"); addObject(runtime, 50, null, "contentType","1.2.840.113549.1.9.3"); addObject(runtime, 51, null, "messageDigest","1.2.840.113549.1.9.4"); addObject(runtime, 52, null, "signingTime","1.2.840.113549.1.9.5"); addObject(runtime, 53, null, "countersignature","1.2.840.113549.1.9.6"); addObject(runtime, 54, null, "challengePassword","1.2.840.113549.1.9.7"); addObject(runtime, 55, null, "unstructuredAddress","1.2.840.113549.1.9.8"); addObject(runtime, 56, null, "extendedCertificateAttributes","1.2.840.113549.1.9.9"); addObject(runtime, 57, "Netscape", "Netscape Communications Corp.","2.16.840.1.113730"); addObject(runtime, 58, "nsCertExt", "Netscape Certificate Extension","2.16.840.1.113730.1"); addObject(runtime, 59, "nsDataType", "Netscape Data Type","2.16.840.1.113730.2"); addObject(runtime, 60, "DES-EDE-CFB", "des-ede-cfb",null); addObject(runtime, 61, "DES-EDE3-CFB", "des-ede3-cfb",null); addObject(runtime, 62, "DES-EDE-OFB", "des-ede-ofb",null); addObject(runtime, 63, "DES-EDE3-OFB", "des-ede3-ofb",null); addObject(runtime, 64, "SHA1", "sha1","1.3.14.3.2.26"); addObject(runtime, 65, "RSA-SHA1", "sha1WithRSAEncryption","1.2.840.113549.1.1.5"); addObject(runtime, 66, "DSA-SHA", "dsaWithSHA","1.3.14.3.2.13"); addObject(runtime, 67, "DSA-old", "dsaEncryption-old","1.3.14.3.2.12"); addObject(runtime, 68, "PBE-SHA1-RC2-64", "pbeWithSHA1AndRC2-CBC","1.2.840.113549.1.5.11"); addObject(runtime, 69, null, "PBKDF2","1.2.840.113549.1.5.12"); addObject(runtime, 70, "DSA-SHA1-old", "dsaWithSHA1-old","1.3.14.3.2.27"); addObject(runtime, 71, "nsCertType", "Netscape Cert Type","2.16.840.1.113730.1.1"); addObject(runtime, 72, "nsBaseUrl", "Netscape Base Url","2.16.840.1.113730.1.2"); addObject(runtime, 73, "nsRevocationUrl", "Netscape Revocation Url","2.16.840.1.113730.1.3"); addObject(runtime, 74, "nsCaRevocationUrl", "Netscape CA Revocation Url","2.16.840.1.113730.1.4"); addObject(runtime, 75, "nsRenewalUrl", "Netscape Renewal Url","2.16.840.1.113730.1.7"); addObject(runtime, 76, "nsCaPolicyUrl", "Netscape CA Policy Url","2.16.840.1.113730.1.8"); addObject(runtime, 77, "nsSslServerName", "Netscape SSL Server Name","2.16.840.1.113730.1.12"); addObject(runtime, 78, "nsComment", "Netscape Comment","2.16.840.1.113730.1.13"); addObject(runtime, 79, "nsCertSequence", "Netscape Certificate Sequence","2.16.840.1.113730.2.5"); addObject(runtime, 80, "DESX-CBC", "desx-cbc",null); addObject(runtime, 81, "id-ce", null,"2.5.29"); addObject(runtime, 82, "subjectKeyIdentifier", "X509v3 Subject Key Identifier","2.5.29.14"); addObject(runtime, 83, "keyUsage", "X509v3 Key Usage","2.5.29.15"); addObject(runtime, 84, "privateKeyUsagePeriod", "X509v3 Private Key Usage Period","2.5.29.16"); addObject(runtime, 85, "subjectAltName", "X509v3 Subject Alternative Name","2.5.29.17"); addObject(runtime, 86, "issuerAltName", "X509v3 Issuer Alternative Name","2.5.29.18"); addObject(runtime, 87, "basicConstraints", "X509v3 Basic Constraints","2.5.29.19"); addObject(runtime, 88, "crlNumber", "X509v3 CRL Number","2.5.29.20"); addObject(runtime, 89, "certificatePolicies", "X509v3 Certificate Policies","2.5.29.32"); addObject(runtime, 90, "authorityKeyIdentifier", "X509v3 Authority Key Identifier","2.5.29.35"); addObject(runtime, 91, "BF-CBC", "bf-cbc","1.3.6.1.4.1.3029.1.2"); addObject(runtime, 92, "BF-ECB", "bf-ecb",null); addObject(runtime, 93, "BF-CFB", "bf-cfb",null); addObject(runtime, 94, "BF-OFB", "bf-ofb",null); addObject(runtime, 95, "MDC2", "mdc2","2.5.8.3.101"); addObject(runtime, 96, "RSA-MDC2", "mdc2withRSA","2.5.8.3.100"); addObject(runtime, 97, "RC4-40", "rc4-40",null); addObject(runtime, 98, "RC2-40-CBC", "rc2-40-cbc",null); addObject(runtime, 99, "G", "givenName","2.5.4.42"); addObject(runtime, 100, "S", "surname","2.5.4.4"); addObject(runtime, 101, "I", "initials","2.5.4.43"); addObject(runtime, 102, "UID", "uniqueIdentifier","2.5.4.45"); // BC prefers UID to map to userId ?! addObject(runtime, 103, "crlDistributionPoints", "X509v3 CRL Distribution Points","2.5.29.31"); addObject(runtime, 104, "RSA-NP-MD5", "md5WithRSA","1.3.14.3.2.3"); addObject(runtime, 105, null, "serialNumber","2.5.4.5"); addObject(runtime, 106, "T", "title","2.5.4.12"); addObject(runtime, 107, "D", "description","2.5.4.13"); addObject(runtime, 108, "CAST5-CBC", "cast5-cbc","1.2.840.113533.7.66.10"); addObject(runtime, 109, "CAST5-ECB", "cast5-ecb",null); addObject(runtime, 110, "CAST5-CFB", "cast5-cfb",null); addObject(runtime, 111, "CAST5-OFB", "cast5-ofb",null); addObject(runtime, 112, null, "pbeWithMD5AndCast5CBC","1.2.840.113533.7.66.12"); addObject(runtime, 113, "DSA-SHA1", "dsaWithSHA1","1.2.840.10040.4.3"); addObject(runtime, 114, "MD5-SHA1", "md5-sha1",null); addObject(runtime, 115, "RSA-SHA1-2", "sha1WithRSA","1.3.14.3.2.29"); addObject(runtime, 116, "DSA", "dsaEncryption","1.2.840.10040.4.1"); addObject(runtime, 117, "RIPEMD160", "ripemd160","1.3.36.3.2.1"); addObject(runtime, 118, "RSA-RIPEMD160", "ripemd160WithRSA","1.3.36.3.3.1.2"); addObject(runtime, 119, "RC5-CBC", "rc5-cbc","1.2.840.113549.3.8"); addObject(runtime, 120, "RC5-ECB", "rc5-ecb",null); addObject(runtime, 121, "RC5-CFB", "rc5-cfb",null); addObject(runtime, 122, "RC5-OFB", "rc5-ofb",null); addObject(runtime, 123, "RLE", "run length compression","1.1.1.1.666.1"); addObject(runtime, 124, "ZLIB", "zlib compression","1.1.1.1.666.2"); addObject(runtime, 125, "extendedKeyUsage", "X509v3 Extended Key Usage","2.5.29.37"); addObject(runtime, 126, "PKIX", null,"1.3.6.1.5.5.7"); addObject(runtime, 127, "id-kp", null,"1.3.6.1.5.5.7.3"); addObject(runtime, 128, "serverAuth", "TLS Web Server Authentication","1.3.6.1.5.5.7.3.1"); addObject(runtime, 129, "clientAuth", "TLS Web Client Authentication","1.3.6.1.5.5.7.3.2"); addObject(runtime, 130, "codeSigning", "Code Signing","1.3.6.1.5.5.7.3.3"); addObject(runtime, 131, "emailProtection", "E-mail Protection","1.3.6.1.5.5.7.3.4"); addObject(runtime, 132, "timeStamping", "Time Stamping","1.3.6.1.5.5.7.3.8"); addObject(runtime, 133, "msCodeInd", "Microsoft Individual Code Signing","1.3.6.1.4.1.311.2.1.21"); addObject(runtime, 134, "msCodeCom", "Microsoft Commercial Code Signing","1.3.6.1.4.1.311.2.1.22"); addObject(runtime, 135, "msCTLSign", "Microsoft Trust List Signing","1.3.6.1.4.1.311.10.3.1"); addObject(runtime, 136, "msSGC", "Microsoft Server Gated Crypto","1.3.6.1.4.1.311.10.3.3"); addObject(runtime, 137, "msEFS", "Microsoft Encrypted File System","1.3.6.1.4.1.311.10.3.4"); addObject(runtime, 138, "nsSGC", "Netscape Server Gated Crypto","2.16.840.1.113730.4.1"); addObject(runtime, 139, "deltaCRL", "X509v3 Delta CRL Indicator","2.5.29.27"); addObject(runtime, 140, "CRLReason", "CRL Reason Code","2.5.29.21"); addObject(runtime, 141, "invalidityDate", "Invalidity Date","2.5.29.24"); addObject(runtime, 142, "SXNetID", "Strong Extranet ID","1.3.101.1.4.1"); addObject(runtime, 143, "PBE-SHA1-RC4-128", "pbeWithSHA1And128BitRC4","1.2.840.113549.1.12.1.1"); addObject(runtime, 144, "PBE-SHA1-RC4-40", "pbeWithSHA1And40BitRC4","1.2.840.113549.1.12.1.2"); addObject(runtime, 145, "PBE-SHA1-3DES", "pbeWithSHA1And3-KeyTripleDES-CBC","1.2.840.113549.1.12.1.3"); addObject(runtime, 146, "PBE-SHA1-2DES", "pbeWithSHA1And2-KeyTripleDES-CBC","1.2.840.113549.1.12.1.4"); addObject(runtime, 147, "PBE-SHA1-RC2-128", "pbeWithSHA1And128BitRC2-CBC","1.2.840.113549.1.12.1.5"); addObject(runtime, 148, "PBE-SHA1-RC2-40", "pbeWithSHA1And40BitRC2-CBC","1.2.840.113549.1.12.1.6"); addObject(runtime, 149, null, "keyBag","1.2.840.113549.1.12.10.1.1"); addObject(runtime, 150, null, "pkcs8ShroudedKeyBag","1.2.840.113549.1.12.10.1.2"); addObject(runtime, 151, null, "certBag","1.2.840.113549.1.12.10.1.3"); addObject(runtime, 152, null, "crlBag","1.2.840.113549.1.12.10.1.4"); addObject(runtime, 153, null, "secretBag","1.2.840.113549.1.12.10.1.5"); addObject(runtime, 154, null, "safeContentsBag","1.2.840.113549.1.12.10.1.6"); addObject(runtime, 155, null, "PBES2","1.2.840.113549.1.5.13"); addObject(runtime, 156, null, "PBMAC1","1.2.840.113549.1.5.14"); addObject(runtime, 157, null, "hmacWithSHA1","1.2.840.113549.2.7"); addObject(runtime, 158, "id-qt-cps", "Policy Qualifier CPS","1.3.6.1.5.5.7.2.1"); addObject(runtime, 159, "id-qt-unotice", "Policy Qualifier User Notice","1.3.6.1.5.5.7.2.2"); addObject(runtime, 160, "RC2-64-CBC", "rc2-64-cbc",null); addObject(runtime, 161, "SMIME-CAPS", "S/MIME Capabilities","1.2.840.113549.1.9.15"); addObject(runtime, 162, "PBE-MD2-RC2-64", "pbeWithMD2AndRC2-CBC","1.2.840.113549.1.5.4"); addObject(runtime, 163, "PBE-MD5-RC2-64", "pbeWithMD5AndRC2-CBC","1.2.840.113549.1.5.6"); addObject(runtime, 164, "PBE-SHA1-DES", "pbeWithSHA1AndDES-CBC","1.2.840.113549.1.5.10"); addObject(runtime, 165, "msExtReq", "Microsoft Extension Request","1.3.6.1.4.1.311.2.1.14"); addObject(runtime, 166, "extReq", "Extension Request","1.2.840.113549.1.9.14"); addObject(runtime, 167, "name", "name","2.5.4.41"); addObject(runtime, 168, "dnQualifier", "dnQualifier","2.5.4.46"); addObject(runtime, 169, "id-pe", null,"1.3.6.1.5.5.7.1"); addObject(runtime, 170, "id-ad", null,"1.3.6.1.5.5.7.48"); addObject(runtime, 171, "authorityInfoAccess", "Authority Information Access","1.3.6.1.5.5.7.1.1"); addObject(runtime, 172, "OCSP", "OCSP","1.3.6.1.5.5.7.48.1"); addObject(runtime, 173, "caIssuers", "CA Issuers","1.3.6.1.5.5.7.48.2"); addObject(runtime, 174, "OCSPSigning", "OCSP Signing","1.3.6.1.5.5.7.3.9"); addObject(runtime, 175, "AES-128-EBC", "aes-128-ebc","2.16.840.1.101.3.4.1.1"); addObject(runtime, 176, "AES-128-CBC", "aes-128-cbc","2.16.840.1.101.3.4.1.2"); addObject(runtime, 177, "AES-128-OFB", "aes-128-ofb","2.16.840.1.101.3.4.1.3"); addObject(runtime, 178, "AES-128-CFB", "aes-128-cfb","2.16.840.1.101.3.4.1.4"); addObject(runtime, 179, "AES-192-EBC", "aes-192-ebc","2.16.840.1.101.3.4.1.21"); addObject(runtime, 180, "AES-192-CBC", "aes-192-cbc","2.16.840.1.101.3.4.1.22"); addObject(runtime, 181, "AES-192-OFB", "aes-192-ofb","2.16.840.1.101.3.4.1.23"); addObject(runtime, 182, "AES-192-CFB", "aes-192-cfb","2.16.840.1.101.3.4.1.24"); addObject(runtime, 183, "AES-256-EBC", "aes-256-ebc","2.16.840.1.101.3.4.1.41"); addObject(runtime, 184, "AES-256-CBC", "aes-256-cbc","2.16.840.1.101.3.4.1.42"); addObject(runtime, 185, "AES-256-OFB", "aes-256-ofb","2.16.840.1.101.3.4.1.43"); addObject(runtime, 186, "AES-256-CFB", "aes-256-cfb","2.16.840.1.101.3.4.1.44"); addObject(runtime, 672, "SHA256", "sha256", "2.16.840.1.101.3.4.2.1"); addObject(runtime, 660, "street", "streetAddress", "2.5.4.9"); addObject(runtime, 391, "DC", "domainComponent", "0.9.2342.19200300.100.1.25"); //addObject(runtime, 509, null, "generationQualifier", "2.5.4.44"); //addObject(runtime, 510, null, "pseudonym", "2.5.4.65"); //addObject(runtime, 661, null, "postalCode", "2.5.4.17"); //addObject(runtime, 861, null, "postalAddress", "2.5.4.16"); // NOTE: left-overs from BC's org.bouncycastle.asn1.x509.X509Name /* DefaultLookUp.put("uid", UID); DefaultLookUp.put("dn", DN_QUALIFIER); DefaultLookUp.put("nameofbirth", NAME_AT_BIRTH); DefaultLookUp.put("placeofbirth", PLACE_OF_BIRTH); DefaultLookUp.put("dateofbirth", DATE_OF_BIRTH);gen DefaultLookUp.put("countryofcitizenship", COUNTRY_OF_CITIZENSHIP); DefaultLookUp.put("countryofresidence", COUNTRY_OF_RESIDENCE); DefaultLookUp.put("gender", GENDER); DefaultLookUp.put("businesscategory", BUSINESS_CATEGORY); DefaultLookUp.put("telephonenumber", TELEPHONE_NUMBER); */ } private static void addObject(final Ruby runtime, final int nid, final String sn, final String ln, final String oid) { if ( oid != null && ( sn != null || ln != null ) ) { ASN1ObjectIdentifier objectId = new ASN1ObjectIdentifier(oid); if ( sn != null ) { symToOid(runtime).put(sn.toLowerCase(), objectId); } if ( ln != null ) { symToOid(runtime).put(ln.toLowerCase(), objectId); } oidToSym(runtime).put(objectId, sn == null ? ln : sn); oidToNid(runtime).put(objectId, nid); nidToOid(runtime).put(nid, objectId); nidToSn(runtime).put(nid, sn); nidToLn(runtime).put(nid, ln); } } private static Map<String, ASN1ObjectIdentifier> symToOid(final Ruby runtime) { Map<String, ASN1ObjectIdentifier> map = SYM_TO_OID.get(runtime); if ( map == null ) { synchronized(ASN1.class) { map = SYM_TO_OID.get(runtime); if ( map == null ) { initMaps(runtime); map = SYM_TO_OID.get(runtime); } } } return map; } private static Map<ASN1ObjectIdentifier, String> oidToSym(final Ruby runtime) { Map<ASN1ObjectIdentifier, String> map = OID_TO_SYM.get(runtime); if ( map == null ) { synchronized(ASN1.class) { map = OID_TO_SYM.get(runtime); if ( map == null ) { initMaps(runtime); map = OID_TO_SYM.get(runtime); } } } return map; } private static Map<Integer, ASN1ObjectIdentifier> nidToOid(final Ruby runtime) { Map<Integer, ASN1ObjectIdentifier> map = NID_TO_OID.get(runtime); if ( map == null ) { synchronized(ASN1.class) { map = NID_TO_OID.get(runtime); if ( map == null ) { initMaps(runtime); map = NID_TO_OID.get(runtime); } } } return map; } private static Map<ASN1ObjectIdentifier, Integer> oidToNid(final Ruby runtime) { Map<ASN1ObjectIdentifier, Integer> map = OID_TO_NID.get(runtime); if ( map == null ) { synchronized(ASN1.class) { map = OID_TO_NID.get(runtime); if ( map == null ) { initMaps(runtime); map = OID_TO_NID.get(runtime); } } } return map; } private static Map<Integer, String> nidToSn(final Ruby runtime) { Map<Integer, String> map = NID_TO_SN.get(runtime); if ( map == null ) { synchronized(ASN1.class) { map = NID_TO_SN.get(runtime); if ( map == null ) { initMaps(runtime); map = NID_TO_SN.get(runtime); } } } return map; } private static Map<Integer, String> nidToLn(final Ruby runtime) { Map<Integer, String> map = NID_TO_LN.get(runtime); if ( map == null ) { synchronized(ASN1.class) { map = NID_TO_LN.get(runtime); if ( map == null ) { initMaps(runtime); map = NID_TO_LN.get(runtime); } } } return map; } static String ln2oid(final Ruby runtime, final String ln) { Map<String, ASN1ObjectIdentifier> map = symToOid(runtime); final ASN1ObjectIdentifier val = map.get(ln); if ( val == null ) { throw new NullPointerException("oid not found for ln = '" + ln + "' (" + runtime + ")"); } return val.getId(); } static Integer oid2nid(final Ruby runtime, final ASN1ObjectIdentifier oid) { return oidToNid(runtime).get(oid); } static String o2a(final Ruby runtime, final ASN1ObjectIdentifier oid) { return o2a(runtime, oid, false); } static String o2a(final Ruby runtime, final ASN1ObjectIdentifier oid, final boolean silent) { Integer nid = oidToNid(runtime).get(oid); if ( nid != null ) { final String name = nid2ln(runtime, nid, false); return name == null ? nid2sn(runtime, nid, false) : name; } nid = ASN1Registry.oid2nid(oid); if ( nid == null ) { if ( silent ) return null; throw new NullPointerException("nid not found for oid = '" + oid + "' (" + runtime + ")"); } final String name = nid2ln(runtime, nid, false); if ( name != null ) return name; return nid2sn(runtime, nid, true); } static String oid2name(final Ruby runtime, final ASN1ObjectIdentifier oid, final boolean silent) { Integer nid = oidToNid(runtime).get(oid); if ( nid != null ) { final String name = nid2sn(runtime, nid, false); return name == null ? nid2ln(runtime, nid, false) : name; } nid = ASN1Registry.oid2nid(oid); if ( nid == null ) { if ( silent ) return null; throw new NullPointerException("nid not found for oid = '" + oid + "' (" + runtime + ")"); } final String name = nid2sn(runtime, nid, false); if ( name != null ) return name; return nid2ln(runtime, nid, true); /* if ( nid == null ) nid = ASN1Registry.oid2nid(oid); if ( nid == null ) { if ( silent ) return null; throw new NullPointerException("nid not found for oid = '" + oid + "' (" + runtime + ")"); } final String name = nid2sn(runtime, nid, true); if ( name != null ) return name; return nid2ln(runtime, nid, true); */ } static String oid2name(final Ruby runtime, final String oid) { return oid2name(runtime, new ASN1ObjectIdentifier(oid), false); } static String nid2sn(final Ruby runtime, final Integer nid) { return nid2sn(runtime, nid, true); } private static String nid2sn(final Ruby runtime, final Integer nid, boolean fallback) { final String ln = nidToSn(runtime).get(nid); if ( ln == null && fallback ) return ASN1Registry.nid2sn(nid); return ln; } static String nid2ln(final Ruby runtime, final Integer nid) { return nid2ln(runtime, nid, true); } private static String nid2ln(final Ruby runtime, final Integer nid, boolean fallback) { final String ln = nidToLn(runtime).get(nid); if ( ln == null && fallback ) return ASN1Registry.nid2ln(nid); return ln; } static String oid2Sym(final Ruby runtime, final ASN1ObjectIdentifier oid) { return oid2Sym(runtime, oid, false); } static String oid2Sym(final Ruby runtime, final ASN1ObjectIdentifier oid, final boolean fallback) { final String sym = getSymLookup(runtime).get(oid); return ( sym == null && fallback ) ? ASN1Registry.oid2sym(oid) : sym; } static ASN1ObjectIdentifier sym2Oid(final Ruby runtime, final String name) { return getOIDLookup(runtime).get(name); } private static Map<String, ASN1ObjectIdentifier> getOIDLookup(final Ruby runtime) { return symToOid(runtime); } private static Map<ASN1ObjectIdentifier, String> getSymLookup(final Ruby runtime) { return oidToSym(runtime); } private final static Object[][] ASN1_INFO = { { "EOC", null, "EndOfContent" }, // OpenSSL::ASN1::EOC (0) { "BOOLEAN", org.bouncycastle.asn1.DERBoolean.class, "Boolean" }, { "INTEGER", org.bouncycastle.asn1.DERInteger.class, "Integer" }, { "BIT_STRING", org.bouncycastle.asn1.DERBitString.class, "BitString" }, { "OCTET_STRING", org.bouncycastle.asn1.DEROctetString.class, "OctetString" }, { "NULL", org.bouncycastle.asn1.DERNull.class, "Null" }, // OpenSSL::ASN1::OBJECT (6) : { "OBJECT", org.bouncycastle.asn1.DERObjectIdentifier.class, "ObjectId" }, { "OBJECT_DESCRIPTOR", null, null }, { "EXTERNAL", null, null }, { "REAL", null, null }, // OpenSSL::ASN1::ENUMERATED (10) : { "ENUMERATED", org.bouncycastle.asn1.DEREnumerated.class, "Enumerated" }, { "EMBEDDED_PDV", null, null }, // OpenSSL::ASN1::UTF8STRING (12) : { "UTF8STRING", org.bouncycastle.asn1.DERUTF8String.class, "UTF8String" }, { "RELATIVE_OID", null, null }, { "[UNIVERSAL 14]", null, null }, { "[UNIVERSAL 15]", null, null }, // OpenSSL::ASN1::SEQUENCE (16) : // NOTE: org.bouncycastle.asn1.DERSequence does not have a getInstance //{ "SEQUENCE", org.bouncycastle.asn1.ASN1Sequence.class, "Sequence" }, { "SEQUENCE", org.bouncycastle.asn1.DERSequence.class, "Sequence" }, // OpenSSL::ASN1::SET (17) : // NOTE: org.bouncycastle.asn1.DERSet does not have a getInstance //{ "SET", org.bouncycastle.asn1.ASN1Set.class, "Set" }, { "SET", org.bouncycastle.asn1.DERSet.class, "Set" }, { "NUMERICSTRING", org.bouncycastle.asn1.DERNumericString.class, "NumericString" }, { "PRINTABLESTRING", org.bouncycastle.asn1.DERPrintableString.class, "PrintableString" }, { "T61STRING", org.bouncycastle.asn1.DERT61String.class, "T61String" }, { "VIDEOTEXSTRING", null, "VideotexString" }, { "IA5STRING", org.bouncycastle.asn1.DERIA5String.class, "IA5String" }, { "UTCTIME", org.bouncycastle.asn1.DERUTCTime.class, "UTCTime" }, { "GENERALIZEDTIME", org.bouncycastle.asn1.DERGeneralizedTime.class, "GeneralizedTime" }, { "GRAPHICSTRING", null, "GraphicString" }, { "ISO64STRING", null, "ISO64String" }, { "GENERALSTRING", org.bouncycastle.asn1.DERGeneralString.class, "GeneralString" }, // OpenSSL::ASN1::UNIVERSALSTRING (28) : { "UNIVERSALSTRING", org.bouncycastle.asn1.DERUniversalString.class, "UniversalString" }, { "CHARACTER_STRING", null, null }, // OpenSSL::ASN1::BMPSTRING (30) : { "BMPSTRING", org.bouncycastle.asn1.DERBMPString.class, "BMPString" }}; private final static Map<Class<?>, Integer> JCLASS_TO_ID = new HashMap<Class<?>, Integer>(24, 1); private final static Map<String, Integer> RCLASS_TO_ID = new HashMap<String, Integer>(28, 1); static { for ( int i = 0; i < ASN1_INFO.length; i++ ) { final Object[] info = ASN1_INFO[i]; if ( info[1] != null ) { JCLASS_TO_ID.put((Class) info[1], Integer.valueOf(i)); } if ( info[2] != null ) { RCLASS_TO_ID.put((String) info[2], Integer.valueOf(i)); } } } private final static int EOC = 0; // OpenSSL::ASN1::EOC (0) //private final static int BOOLEAN = 1; // OpenSSL::ASN1::BOOLEAN (1) //private final static int INTEGER = 2; // OpenSSL::ASN1::INTEGER (2) private final static int BIT_STRING = 3; // OpenSSL::ASN1::BIT_STRING (3) private final static int OCTET_STRING = 4; // OpenSSL::ASN1::OCTET_STRING (4) //private final static int NULL = 5; // OpenSSL::ASN1::NULL (5) //private final static int OBJECT = 6; // OpenSSL::ASN1::OBJECT (6) //private final static int ENUMARATED = 10; // OpenSSL::ASN1::ENUMERATED (10) //private final static int UTFSTRING = 12; // OpenSSL::ASN1::UTF8STRING (12) private final static int SEQUENCE = 16; // OpenSSL::ASN1::SEQUENCE (16) private final static int SET = 17; // OpenSSL::ASN1::SET (17) //private final static int NUMERICSTRING = 18; // OpenSSL::ASN1::NUMERICSTRING (18) // OpenSSL::ASN1::PRINTABLESTRING (19) // OpenSSL::ASN1::T61STRING (20) // OpenSSL::ASN1::VIDEOTEXSTRING (21) // OpenSSL::ASN1::IA5STRING (22) // OpenSSL::ASN1::UTCTIME (23) // OpenSSL::ASN1::GENERALIZEDTIME (24) // OpenSSL::ASN1::GRAPHICSTRING (25) // OpenSSL::ASN1::ISO64STRING (26) // OpenSSL::ASN1::GENERALSTRING (27) // OpenSSL::ASN1::UNIVERSALSTRING (28) // OpenSSL::ASN1::BMPSTRING (30) private static Integer typeId(Class<?> type) { Integer id = null; while ( type != Object.class && id == null ) { id = JCLASS_TO_ID.get(type); if ( id == null ) type = type.getSuperclass(); } return id; //return v == null ? -1 : v.intValue(); } static Integer typeId(final ASN1Encodable obj) { return typeId( obj.getClass() ); } private static Integer typeId(final RubyClass metaClass) { final String name = metaClass.getRealClass().getBaseName(); final Integer id = RCLASS_TO_ID.get(name); return id == null ? null : id; } @SuppressWarnings("unchecked") static Class<? extends ASN1Encodable> typeClass(final RubyClass metaClass) { final Integer tag = typeId( metaClass ); if ( tag == null ) return null; return (Class<? extends ASN1Encodable>) ASN1_INFO[tag][1]; } @SuppressWarnings("unchecked") static Class<? extends ASN1Encodable> typeClass(final int typeId) { return (Class<? extends ASN1Encodable>) ASN1_INFO[typeId][1]; } static ASN1Encodable typeInstance(Class<? extends ASN1Encodable> type, Object value) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Method getInstance = null; try { getInstance = type.getMethod("getInstance", Object.class); } catch (NoSuchMethodException e) { Class superType = type.getSuperclass(); try { if ( superType != Object.class ) { getInstance = type.getSuperclass().getMethod("getInstance", Object.class); } } catch (NoSuchMethodException e2) { } if ( getInstance == null ) throw e; } return (ASN1Encodable) getInstance.invoke(null, value); } public static void createASN1(final Ruby runtime, final RubyModule OpenSSL) { final RubyModule ASN1 = OpenSSL.defineModuleUnder("ASN1"); final RubyClass OpenSSLError = OpenSSL.getClass("OpenSSLError"); ASN1.defineClassUnder("ASN1Error", OpenSSLError, OpenSSLError.getAllocator()); ASN1.defineAnnotatedMethods(ASN1.class); final RubyArray UNIVERSAL_TAG_NAME = runtime.newArray(ASN1_INFO.length); for ( int i = 0; i < ASN1_INFO.length; i++ ) { final String name = (String) ASN1_INFO[i][0]; if ( name.charAt(0) != '[' ) { UNIVERSAL_TAG_NAME.append( runtime.newString(name) ); ASN1.setConstant( name, runtime.newFixnum(i) ); } else { UNIVERSAL_TAG_NAME.append( runtime.getNil() ); } } ASN1.setConstant("UNIVERSAL_TAG_NAME", UNIVERSAL_TAG_NAME); final ThreadContext context = runtime.getCurrentContext(); final ObjectAllocator asn1DataAllocator = ASN1Data.ALLOCATOR; RubyClass _ASN1Data = ASN1.defineClassUnder("ASN1Data", runtime.getObject(), asn1DataAllocator); _ASN1Data.addReadWriteAttribute(context, "value"); _ASN1Data.addReadWriteAttribute(context, "tag"); _ASN1Data.addReadWriteAttribute(context, "tag_class"); _ASN1Data.defineAnnotatedMethods(ASN1Data.class); final ObjectAllocator primitiveAllocator = Primitive.ALLOCATOR; RubyClass Primitive = ASN1.defineClassUnder("Primitive", _ASN1Data, primitiveAllocator); Primitive.addReadWriteAttribute(context, "tagging"); Primitive.addReadAttribute(context, "infinite_length"); Primitive.defineAnnotatedMethods(Primitive.class); final ObjectAllocator constructiveAllocator = Constructive.ALLOCATOR; RubyClass Constructive = ASN1.defineClassUnder("Constructive", _ASN1Data, constructiveAllocator); Constructive.includeModule( runtime.getModule("Enumerable") ); Constructive.addReadWriteAttribute(context, "tagging"); Constructive.addReadWriteAttribute(context, "infinite_length"); Constructive.defineAnnotatedMethods(Constructive.class); ASN1.defineClassUnder("Boolean", Primitive, primitiveAllocator); // OpenSSL::ASN1::Boolean <=> value is a Boolean ASN1.defineClassUnder("Integer", Primitive, primitiveAllocator); // OpenSSL::ASN1::Integer <=> value is a Number ASN1.defineClassUnder("Null", Primitive, primitiveAllocator); // OpenSSL::ASN1::Null <=> value is always nil ASN1.defineClassUnder("Object", Primitive, primitiveAllocator); // OpenSSL::ASN1::Object <=> value is a String ASN1.defineClassUnder("Enumerated", Primitive, primitiveAllocator); // OpenSSL::ASN1::Enumerated <=> value is a Number RubyClass BitString = ASN1.defineClassUnder("BitString", Primitive, primitiveAllocator); BitString.addReadWriteAttribute(context, "unused_bits"); ASN1.defineClassUnder("OctetString", Primitive, primitiveAllocator); ASN1.defineClassUnder("UTF8String", Primitive, primitiveAllocator); ASN1.defineClassUnder("NumericString", Primitive, primitiveAllocator); ASN1.defineClassUnder("PrintableString", Primitive, primitiveAllocator); ASN1.defineClassUnder("T61String", Primitive, primitiveAllocator); ASN1.defineClassUnder("VideotexString", Primitive, primitiveAllocator); ASN1.defineClassUnder("IA5String", Primitive, primitiveAllocator); ASN1.defineClassUnder("GraphicString", Primitive, primitiveAllocator); ASN1.defineClassUnder("ISO64String", Primitive, primitiveAllocator); ASN1.defineClassUnder("GeneralString", Primitive, primitiveAllocator); ASN1.defineClassUnder("UniversalString", Primitive, primitiveAllocator); ASN1.defineClassUnder("BMPString", Primitive, primitiveAllocator); ASN1.defineClassUnder("UTCTime", Primitive, primitiveAllocator); // OpenSSL::ASN1::UTCTime <=> value is a Time ASN1.defineClassUnder("GeneralizedTime", Primitive, primitiveAllocator); // OpenSSL::ASN1::GeneralizedTime <=> value is a Time ASN1.defineClassUnder("EndOfContent", Primitive, primitiveAllocator); // OpenSSL::ASN1::EndOfContent <=> value is always nil RubyClass ObjectId = ASN1.defineClassUnder("ObjectId", Primitive, primitiveAllocator); ObjectId.defineAnnotatedMethods(ObjectId.class); ASN1.defineClassUnder("Sequence", Constructive, Constructive.getAllocator()); ASN1.defineClassUnder("Set", Constructive, Constructive.getAllocator()); } static ASN1ObjectIdentifier getObjectID(final Ruby runtime, final String nameOrOid) throws IllegalArgumentException { final String name = nameOrOid.toLowerCase(); ASN1ObjectIdentifier objectId = getOIDLookup(runtime).get( name ); if ( objectId != null ) return objectId; final String objectIdStr = ASN1Registry.getOIDLookup().get( name ); if ( objectIdStr != null ) return toObjectID(objectIdStr, false); return new ASN1ObjectIdentifier( nameOrOid ); } static ASN1ObjectIdentifier toObjectID(final String oid, final boolean silent) throws IllegalArgumentException { try { return new ASN1ObjectIdentifier(oid); } catch (IllegalArgumentException e) { if ( silent ) return null; throw e; } } @JRubyMethod(name="Boolean", module=true, rest=true) public static IRubyObject fact_Boolean(IRubyObject self, IRubyObject[] args) { return callClassNew(self, "Boolean", args); } @JRubyMethod(name="Integer", module=true, rest=true) public static IRubyObject fact_Integer(IRubyObject self, IRubyObject[] args) { return callClassNew(self, "Integer", args); } @JRubyMethod(name="Enumerated", module=true, rest=true) public static IRubyObject fact_Enumerated(IRubyObject self, IRubyObject[] args) { return callClassNew(self, "Enumerated", args); } @JRubyMethod(name="BitString", module=true, rest=true) public static IRubyObject fact_BitString(IRubyObject self, IRubyObject[] args) { return callClassNew(self, "BitString", args); } @JRubyMethod(name="OctetString", module=true, rest=true) public static IRubyObject fact_OctetString(IRubyObject self, IRubyObject[] args) { return callClassNew(self, "OctetString", args); } @JRubyMethod(name="UTF8String", module=true, rest=true) public static IRubyObject fact_UTF8String(IRubyObject self, IRubyObject[] args) { return callClassNew(self, "UTF8String", args); } @JRubyMethod(name="NumericString", module=true, rest=true) public static IRubyObject fact_NumericString(IRubyObject self, IRubyObject[] args) { return callClassNew(self, "NumericString", args); } @JRubyMethod(name="PrintableString", module=true, rest=true) public static IRubyObject fact_PrintableString(IRubyObject self, IRubyObject[] args) { return callClassNew(self, "PrintableString", args); } @JRubyMethod(name="T61String", module=true, rest=true) public static IRubyObject fact_T61String(IRubyObject self, IRubyObject[] args) { return callClassNew(self, "T61String", args); } @JRubyMethod(name="VideotexString", module=true, rest=true) public static IRubyObject fact_VideotexString(IRubyObject recv, IRubyObject[] args) { return ((RubyModule)recv).getClass("VideotexString").callMethod(recv.getRuntime().getCurrentContext(),"new",args); } @JRubyMethod(name="IA5String", module=true, rest=true) public static IRubyObject fact_IA5String(IRubyObject recv, IRubyObject[] args) { return ((RubyModule)recv).getClass("IA5String").callMethod(recv.getRuntime().getCurrentContext(),"new",args); } @JRubyMethod(name="GraphicString", module=true, rest=true) public static IRubyObject fact_GraphicString(IRubyObject recv, IRubyObject[] args) { return ((RubyModule)recv).getClass("GraphicString").callMethod(recv.getRuntime().getCurrentContext(),"new",args); } @JRubyMethod(name="ISO64String", module=true, rest=true) public static IRubyObject fact_ISO64String(IRubyObject recv, IRubyObject[] args) { return ((RubyModule)recv).getClass("ISO64String").callMethod(recv.getRuntime().getCurrentContext(),"new",args); } @JRubyMethod(name="GeneralString", module=true, rest=true) public static IRubyObject fact_GeneralString(IRubyObject self, IRubyObject[] args) { return callClassNew(self, "GeneralString", args); } @JRubyMethod(name="UniversalString", module=true, rest=true) public static IRubyObject fact_UniversalString(IRubyObject self, IRubyObject[] args) { return callClassNew(self, "UniversalString", args); } @JRubyMethod(name="BMPString", module=true, rest=true) public static IRubyObject fact_BMPString(IRubyObject self, IRubyObject[] args) { return callClassNew(self, "BMPString", args); } @JRubyMethod(name="Nul", module=true, rest=true) public static IRubyObject fact_Null(IRubyObject self, IRubyObject[] args) { return callClassNew(self, "Null", args); } @JRubyMethod(name="ObjectId", module=true, rest=true) public static IRubyObject fact_ObjectId(IRubyObject self, IRubyObject[] args) { return callClassNew(self, "ObjectId", args); } @JRubyMethod(name="UTCTime", module=true, rest=true) public static IRubyObject fact_UTCTime(IRubyObject self, IRubyObject[] args) { return callClassNew(self, "UTCTime", args); } @JRubyMethod(name="GeneralizedTime", module=true, rest=true) public static IRubyObject fact_GeneralizedTime(IRubyObject self, IRubyObject[] args) { return callClassNew(self, "GeneralizedTime", args); } @JRubyMethod(name="Sequence", module=true, rest=true) public static IRubyObject fact_Sequence(IRubyObject self, IRubyObject[] args) { return callClassNew(self, "Sequence", args); } @JRubyMethod(name="Set", module=true, rest=true) public static IRubyObject fact_Set(IRubyObject self, IRubyObject[] args) { return callClassNew(self, "Set", args); } private static IRubyObject callClassNew(final IRubyObject self, final String className, final IRubyObject[] args) { return ((RubyModule) self).getClass(className).callMethod(self.getRuntime().getCurrentContext(), "new", args); } public static class ObjectId { @JRubyMethod(meta = true, rest = true) public static IRubyObject register(final IRubyObject self, final IRubyObject[] args) { final Ruby runtime = self.getRuntime(); final ASN1ObjectIdentifier derOid = new ASN1ObjectIdentifier( args[0].toString() ); final String a1 = args[1].toString(); final String a2 = args[2].toString(); synchronized(ASN1.class) { Map<String, ASN1ObjectIdentifier> sym2oid = getOIDLookup(runtime); sym2oid.put( a1.toLowerCase(), derOid ); sym2oid.put( a2.toLowerCase(), derOid ); getSymLookup(runtime).put( derOid, a1 ); } return runtime.getTrue(); } @JRubyMethod(name = { "sn", "short_name" }) public static RubyString sn(final ThreadContext context, final IRubyObject self) { return name(context, self.callMethod(context, "value"), false); } @JRubyMethod(name = { "ln", "long_name" }) public static RubyString ln(final ThreadContext context, final IRubyObject self) { return name(context, self.callMethod(context, "value"), true); } @JRubyMethod public static RubyString oid(final ThreadContext context, final IRubyObject self) { final Ruby runtime = context.runtime; return runtime.newString( getObjectID(runtime, self.callMethod(context, "value").toString()).getId() ); } private static RubyString name(final ThreadContext context, IRubyObject value, final boolean longName) { final Ruby runtime = context.runtime; final String oid = value.toString(); // name or oid Integer nid = null; try { nid = ASN1.oid2nid(runtime, ASN1.getObjectID(runtime, oid)); } catch (IllegalArgumentException e) { /* ignored */ } // not an oid if ( nid != null ) { String val = longName ? nid2ln(runtime, nid) : nid2sn(runtime, nid); if ( val != null ) return runtime.newString(val); } return value.asString(); } } // ObjectId static IRubyObject decodeObject(final ThreadContext context, final RubyModule ASN1, final org.bouncycastle.asn1.ASN1Encodable obj) throws IOException, IllegalArgumentException { final Ruby runtime = context.runtime; if ( obj instanceof ASN1Integer ) { final BN val = BN.newBN(runtime, ((ASN1Integer) obj).getValue()); return ASN1.getClass("Integer").callMethod(context, "new", val); } if ( obj instanceof DERInteger ) { final BN val = BN.newBN(runtime, ((DERInteger) obj).getValue()); return ASN1.getClass("Integer").callMethod(context, "new", val); } if ( obj instanceof DERBitString ) { final DERBitString derObj = (DERBitString) obj; RubyString str = runtime.newString( new ByteList(derObj.getBytes(), false) ); IRubyObject bitString = ASN1.getClass("BitString").callMethod(context, "new", str); bitString.callMethod(context, "unused_bits=", runtime.newFixnum( derObj.getPadBits() )); return bitString; } if ( obj instanceof ASN1String ) { final Integer typeId = typeId( obj.getClass() ); String type = typeId == null ? null : (String) ( ASN1_INFO[typeId][2] ); final ByteList bytes; if ( obj instanceof DERUTF8String ) { if ( type == null ) type = "UTF8String"; bytes = new ByteList(((DERUTF8String) obj).getString().getBytes("UTF-8"), false); } else { if ( type == null ) { if ( obj instanceof DERNumericString ) { type = "NumericString"; } else if ( obj instanceof DERPrintableString ) { type = "PrintableString"; } else if ( obj instanceof DERIA5String ) { type = "IA5String"; } else if ( obj instanceof DERT61String ) { type = "T61String"; } else if ( obj instanceof DERGeneralString ) { type = "GeneralString"; } else if ( obj instanceof DERUniversalString ) { type = "UniversalString"; } else if ( obj instanceof DERBMPString ) { type = "BMPString"; } else { // NOTE "VideotexString", "GraphicString", "ISO64String" not-handled in BC ! throw new IllegalArgumentException("could not handle ASN1 string type: " + obj + " (" + obj.getClass().getName() + ")"); } } bytes = ByteList.create(((ASN1String) obj).getString()); } return ASN1.getClass(type).callMethod(context, "new", runtime.newString(bytes)); } //if ( obj instanceof DEROctetString ) { // byte[] octets = ((ASN1OctetString) obj).getOctets(); // if ( (octets[0] & 0xFF) == 0xD1 ) Thread.dumpStack(); //} if ( obj instanceof ASN1OctetString ) { final ByteList octets = new ByteList(((ASN1OctetString) obj).getOctets(), false); // NOTE: sometimes MRI does include the tag but it really should not ;( ! //final ByteList octets = new ByteList(((ASN1OctetString) obj).getEncoded(ASN1Encoding.DER), false); return ASN1.getClass("OctetString").callMethod(context, "new", runtime.newString(octets)); } if ( obj instanceof ASN1Null ) { return ASN1.getClass("Null").callMethod(context,"new", runtime.getNil()); } if ( obj instanceof ASN1Boolean ) { final boolean val = ((ASN1Boolean) obj).isTrue(); return ASN1.getClass("Boolean").callMethod(context, "new", runtime.newBoolean(val)); } // DERBoolean extends ASN1Boolean only since 1.51 (<= 1.50 the other way around) if ( obj instanceof DERBoolean ) { final boolean val = ((DERBoolean) obj).isTrue(); return ASN1.getClass("Boolean").callMethod(context, "new", runtime.newBoolean(val)); } if ( obj instanceof ASN1UTCTime ) { final Date adjustedTime; try { adjustedTime = ((ASN1UTCTime) obj).getAdjustedDate(); } catch (ParseException e) { throw new IOException(e); } final RubyTime time = RubyTime.newTime(runtime, adjustedTime.getTime()); return ASN1.getClass("UTCTime").callMethod(context,"new", time); } // NOTE: keep for BC versions compatibility ... extends ASN1UTCTime (since BC 1.51) if ( obj instanceof DERUTCTime ) { final Date adjustedTime; try { adjustedTime = ((DERUTCTime) obj).getAdjustedDate(); } catch (ParseException e) { throw new IOException(e); } final RubyTime time = RubyTime.newTime(runtime, adjustedTime.getTime()); return ASN1.getClass("UTCTime").callMethod(context,"new", time); } if ( obj instanceof ASN1GeneralizedTime ) { final Date generalTime; try { generalTime = ((ASN1GeneralizedTime) obj).getDate(); } catch (ParseException e) { throw new IOException(e); } final RubyTime time = RubyTime.newTime(runtime, generalTime.getTime()); return ASN1.getClass("GeneralizedTime").callMethod(context,"new", time); } // NOTE: keep for BC versions compatibility ... extends ASN1GeneralizedTime (since BC 1.51) if ( obj instanceof DERGeneralizedTime ) { final Date generalTime; try { generalTime = ((DERGeneralizedTime) obj).getDate(); } catch (ParseException e) { throw new IOException(e); } final RubyTime time = RubyTime.newTime(runtime, generalTime.getTime()); return ASN1.getClass("GeneralizedTime").callMethod(context,"new", time); } if ( obj instanceof ASN1ObjectIdentifier ) { final String objId = ((ASN1ObjectIdentifier) obj).getId(); return ASN1.getClass("ObjectId").callMethod(context, "new", runtime.newString(objId)); } // ASN1ObjectIdentifier extends DERObjectIdentifier < 1.51 // DERObjectIdentifier extends ASN1ObjectIdentifier = 1.51 if ( obj instanceof DERObjectIdentifier ) { final String objId = ((DERObjectIdentifier) obj).getId(); return ASN1.getClass("ObjectId").callMethod(context, "new", runtime.newString(objId)); } if ( obj instanceof ASN1TaggedObject ) { final ASN1TaggedObject taggedObj = (ASN1TaggedObject) obj; IRubyObject val = decodeObject(context, ASN1, taggedObj.getObject()); IRubyObject tag = runtime.newFixnum( taggedObj.getTagNo() ); IRubyObject tag_class = runtime.newSymbol("CONTEXT_SPECIFIC"); final RubyArray valArr = runtime.newArray(val); return ASN1.getClass("ASN1Data").callMethod(context, "new", new IRubyObject[] { valArr, tag, tag_class } ); } if ( obj instanceof DERApplicationSpecific ) { final DERApplicationSpecific appSpecific = (DERApplicationSpecific) obj; IRubyObject tag = runtime.newFixnum( appSpecific.getApplicationTag() ); IRubyObject tag_class = runtime.newSymbol("APPLICATION"); final ASN1Sequence sequence = (ASN1Sequence) appSpecific.getObject(SEQUENCE); @SuppressWarnings("unchecked") final RubyArray valArr = decodeObjects(context, ASN1, sequence.getObjects()); return ASN1.getClass("ASN1Data").callMethod(context, "new", new IRubyObject[] { valArr, tag, tag_class } ); } if ( obj instanceof ASN1Sequence ) { @SuppressWarnings("unchecked") RubyArray arr = decodeObjects(context, ASN1, ((ASN1Sequence) obj).getObjects()); return ASN1.getClass("Sequence").callMethod(context, "new", arr); } if ( obj instanceof ASN1Set ) { @SuppressWarnings("unchecked") RubyArray arr = decodeObjects(context, ASN1, ((ASN1Set) obj).getObjects()); return ASN1.getClass("Set").callMethod(context, "new", arr); } if ( obj instanceof ASN1Enumerated ) { final RubyInteger value = RubyBignum.bignorm(runtime, ((ASN1Enumerated) obj).getValue()); return ASN1.getClass("Enumerated").callMethod(context, "new", value); } throw new IllegalArgumentException("unable to decode object: " + obj + " (" + ( obj == null ? "" : obj.getClass().getName() ) + ")"); } private static RubyArray decodeObjects(final ThreadContext context, final RubyModule ASN1, final Enumeration<ASN1Encodable> e) throws IOException { final RubyArray arr = context.runtime.newArray(); while ( e.hasMoreElements() ) { arr.append( decodeObject(context, ASN1, e.nextElement()) ); } return arr; } @JRubyMethod(meta = true) public static IRubyObject decode(final ThreadContext context, final IRubyObject self, final IRubyObject obj) { try { return decodeImpl(context, (RubyModule) self, obj); } catch (IOException e) { //throw context.runtime.newIOErrorFromException(e); throw newASN1Error(context.runtime, e.getMessage()); } catch (IllegalArgumentException e) { debugStackTrace(context.runtime, e); throw context.runtime.newArgumentError(e.getMessage()); } //catch (RuntimeException e) { // final Ruby runtime = context.runtime; // debugStackTrace(runtime, e); // throw Utils.newRuntimeError(context.runtime, e); //} } static IRubyObject decodeImpl(final ThreadContext context, IRubyObject obj) throws IOException, IllegalArgumentException { return decodeImpl(context, _ASN1(context.runtime), obj); } static IRubyObject decodeImpl(final ThreadContext context, final RubyModule ASN1, IRubyObject obj) throws IOException, IllegalArgumentException { obj = to_der_if_possible(context, obj); BytesInputStream in = new BytesInputStream( obj.asString().getByteList() ); final IRubyObject decoded = decodeImpl(context, ASN1, in); if ( in.available() > 0 ) { final int read = in.readCount(); throw new IOException("Type mismatch. Total bytes read: "+ read + " Bytes available: " + in.available()); } return decoded; } private static class BytesInputStream extends ByteArrayInputStream { private BytesInputStream(final ByteList bytes) { super(bytes.unsafeBytes(), bytes.getBegin(), bytes.getRealSize()); } final byte[] bytes() { return buf; } final int readCount() { return pos - mark; } // since last mark final int position() { return pos; } final int offset() { return mark; } } private static IRubyObject decodeImpl(final ThreadContext context, final RubyModule ASN1, final BytesInputStream in) throws IOException, IllegalArgumentException { // NOTE: need to handle OpenSSL::ASN1::Constructive wrapping by hand : final Integer tag = getConstructiveTag(in.bytes(), in.offset()); IRubyObject decoded = decodeObject(context, ASN1, readObject( in )); if ( tag != null ) { // OpenSSL::ASN1::Constructive.new( arg ) : final String type; List<IRubyObject> value = null; if ( tag.intValue() == SEQUENCE ) { //type = "Sequence"; // got a OpenSSL::ASN1::Sequence already : return Constructive.setInfiniteLength(context, decoded); } else if ( tag.intValue() == SET ) { //type = "Set"; // got a OpenSSL::ASN1::Set already : return Constructive.setInfiniteLength(context, decoded); } else { type = "Constructive"; } if ( value == null ) value = Collections.singletonList(decoded); return Constructive.newInfiniteConstructive(context, type, value, tag); } return decoded; } @JRubyMethod(meta = true, required = 1) public static IRubyObject decode_all(final ThreadContext context, final IRubyObject self, IRubyObject obj) { obj = to_der_if_possible(context, obj); BytesInputStream in = new BytesInputStream( obj.asString().getByteList() ); final RubyModule ASN1 = _ASN1(context.runtime); final RubyArray arr = context.runtime.newArray(); while ( in.available() > 0 ) { try { in.mark(0); // set offset() before each object is read arr.append( decodeImpl(context, ASN1, in) ); } catch (IOException e) { //throw context.runtime.newIOErrorFromException(e); throw newASN1Error(context.runtime, e.getMessage()); } catch (IllegalArgumentException e) { debugStackTrace(context.runtime, e); throw context.runtime.newArgumentError(e.getMessage()); } } return arr; } @JRubyMethod(meta = true, required = 1) public static IRubyObject traverse(final ThreadContext context, final IRubyObject self, IRubyObject arg) { warn(context, "WARNING: unimplemented method called: ASN1#traverse"); return context.runtime.getNil(); } public static RaiseException newASN1Error(Ruby runtime, String message) { return Utils.newError(runtime, _ASN1(runtime).getClass("ASN1Error"), message, false); } static RubyModule _ASN1(final Ruby runtime) { return (RubyModule) runtime.getModule("OpenSSL").getConstant("ASN1"); } static org.bouncycastle.asn1.ASN1Primitive readObject(final byte[] bytes) throws IOException { return new ASN1InputStream(new ByteArrayInputStream(bytes)).readObject(); } private static org.bouncycastle.asn1.ASN1Primitive readObject(final InputStream bytes) throws IOException { return new ASN1InputStream(bytes).readObject(); } // NOTE: BC's ASNInputStream internals "reinvented" a bit : private static Integer getConstructiveTag(final byte[] asn1, int offset) { final int tag = asn1[ offset ] & 0xFF; if ( ( tag & BERTags.CONSTRUCTED ) != 0 ) { // isConstructed // // calculate tag number // // readTagNumber(asn1, ++offset, tag) : int tagNo = tag & 0x1f; // // with tagged object tag number is bottom 5 bits, or stored at the start of the content // if (tagNo == 0x1f) { tagNo = 0; int b = asn1[ ++offset ]; //s.read(); // X.690-0207 8.1.2.4.2 // "c) bits 7 to 1 of the first subsequent octet shall not all be zero." if ((b & 0x7f) == 0) // Note: -1 will pass { return null; //throw new IOException("corrupted stream - invalid high tag number found"); } while ((b >= 0) && ((b & 0x80) != 0)) { tagNo |= (b & 0x7f); tagNo <<= 7; b = asn1[ ++offset ]; //s.read(); } if (b < 0) { return null; //throw new EOFException("EOF found inside tag value."); } tagNo |= (b & 0x7f); } // // calculate length // final int length = asn1[ ++offset ] & 0xFF; if ( length == 0x80 ) { // return -1; // indefinite-length encoding } else { return null; } if ((tag & BERTags.APPLICATION) != 0) { //return new BERApplicationSpecificParser(tagNo, sp).getLoadedObject(); } if ((tag & BERTags.TAGGED) != 0) { //return new BERTaggedObjectParser(true, tagNo, sp).getLoadedObject(); } //System.out.println(" tagNo = 0x" + Integer.toHexString(tagNo)); // TODO There are other tags that may be constructed (e.g. BIT_STRING) switch (tagNo) { case BERTags.SEQUENCE : //return new BERSequenceParser(sp).getLoadedObject(); return Integer.valueOf( SEQUENCE ); //return "Sequence"; case BERTags.SET : //return new BERSetParser(sp).getLoadedObject(); return Integer.valueOf( SET ); //return "Set"; case BERTags.OCTET_STRING : return Integer.valueOf( OCTET_STRING ); //return new BEROctetStringParser(sp).getLoadedObject(); case BERTags.EXTERNAL : //return new DERExternalParser(sp).getLoadedObject(); default: return Integer.valueOf( 0 ); //return "Constructive"; //throw new IOException("unknown BER object encountered"); } } return null; } public static class ASN1Data extends RubyObject { private static final long serialVersionUID = 6117598347932209839L; static ObjectAllocator ALLOCATOR = new ObjectAllocator() { public IRubyObject allocate(Ruby runtime, RubyClass klass) { return new ASN1Data(runtime, klass); } }; static final int MAX_TAG_VALUE = ASN1_INFO.length; public ASN1Data(Ruby runtime, RubyClass type) { super(runtime,type); } @JRubyMethod(visibility = Visibility.PRIVATE) public IRubyObject initialize(final ThreadContext context, final IRubyObject value, final IRubyObject tag, final IRubyObject tag_class) { checkTag(context.runtime, tag, tag_class, "UNIVERSAL"); this.callMethod(context, "tag=", tag); this.callMethod(context, "value=", value); this.callMethod(context, "tag_class=", tag_class); return this; } private void checkTag(final Ruby runtime, final IRubyObject tag, final IRubyObject tagClass, final String expected) { if ( ! (tagClass instanceof RubySymbol) ) { throw newASN1Error(runtime, "invalid tag class"); } if ( tagClass.toString().equals(expected) && RubyNumeric.fix2int(tag) > MAX_TAG_VALUE ) { throw newASN1Error(runtime, "tag number for :" + expected + " too large"); } } boolean isEOC() { return false; } boolean isExplicitTagging() { return ! isImplicitTagging(); } boolean isImplicitTagging() { return true; } int getTag(final ThreadContext context) { return RubyNumeric.fix2int(callMethod(context, "tag")); } ASN1Encodable toASN1(final ThreadContext context) { return toASN1TaggedObject(context); } final ASN1TaggedObject toASN1TaggedObject(final ThreadContext context) { final int tag = getTag(context); final IRubyObject val = callMethod(context, "value"); if ( val instanceof RubyArray ) { final RubyArray arr = (RubyArray) val; if ( arr.size() > 1 ) { ASN1EncodableVector vec = new ASN1EncodableVector(); for ( final IRubyObject obj : arr.toJavaArray() ) { ASN1Encodable data = ((ASN1Data) obj).toASN1(context); if ( data == null ) break; vec.add( data ); } return new DERTaggedObject(isExplicitTagging(), tag, new DERSequence(vec)); } else if ( arr.size() == 1 ) { ASN1Encodable data = ((ASN1Data) arr.entry(0)).toASN1(context); return new DERTaggedObject(isExplicitTagging(), tag, data); } else { throw new IllegalStateException("empty array detected"); } } return new DERTaggedObject(isExplicitTagging(), tag, ((ASN1Data) val).toASN1(context)); } @JRubyMethod public IRubyObject to_der(final ThreadContext context) { try { final byte[] encoded = toDER(context); return context.runtime.newString(new ByteList(encoded, false)); } catch (IOException e) { throw newASN1Error(context.runtime, e.getMessage()); } } byte[] toDER(final ThreadContext context) throws IOException { return toASN1(context).toASN1Primitive().getEncoded(ASN1Encoding.DER); } protected IRubyObject defaultTag() { final Integer id = typeId( getMetaClass() ); if ( id == null ) return getRuntime().getNil(); return getRuntime().newFixnum( id.intValue() ); } final IRubyObject value() { return value(getRuntime().getCurrentContext()); } IRubyObject value(final ThreadContext context) { return callMethod(context, "value"); } final String getClassBaseName() { return getMetaClass().getBaseName(); } @Override public String toString() { return value().toString(); } protected final void print() { print(0); } protected void print(int indent) { final PrintStream out = getRuntime().getOut(); printIndent(out, indent); final IRubyObject value = value(); out.println("ASN1Data: "); if ( value instanceof RubyArray ) { printArray(out, indent, (RubyArray) value); } else { ((ASN1Data) value).print(indent + 1); } } static void printIndent(final PrintStream out, final int indent) { for ( int i = 0; i < indent; i++) out.print(" "); } static void printArray(final PrintStream out, final int indent, final RubyArray array) { for ( int i = 0; i < array.size(); i++ ) { ((ASN1Data) array.entry(i)).print(indent + 1); } } static RaiseException createNativeRaiseException(final ThreadContext context, final Throwable e) { Throwable cause = e.getCause(); if ( cause == null ) cause = e; return RaiseException.createNativeRaiseException(context.runtime, cause); } } public static class Primitive extends ASN1Data { private static final long serialVersionUID = 8489625559339190259L; static ObjectAllocator ALLOCATOR = new ObjectAllocator() { public IRubyObject allocate(Ruby runtime, RubyClass klass) { return new Primitive(runtime, klass); } }; public Primitive(Ruby runtime, RubyClass type) { super(runtime,type); } @Override @JRubyMethod public IRubyObject to_der(final ThreadContext context) { if ( value(context).isNil() ) { // MRI compatibility but avoids Java exceptions as well e.g. // Java::JavaLang::NumberFormatException // java.math.BigInteger.<init>(BigInteger.java:296) // java.math.BigInteger.<init>(BigInteger.java:476) // org.jruby.ext.openssl.ASN1$ASN1Primitive.toASN1(ASN1.java:1287) // org.jruby.ext.openssl.ASN1$ASN1Data.to_der(ASN1.java:1129) // org.jruby.ext.openssl.ASN1$ASN1Primitive.to_der(ASN1.java:1202) throw context.runtime.newTypeError("nil value"); } return super.to_der(context); } @JRubyMethod(required = 0, optional = 4, visibility = Visibility.PRIVATE) public IRubyObject initialize(final ThreadContext context, final IRubyObject[] args) { initializeImpl(context, this, args); return this; } // shared initialize logic between Primitive and Constructive static void initializeImpl(final ThreadContext context, final ASN1Data self, final IRubyObject[] args) { final Ruby runtime = context.runtime; final int len = args.length; IRubyObject value = len == 0 ? runtime.getNil() : args[0]; final IRubyObject tag; IRubyObject tagging = runtime.getNil(); IRubyObject tag_class = runtime.getNil(); if ( len > 1 ) { tag = args[1]; if ( len > 2 ) { tagging = args[2]; if ( len > 3 ) tag_class = args[3]; } if ( tag.isNil() ) throw newASN1Error(runtime, "must specify tag number"); if ( tagging.isNil() ) tagging = runtime.newSymbol("EXPLICIT"); if ( ! (tagging instanceof RubySymbol) ) { throw newASN1Error(runtime, "invalid tag default"); } if ( tag_class.isNil() ) tag_class = runtime.newSymbol("CONTEXT_SPECIFIC"); if ( ! (tag_class instanceof RubySymbol) ) { throw newASN1Error(runtime, "invalid tag class"); } if ( tagging.toString().equals("IMPLICIT") && RubyNumeric.fix2int(tag) > MAX_TAG_VALUE ) { throw newASN1Error(runtime, "tag number for Universal too large"); } } else { tag = self.defaultTag(); tag_class = runtime.newSymbol("UNIVERSAL"); } // NOTE: Primitive only final String baseName = self.getMetaClass().getRealClass().getBaseName(); if ( "ObjectId".equals( baseName ) ) { final String name; try { name = oid2Sym( runtime, getObjectID(runtime, value.toString()), true ); } catch (IllegalArgumentException e) { // e.g. in case of nil "string not an OID" throw runtime.newTypeError(e.getMessage()); } if ( name != null ) value = runtime.newString(name); } self.setInstanceVariable("@tag", tag); self.setInstanceVariable("@value", value); self.setInstanceVariable("@tag_class", tag_class); self.setInstanceVariable("@tagging", tagging); self.setInstanceVariable("@infinite_length", runtime.getFalse()); } @Override boolean isExplicitTagging() { return "EXPLICIT".equals( getInstanceVariable("@tagging").toString() ); } @Override boolean isImplicitTagging() { IRubyObject tagging = getInstanceVariable("@tagging"); if ( tagging.isNil() ) return true; return "IMPLICIT".equals( tagging.toString() ); } @Override boolean isEOC() { return "EndOfContent".equals( getClassBaseName() ); } @Override byte[] toDER(final ThreadContext context) throws IOException { if ( isEOC() ) return new byte[] { 0x00, 0x00 }; return toASN1(context).toASN1Primitive().getEncoded(ASN1Encoding.DER); } static Primitive newInstance(final ThreadContext context, final String type, final IRubyObject value) { RubyClass klass = _ASN1(context.runtime).getClass(type); final Primitive self = new Primitive(context.runtime, klass); if ( value != null ) self.setInstanceVariable("@value", value); return self; } static Primitive newEndOfContent(final ThreadContext context) { return newInstance(context, "EndOfContent", null); } /* private static final Class DERBooleanClass; static { Class klass; try { klass = Class.forName("org.bouncycastle.asn1.DERBoolean"); } catch(ClassNotFoundException e) { klass = null; } DERBooleanClass = klass; } */ @Override ASN1Encodable toASN1(final ThreadContext context) { Class<? extends ASN1Encodable> type = typeClass( getMetaClass() ); if ( type == null ) { final int tag = getTag(context); if ( tag == 0 ) return null; // TODO pass EOC to BC ? if ( isExplicitTagging() ) type = typeClass( tag ); if ( type == null ) { throw new IllegalArgumentException( "no type for: " + getMetaClass() + " or tag: " + getTag(context) ); } } final IRubyObject val = callMethod(context, "value"); if ( type == ASN1ObjectIdentifier.class || type == DERObjectIdentifier.class ) { return getObjectID(context.runtime, val.toString()); } if ( type == DERNull.class || type == ASN1Null.class ) { return DERNull.INSTANCE; } if ( ASN1Boolean.class.isAssignableFrom( type ) ) { return ASN1Boolean.getInstance(val.isTrue()); } if ( type == DERBoolean.class ) { return DERBoolean.getInstance(val.isTrue()); } if ( type == DERUTCTime.class ) { if ( val instanceof RubyTime ) { return new DERUTCTime(((RubyTime) val).getJavaDate()); } return DERUTCTime.getInstance( val.asString().getBytes() ); } if ( type == DERGeneralizedTime.class ) { if ( val instanceof RubyTime ) { return new DERGeneralizedTime(((RubyTime) val).getJavaDate()); } return DERGeneralizedTime.getInstance( val.asString().getBytes() ); } if ( type == DERInteger.class ) { return new DERInteger( bigIntegerValue(val) ); } if ( ASN1Integer.class.isAssignableFrom( type ) ) { return new ASN1Integer( bigIntegerValue(val) ); } if ( type == DEREnumerated.class ) { return new DEREnumerated( bigIntegerValue(val) ); } if ( type == ASN1Enumerated.class ) { return new ASN1Enumerated( bigIntegerValue(val) ); } //if ( type == DEROctetString.class ) { if ( ASN1OctetString.class.isAssignableFrom( type ) ) { return new DEROctetString( val.asString().getBytes() ); } if ( type == DERBitString.class ) { final byte[] bs = val.asString().getBytes(); int unused = 0; for ( int i = (bs.length - 1); i > -1; i-- ) { if (bs[i] == 0) unused += 8; else { byte v2 = bs[i]; int x = 8; while ( v2 != 0 ) { v2 <<= 1; x--; } unused += x; break; } } return new DERBitString(bs, unused); } if ( type == DERIA5String.class ) { return new DERIA5String( val.asString().toString() ); } if ( type == DERUTF8String.class ) { return new DERUTF8String( val.asString().toString() ); } if ( type == DERBMPString.class ) { return new DERBMPString( val.asString().toString() ); } if ( type == DERUniversalString.class ) { return new DERUniversalString( val.asString().getBytes() ); } if ( type == DERGeneralString.class ) { return DERGeneralString.getInstance( val.asString().getBytes() ); } if ( type == DERVisibleString.class ) { return DERVisibleString.getInstance( val.asString().getBytes() ); } if ( type == DERNumericString.class ) { return DERNumericString.getInstance( val.asString().getBytes() ); } if ( val instanceof RubyString ) { try { return typeInstance(type, ( (RubyString) val ).getBytes()); } catch (Exception e) { // TODO exception handling debugStackTrace(context.runtime, e); throw createNativeRaiseException(context, e); } } // TODO throw an exception here too? if ( isDebug(context.runtime) ) { debug(this + " toASN1() could not handle class " + getMetaClass() + " and value " + val.inspect() + " (" + val.getClass().getName() + ")"); } warn(context, "WARNING: unimplemented method called: ASN1Data#toASN1 (" + type + ")"); return null; } private static BigInteger bigIntegerValue(final IRubyObject val) { if ( val instanceof RubyInteger ) { // RubyBignum return ((RubyInteger) val).getBigIntegerValue(); } if ( val instanceof BN ) ((BN) val).getValue(); return new BigInteger( val.asString().getBytes() ); } @Override protected void print(int indent) { final PrintStream out = getRuntime().getOut(); printIndent(out, indent); out.print(getMetaClass().getRealClass().getBaseName()); out.print(": "); out.println(value().callMethod(getRuntime().getCurrentContext(), "inspect").toString()); } } public static class Constructive extends ASN1Data { // implements ASN1Encodable { private static final long serialVersionUID = -7166662655104776828L; static ObjectAllocator ALLOCATOR = new ObjectAllocator() { public IRubyObject allocate(Ruby runtime, RubyClass klass) { return new Constructive(runtime, klass); } }; public Constructive(Ruby runtime, RubyClass type) { super(runtime, type); } @JRubyMethod(required = 1, optional = 3, visibility = Visibility.PRIVATE) public IRubyObject initialize(final ThreadContext context, final IRubyObject[] args) { Primitive.initializeImpl(context, this, args); return this; } static Constructive newInfiniteConstructive(final ThreadContext context, final String type, final List<IRubyObject> value, final int defaultTag) { final Ruby runtime = context.runtime; final RubyClass klass = _ASN1(context.runtime).getClass(type); final Constructive self = new Constructive(runtime, klass); final RubyArray values = runtime.newArray(value.size()); for ( final IRubyObject val : value ) values.append(val); // values.append( Primitive.newEndOfContent(context) ); self.setInstanceVariable("@tag", runtime.newFixnum(defaultTag)); self.setInstanceVariable("@value", values); self.setInstanceVariable("@tag_class", runtime.newSymbol("UNIVERSAL")); self.setInstanceVariable("@tagging", context.nil); return setInfiniteLength(context, self); } static Constructive setInfiniteLength(final ThreadContext context, final IRubyObject constructive) { final Constructive instance = ((Constructive) constructive); final IRubyObject eoc = Primitive.newEndOfContent(context); final IRubyObject value = instance.value(context); if ( value instanceof RubyArray ) ((RubyArray) value).append(eoc); else value.callMethod(context, "<<", eoc); instance.setInstanceVariable("@infinite_length", context.runtime.getTrue()); return instance; } private boolean rawConstructive() { return "Constructive".equals( getClassBaseName() ); } private boolean isSequence() { return "Sequence".equals( getClassBaseName() ); } private boolean isSet() { return "Set".equals( getClassBaseName() ); } private boolean isInfiniteLength() { return getInstanceVariable("@infinite_length").isTrue(); } @Override boolean isExplicitTagging() { IRubyObject tagging = getInstanceVariable("@tagging"); if ( tagging.isNil() ) return true; return "EXPLICIT".equals( tagging.toString() ); } @Override boolean isImplicitTagging() { return "IMPLICIT".equals( getInstanceVariable("@tagging").toString() ); } @Override ASN1Encodable toASN1(final ThreadContext context) { if ( isInfiniteLength() ) return super.toASN1(context); if ( isSequence() ) { return new DERSequence( toASN1EncodableVector(context) ); } if ( isSet() ) { return new DLSet( toASN1EncodableVector(context) ); // return new BERSet(values); //return ASN1Set.getInstance(toASN1TaggedObject(context), isExplicitTagging()); } switch ( getTag(context) ) { // "raw" Constructive ?!? case OCTET_STRING: final ASN1EncodableVector values = toASN1EncodableVector(context); ASN1OctetString[] octets = new ASN1OctetString[ values.size() ]; for ( int i = 0; i < values.size(); i++ ) { octets[i] = (ASN1OctetString) values.get(i).toASN1Primitive(); } return new BEROctetString(octets); case SEQUENCE: return new DERSequence( toASN1EncodableVector(context) ); case SET: return new DLSet( toASN1EncodableVector(context) ); // return new BERSet(values); //return ASN1Set.getInstance(toASN1TaggedObject(context), isExplicitTagging()); } throw new UnsupportedOperationException( this.inspect().toString() ); } @Override @JRubyMethod public IRubyObject to_der(final ThreadContext context) { if ( rawConstructive() ) { // MRI compatibility if ( ! isInfiniteLength() && ! super.value(context).isNil() ) { final Ruby runtime = context.runtime; throw newASN1Error(runtime, "Constructive shall only be used" + " with infinite length"); } } return super.to_der(context); } @Override byte[] toDER(final ThreadContext context) throws IOException { if ( isInfiniteLength() ) { if ( isSequence() ) { return sequenceToDER(context); } else if ( isSet() ) { return setToDER(context); } else { // "raw" Constructive switch ( getTag(context) ) { case OCTET_STRING: return octetStringToDER(context); case BIT_STRING: return bitStringToDER(context); case SEQUENCE: return sequenceToDER(context); case SET: return setToDER(context); } throw new UnsupportedOperationException( this.inspect().toString() ); } } return super.toDER(context); } private byte[] bitStringToDER(final ThreadContext context) throws IOException { final ASN1EncodableVector values = toASN1EncodableVector(context); final ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write(BERTags.CONSTRUCTED | BERTags.BIT_STRING); out.write(0x80); // infinite-length for ( int i = 0; i < values.size(); i++ ) { out.write( values.get(i).toASN1Primitive().getEncoded() ); } out.write(0x00); out.write(0x00); // writeBEREnd return out.toByteArray(); } private byte[] octetStringToDER(final ThreadContext context) throws IOException { final ASN1EncodableVector values = toASN1EncodableVector(context); ASN1OctetString[] octets = new ASN1OctetString[ values.size() ]; for ( int i = 0; i < values.size(); i++ ) { octets[i] = (ASN1OctetString) values.get(i).toASN1Primitive(); } return new BEROctetString(octets).getEncoded(); } private byte[] sequenceToDER(final ThreadContext context) throws IOException { final ASN1EncodableVector values = toASN1EncodableVector(context); final ByteArrayOutputStream out = new ByteArrayOutputStream(64); BERSequenceGenerator sequenceGenerator = new BERSequenceGenerator(out); for ( int i = 0; i < values.size(); i++ ) { final ASN1Encodable value = values.get(i); if ( value instanceof InternalEncodable ) { // HACK byte[] nested = ((InternalEncodable) value).entry.toDER(context); out.write(nested, 0, nested.length); continue; } sequenceGenerator.addObject( values.get(i) ); } sequenceGenerator.close(); return out.toByteArray(); } private byte[] setToDER(final ThreadContext context) throws IOException { final ASN1EncodableVector values = toASN1EncodableVector(context); return new BERSet(values).toASN1Primitive().getEncoded(); } private ASN1EncodableVector toASN1EncodableVector(final ThreadContext context) { final ASN1EncodableVector vec = new ASN1EncodableVector(); final IRubyObject value = value(context); if ( value instanceof RubyArray ) { final RubyArray val = (RubyArray) value; for ( int i = 0; i < val.size(); i++ ) { if ( addEntry(context, vec, val.entry(i)) ) break; } } else { final int size = RubyInteger.num2int(value.callMethod(context, "size")); for ( int i = 0; i < size; i++ ) { final RubyInteger idx = context.runtime.newFixnum(i); IRubyObject entry = value.callMethod(context, "[]", idx); if ( addEntry(context, vec, entry) ) break; } } return vec; } public ASN1Primitive toASN1Primitive() { throw new UnsupportedOperationException(); } private static class InternalEncodable implements ASN1Encodable { final Constructive entry; InternalEncodable(Constructive entry) { this.entry = entry; } @Override public ASN1Primitive toASN1Primitive() { throw new UnsupportedOperationException(); } } private static boolean addEntry(final ThreadContext context, final ASN1EncodableVector vec, final IRubyObject entry) { try { if ( entry instanceof Constructive ) { final Constructive constructive = (Constructive) entry; if ( constructive.isInfiniteLength() || constructive.rawConstructive() ) { vec.add( new InternalEncodable( (Constructive) entry) ); } else { vec.add( constructive.toASN1(context) ); } } else if ( entry instanceof ASN1Data ) { final ASN1Data data = ( (ASN1Data) entry ); if ( data.isEOC() ) return true; vec.add( data.toASN1(context) ); } else { vec.add( ( (ASN1Data) decodeImpl(context, entry) ).toASN1(context) ); } return false; } catch (IOException e) { throw Utils.newIOError(context.runtime, e); } } @JRubyMethod public IRubyObject each(final ThreadContext context, final Block block) { final IRubyObject value = value(context); if ( value instanceof RubyArray ) { final RubyArray val = (RubyArray) value; for ( int i = 0; i < val.size(); i++ ) { block.yield(context, val.entry(i)); } } else { value.callMethod(context, "each", NULL_ARRAY, block); //final int size = RubyInteger.num2int(value.callMethod(context, "size")); //for ( int i = 0; i < size; i++ ) { // final RubyInteger idx = context.runtime.newFixnum(i); // block.yield(context, value.callMethod(context, "[]", idx)); //} } return context.runtime.getNil(); } @JRubyMethod public IRubyObject size(final ThreadContext context) { final IRubyObject value = value(context); if ( value instanceof RubyArray ) { final RubyArray val = (RubyArray) value; return context.runtime.newFixnum(val.size()); } else { return value.callMethod(context, "size"); } } @Override protected void print(int indent) { final PrintStream out = getRuntime().getOut(); printIndent(out, indent); out.print(getMetaClass().getRealClass().getBaseName()); out.println(": "); printArray( out, indent, (RubyArray) value( getRuntime().getCurrentContext() ) ); } } }// ASN1