| /* | |
| * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package sun.security.pkcs11; | |
| import java.lang.ref.Cleaner; | |
| import java.math.BigInteger; | |
| import java.nio.ByteBuffer; | |
| import java.nio.CharBuffer; | |
| import java.nio.charset.Charset; | |
| import java.security.*; | |
| import sun.security.pkcs11.wrapper.PKCS11Exception; | |
| import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.*; | |
| /** | |
| * Collection of static utility methods. | |
| * | |
| * @author Andreas Sterbenz | |
| * @since 1.5 | |
| */ | |
| public final class P11Util { | |
| // A cleaner, shared within this module. | |
| public static final Cleaner cleaner = Cleaner.create(); | |
| private static final Object LOCK = new Object(); | |
| private static volatile Provider sun, sunRsaSign, sunJce; | |
| private P11Util() { | |
| // empty | |
| } | |
| static boolean isNSS(Token token) { | |
| char[] tokenLabel = token.tokenInfo.label; | |
| if (tokenLabel != null && tokenLabel.length >= 3) { | |
| return (tokenLabel[0] == 'N' && tokenLabel[1] == 'S' | |
| && tokenLabel[2] == 'S'); | |
| } | |
| return false; | |
| } | |
| static char[] encodePassword(char[] password, Charset cs, | |
| int nullTermBytes) { | |
| /* | |
| * When a Java char (2 bytes) is converted to CK_UTF8CHAR (1 byte) for | |
| * a PKCS #11 (native) call, the high-order byte is discarded (see | |
| * jCharArrayToCKUTF8CharArray in p11_util.c). In order to have an | |
| * encoded string passed to C_GenerateKey, we need to account for | |
| * truncation and expand beforehand: high and low parts of each char | |
| * are split into 2 chars. As an example, this is the transformation | |
| * for a NULL terminated password "a" that has to be encoded in | |
| * UTF-16 BE: | |
| * char[] password => [ 0x0061, 0x0000 ] | |
| * / \ / \ | |
| * ByteBuffer passwordBytes => [ 0x00, 0x61, 0x00, 0x00 ] | |
| * | | | | | |
| * char[] encPassword => [0x0000, 0x0061, 0x0000, 0x0000] | |
| * | | | | | |
| * PKCS #11 call (bytes) => [ 0x00, 0x61, 0x00, 0x00 ] | |
| */ | |
| ByteBuffer passwordBytes = cs.encode(CharBuffer.wrap(password)); | |
| char[] encPassword = | |
| new char[passwordBytes.remaining() + nullTermBytes]; | |
| int i = 0; | |
| while (passwordBytes.hasRemaining()) { | |
| encPassword[i] = (char) (passwordBytes.get() & 0xFF); | |
| // Erase password bytes as we read during encoding. | |
| passwordBytes.put(i++, (byte) 0); | |
| } | |
| return encPassword; | |
| } | |
| static Provider getSunProvider() { | |
| Provider p = sun; | |
| if (p == null) { | |
| synchronized (LOCK) { | |
| p = getProvider | |
| (sun, "SUN", "sun.security.provider.Sun"); | |
| sun = p; | |
| } | |
| } | |
| return p; | |
| } | |
| static Provider getSunRsaSignProvider() { | |
| Provider p = sunRsaSign; | |
| if (p == null) { | |
| synchronized (LOCK) { | |
| p = getProvider | |
| (sunRsaSign, "SunRsaSign", "sun.security.rsa.SunRsaSign"); | |
| sunRsaSign = p; | |
| } | |
| } | |
| return p; | |
| } | |
| static Provider getSunJceProvider() { | |
| Provider p = sunJce; | |
| if (p == null) { | |
| synchronized (LOCK) { | |
| p = getProvider | |
| (sunJce, "SunJCE", "com.sun.crypto.provider.SunJCE"); | |
| sunJce = p; | |
| } | |
| } | |
| return p; | |
| } | |
| @SuppressWarnings("deprecation") | |
| private static Provider getProvider(Provider p, String providerName, | |
| String className) { | |
| if (p != null) { | |
| return p; | |
| } | |
| p = Security.getProvider(providerName); | |
| if (p == null) { | |
| try { | |
| final Class<?> c = Class.forName(className); | |
| p = (Provider) c.newInstance(); | |
| } catch (Exception e) { | |
| // Unexpected, as className is not a user but a | |
| // P11Util-internal value. | |
| throw new ProviderException("Could not find provider " + | |
| providerName, e); | |
| } | |
| } | |
| return p; | |
| } | |
| static byte[] convert(byte[] input, int offset, int len) { | |
| if ((offset == 0) && (len == input.length)) { | |
| return input; | |
| } else { | |
| byte[] t = new byte[len]; | |
| System.arraycopy(input, offset, t, 0, len); | |
| return t; | |
| } | |
| } | |
| static byte[] subarray(byte[] b, int ofs, int len) { | |
| byte[] out = new byte[len]; | |
| System.arraycopy(b, ofs, out, 0, len); | |
| return out; | |
| } | |
| static byte[] concat(byte[] b1, byte[] b2) { | |
| byte[] b = new byte[b1.length + b2.length]; | |
| System.arraycopy(b1, 0, b, 0, b1.length); | |
| System.arraycopy(b2, 0, b, b1.length, b2.length); | |
| return b; | |
| } | |
| static long[] concat(long[] b1, long[] b2) { | |
| if (b1.length == 0) { | |
| return b2; | |
| } | |
| long[] b = new long[b1.length + b2.length]; | |
| System.arraycopy(b1, 0, b, 0, b1.length); | |
| System.arraycopy(b2, 0, b, b1.length, b2.length); | |
| return b; | |
| } | |
| public static byte[] getMagnitude(BigInteger bi) { | |
| byte[] b = bi.toByteArray(); | |
| if ((b.length > 1) && (b[0] == 0)) { | |
| int n = b.length - 1; | |
| byte[] newarray = new byte[n]; | |
| System.arraycopy(b, 1, newarray, 0, n); | |
| b = newarray; | |
| } | |
| return b; | |
| } | |
| static byte[] sha1(byte[] data) { | |
| try { | |
| MessageDigest md = MessageDigest.getInstance("SHA-1"); | |
| md.update(data); | |
| return md.digest(); | |
| } catch (GeneralSecurityException e) { | |
| throw new ProviderException(e); | |
| } | |
| } | |
| private static final char[] hexDigits = "0123456789abcdef".toCharArray(); | |
| static String toString(byte[] b) { | |
| if (b == null) { | |
| return "(null)"; | |
| } | |
| StringBuilder sb = new StringBuilder(b.length * 3); | |
| for (int i = 0; i < b.length; i++) { | |
| int k = b[i] & 0xff; | |
| if (i != 0) { | |
| sb.append(':'); | |
| } | |
| sb.append(hexDigits[k >>> 4]); | |
| sb.append(hexDigits[k & 0xf]); | |
| } | |
| return sb.toString(); | |
| } | |
| // returns true if successfully cancelled | |
| static boolean trySessionCancel(Token token, Session session, long flags) | |
| throws ProviderException { | |
| if (token.p11.getVersion().major == 3) { | |
| try { | |
| token.p11.C_SessionCancel(session.id(), flags); | |
| return true; | |
| } catch (PKCS11Exception e) { | |
| // return false for CKR_OPERATION_CANCEL_FAILED, so callers | |
| // can cancel in the pre v3.0 way, i.e. by finishing off the | |
| // current operation | |
| if (!e.match(CKR_OPERATION_CANCEL_FAILED)) { | |
| throw new ProviderException("cancel failed", e); | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| } |