/** * Returns the encoded SPNEGO token Note: inserts the required CHOICE tags * * @return the encoded token * @exception GSSException */ byte[] getEncoded() throws IOException, GSSException { // get the token encoded value DerOutputStream token = new DerOutputStream(); token.write(encode()); // now insert the CHOICE switch (tokenType) { case NEG_TOKEN_INIT_ID: // Insert CHOICE of Negotiation Token DerOutputStream initToken = new DerOutputStream(); initToken.write( DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) NEG_TOKEN_INIT_ID), token); return initToken.toByteArray(); case NEG_TOKEN_TARG_ID: // Insert CHOICE of Negotiation Token DerOutputStream targToken = new DerOutputStream(); targToken.write( DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) NEG_TOKEN_TARG_ID), token); return targToken.toByteArray(); default: return token.toByteArray(); } }
@SuppressWarnings("deprecation") private byte[] extractKeyData(DerInputStream stream) throws IOException, NoSuchAlgorithmException, CertificateException { byte[] returnValue = null; DerValue[] safeBags = stream.getSequence(2); int count = safeBags.length; /* * Spin over the SafeBags. */ for (int i = 0; i < count; i++) { ObjectIdentifier bagId; DerInputStream sbi; DerValue bagValue; Object bagItem = null; sbi = safeBags[i].toDerInputStream(); bagId = sbi.getOID(); bagValue = sbi.getDerValue(); if (!bagValue.isContextSpecific((byte) 0)) { throw new IOException("unsupported PKCS12 bag value type " + bagValue.tag); } bagValue = bagValue.data.getDerValue(); if (bagId.equals(PKCS8ShroudedKeyBag_OID)) { // got what we were looking for. Return it. returnValue = bagValue.toByteArray(); } else { // log error message for "unsupported PKCS12 bag type" System.out.println("Unsupported bag type '" + bagId + "'"); } } return returnValue; }
/** * Check if the certificate allows use of the given DNS name. * * <p>From RFC2818: If a subjectAltName extension of type dNSName is present, that MUST be used as * the identity. Otherwise, the (most specific) Common Name field in the Subject field of the * certificate MUST be used. Although the use of the Common Name is existing practice, it is * deprecated and Certification Authorities are encouraged to use the dNSName instead. * * <p>Matching is performed using the matching rules specified by [RFC2459]. If more than one * identity of a given type is present in the certificate (e.g., more than one dNSName name, a * match in any one of the set is considered acceptable.) */ private void matchDNS(String expectedName, X509Certificate cert) throws CertificateException { Collection<List<?>> subjAltNames = cert.getSubjectAlternativeNames(); if (subjAltNames != null) { boolean foundDNS = false; for (List<?> next : subjAltNames) { if (((Integer) next.get(0)).intValue() == ALTNAME_DNS) { foundDNS = true; String dnsName = (String) next.get(1); if (isMatched(expectedName, dnsName)) { return; } } } if (foundDNS) { // if certificate contains any subject alt names of type DNS // but none match, reject throw new CertificateException( "No subject alternative DNS " + "name matching " + expectedName + " found."); } } X500Name subjectName = getSubjectX500Name(cert); DerValue derValue = subjectName.findMostSpecificAttribute(X500Name.commonName_oid); if (derValue != null) { try { if (isMatched(expectedName, derValue.getAsString())) { return; } } catch (IOException e) { // ignore } } String msg = "No name matching " + expectedName + " found"; throw new CertificateException(msg); }
@SuppressWarnings("deprecation") private byte[] fetchPrivateKeyFromBag(byte[] privateKeyInfo) throws IOException, NoSuchAlgorithmException, CertificateException { byte[] returnValue = null; DerValue val = new DerValue(new ByteArrayInputStream(privateKeyInfo)); DerInputStream s = val.toDerInputStream(); int version = s.getInteger(); if (version != 3) { throw new IOException("PKCS12 keystore not in version 3 format"); } /* * Read the authSafe. */ byte[] authSafeData; ContentInfo authSafe = new ContentInfo(s); ObjectIdentifier contentType = authSafe.getContentType(); if (contentType.equals(ContentInfo.DATA_OID)) { authSafeData = authSafe.getData(); } else /* signed data */ { throw new IOException("public key protected PKCS12 not supported"); } DerInputStream as = new DerInputStream(authSafeData); DerValue[] safeContentsArray = as.getSequence(2); int count = safeContentsArray.length; /* * Spin over the ContentInfos. */ for (int i = 0; i < count; i++) { byte[] safeContentsData; ContentInfo safeContents; DerInputStream sci; byte[] eAlgId = null; sci = new DerInputStream(safeContentsArray[i].toByteArray()); safeContents = new ContentInfo(sci); contentType = safeContents.getContentType(); safeContentsData = null; if (contentType.equals(ContentInfo.DATA_OID)) { safeContentsData = safeContents.getData(); } else if (contentType.equals(ContentInfo.ENCRYPTED_DATA_OID)) { // The password was used to export the private key from the keychain. // The Keychain won't export the key with encrypted data, so we don't need // to worry about it. continue; } else { throw new IOException("public key protected PKCS12" + " not supported"); } DerInputStream sc = new DerInputStream(safeContentsData); returnValue = extractKeyData(sc); } return returnValue; }
/** * Parse (unmarshal) a realm from a DER input stream. This form parsing might be used when * expanding a value which is part of a constructed sequence and uses explicitly tagged type. * * @exception Asn1Exception on error. * @param data the Der input stream value, which contains one or more marshaled value. * @param explicitTag tag number. * @param optional indicate if this data field is optional * @return an instance of Realm. */ public static Realm parse(DerInputStream data, byte explicitTag, boolean optional) throws Asn1Exception, IOException, RealmException { if ((optional) && (((byte) data.peekByte() & (byte) 0x1F) != explicitTag)) { return null; } DerValue der = data.getDerValue(); if (explicitTag != (der.getTag() & (byte) 0x1F)) { throw new Asn1Exception(Krb5.ASN1_BAD_ID); } else { DerValue subDer = der.getData().getDerValue(); return new Realm(subDer); } }
/* * parse Algorithm Parameters */ private AlgorithmParameters parseAlgParameters(DerInputStream in) throws IOException { AlgorithmParameters algParams = null; try { DerValue params; if (in.available() == 0) { params = null; } else { params = in.getDerValue(); if (params.tag == DerValue.tag_Null) { params = null; } } if (params != null) { algParams = AlgorithmParameters.getInstance("PBE"); algParams.init(params.toByteArray()); } } catch (Exception e) { IOException ioe = new IOException("parseAlgParameters failed: " + e.getMessage()); ioe.initCause(e); throw ioe; } return algParams; }
/** * Returns the key associated with the given alias, using the given password to recover it. * * @param alias the alias name * @param password the password for recovering the key. This password is used internally as the * key is exported in a PKCS12 format. * @return the requested key, or null if the given alias does not exist or does not identify a * <i>key entry</i>. * @exception NoSuchAlgorithmException if the algorithm for recovering the key cannot be found * @exception UnrecoverableKeyException if the key cannot be recovered (e.g., the given password * is wrong). */ public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException { permissionCheck(); // An empty password is rejected by MacOS API, no private key data // is exported. If no password is passed (as is the case when // this implementation is used as browser keystore in various // deployment scenarios like Webstart, JFX and applets), create // a dummy password so MacOS API is happy. if (password == null || password.length == 0) { // Must not be a char array with only a 0, as this is an empty // string. if (random == null) { random = new SecureRandom(); } password = Long.toString(random.nextLong()).toCharArray(); } Object entry = entries.get(alias.toLowerCase()); if (entry == null || !(entry instanceof KeyEntry)) { return null; } // This call gives us a PKCS12 bag, with the key inside it. byte[] exportedKeyInfo = _getEncodedKeyData(((KeyEntry) entry).keyRef, password); if (exportedKeyInfo == null) { return null; } PrivateKey returnValue = null; try { byte[] pkcs8KeyData = fetchPrivateKeyFromBag(exportedKeyInfo); byte[] encryptedKey; AlgorithmParameters algParams; ObjectIdentifier algOid; try { // get the encrypted private key EncryptedPrivateKeyInfo encrInfo = new EncryptedPrivateKeyInfo(pkcs8KeyData); encryptedKey = encrInfo.getEncryptedData(); // parse Algorithm parameters DerValue val = new DerValue(encrInfo.getAlgorithm().encode()); DerInputStream in = val.toDerInputStream(); algOid = in.getOID(); algParams = parseAlgParameters(in); } catch (IOException ioe) { UnrecoverableKeyException uke = new UnrecoverableKeyException( "Private key not stored as " + "PKCS#8 EncryptedPrivateKeyInfo: " + ioe); uke.initCause(ioe); throw uke; } // Use JCE to decrypt the data using the supplied password. SecretKey skey = getPBEKey(password); Cipher cipher = Cipher.getInstance(algOid.toString()); cipher.init(Cipher.DECRYPT_MODE, skey, algParams); byte[] decryptedPrivateKey = cipher.doFinal(encryptedKey); PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(decryptedPrivateKey); // Parse the key algorithm and then use a JCA key factory to create the private key. DerValue val = new DerValue(decryptedPrivateKey); DerInputStream in = val.toDerInputStream(); // Ignore this -- version should be 0. int i = in.getInteger(); // Get the Algorithm ID next DerValue[] value = in.getSequence(2); AlgorithmId algId = new AlgorithmId(value[0].getOID()); String algName = algId.getName(); // Get a key factory for this algorithm. It's likely to be 'RSA'. KeyFactory kfac = KeyFactory.getInstance(algName); returnValue = kfac.generatePrivate(kspec); } catch (Exception e) { UnrecoverableKeyException uke = new UnrecoverableKeyException("Get Key failed: " + e.getMessage()); uke.initCause(e); throw uke; } return returnValue; }