1 /* 2 * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.ec; 27 28 import java.io.IOException; 29 import java.math.BigInteger; 30 31 import java.security.*; 32 import java.security.interfaces.*; 33 import java.security.spec.*; 34 35 import sun.security.util.*; 36 import sun.security.x509.AlgorithmId; 37 import sun.security.pkcs.PKCS8Key; 38 39 /** 40 * Key implementation for EC private keys. 41 * 42 * ASN.1 syntax for EC private keys from SEC 1 v1.5 (draft): 43 * 44 * <pre> 45 * EXPLICIT TAGS 46 * 47 * ECPrivateKey ::= SEQUENCE { 48 * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), 49 * privateKey OCTET STRING, 50 * parameters [0] ECDomainParameters {{ SECGCurveNames }} OPTIONAL, 51 * publicKey [1] BIT STRING OPTIONAL 52 * } 53 * </pre> 54 * 55 * We currently ignore the optional parameters and publicKey fields. We 56 * require that the parameters are encoded as part of the AlgorithmIdentifier, 57 * not in the private key structure. 58 * 59 * @since 1.6 60 * @author Andreas Sterbenz 61 */ 62 public final class ECPrivateKeyImpl extends PKCS8Key implements ECPrivateKey { 63 64 private static final long serialVersionUID = 88695385615075129L; 65 66 private BigInteger s; // private value 67 private byte[] arrayS; // private value as a little-endian array 68 private ECParameterSpec params; 69 70 /** 71 * Construct a key from its encoding. Called by the ECKeyFactory. 72 */ 73 ECPrivateKeyImpl(byte[] encoded) throws InvalidKeyException { 74 decode(encoded); 75 } 76 77 /** 78 * Construct a key from its components. Used by the 79 * KeyFactory. 80 */ 81 ECPrivateKeyImpl(BigInteger s, ECParameterSpec params) 82 throws InvalidKeyException { 83 this.s = s; 84 this.params = params; 85 makeEncoding(s); 86 87 } 88 89 ECPrivateKeyImpl(byte[] s, ECParameterSpec params) 90 throws InvalidKeyException { 91 this.arrayS = s.clone(); 92 this.params = params; 93 makeEncoding(s); 94 } 95 96 private void makeEncoding(byte[] s) throws InvalidKeyException { 97 algid = new AlgorithmId 98 (AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params)); 99 try { 100 DerOutputStream out = new DerOutputStream(); 101 out.putInteger(1); // version 1 102 byte[] privBytes = s.clone(); 103 ArrayUtil.reverse(privBytes); 104 out.putOctetString(privBytes); 105 DerValue val = 106 new DerValue(DerValue.tag_Sequence, out.toByteArray()); 107 key = val.toByteArray(); 108 } catch (IOException exc) { 109 // should never occur 110 throw new InvalidKeyException(exc); 111 } 112 } 113 114 private void makeEncoding(BigInteger s) throws InvalidKeyException { 115 algid = new AlgorithmId 116 (AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params)); 117 try { 118 byte[] sArr = s.toByteArray(); 119 // convert to fixed-length array 120 int numOctets = (params.getOrder().bitLength() + 7) / 8; 121 byte[] sOctets = new byte[numOctets]; 122 int inPos = Math.max(sArr.length - sOctets.length, 0); 123 int outPos = Math.max(sOctets.length - sArr.length, 0); 124 int length = Math.min(sArr.length, sOctets.length); 125 System.arraycopy(sArr, inPos, sOctets, outPos, length); 126 127 DerOutputStream out = new DerOutputStream(); 128 out.putInteger(1); // version 1 129 out.putOctetString(sOctets); 130 DerValue val = 131 new DerValue(DerValue.tag_Sequence, out.toByteArray()); 132 key = val.toByteArray(); 133 } catch (IOException exc) { 134 // should never occur 135 throw new InvalidKeyException(exc); 136 } 137 } 138 139 // see JCA doc 140 public String getAlgorithm() { 141 return "EC"; 142 } 143 144 // see JCA doc 145 public BigInteger getS() { 146 if (s == null) { 147 byte[] arrCopy = arrayS.clone(); 148 ArrayUtil.reverse(arrCopy); 149 s = new BigInteger(1, arrCopy); 150 } 151 return s; 152 } 153 154 public byte[] getArrayS() { 155 if (arrayS == null) { 156 byte[] arr = getS().toByteArray(); 157 ArrayUtil.reverse(arr); 158 int byteLength = (params.getOrder().bitLength() + 7) / 8; 159 arrayS = new byte[byteLength]; 160 int length = Math.min(byteLength, arr.length); 161 System.arraycopy(arr, 0, arrayS, 0, length); 162 } 163 return arrayS.clone(); 164 } 165 166 // see JCA doc 167 public ECParameterSpec getParams() { 168 return params; 169 } 170 171 /** 172 * Parse the key. Called by PKCS8Key. 173 */ 174 protected void parseKeyBits() throws InvalidKeyException { 175 try { 176 DerInputStream in = new DerInputStream(key); 177 DerValue derValue = in.getDerValue(); 178 if (derValue.tag != DerValue.tag_Sequence) { 179 throw new IOException("Not a SEQUENCE"); 180 } 181 DerInputStream data = derValue.data; 182 int version = data.getInteger(); 183 if (version != 1) { 184 throw new IOException("Version must be 1"); 185 } 186 byte[] privData = data.getOctetString(); 187 ArrayUtil.reverse(privData); 188 arrayS = privData; 189 while (data.available() != 0) { 190 DerValue value = data.getDerValue(); 191 if (value.isContextSpecific((byte) 0)) { 192 // ignore for now 193 } else if (value.isContextSpecific((byte) 1)) { 194 // ignore for now 195 } else { 196 throw new InvalidKeyException("Unexpected value: " + value); 197 } 198 } 199 AlgorithmParameters algParams = this.algid.getParameters(); 200 if (algParams == null) { 201 throw new InvalidKeyException("EC domain parameters must be " 202 + "encoded in the algorithm identifier"); 203 } 204 params = algParams.getParameterSpec(ECParameterSpec.class); 205 } catch (IOException e) { 206 throw new InvalidKeyException("Invalid EC private key", e); 207 } catch (InvalidParameterSpecException e) { 208 throw new InvalidKeyException("Invalid EC private key", e); 209 } 210 } 211 }