001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2016, Connect2id Ltd and contributors. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.jose.crypto.impl; 019 020 021import java.security.NoSuchAlgorithmException; 022import java.security.Provider; 023import java.security.Signature; 024import java.security.interfaces.ECKey; 025import java.security.spec.ECParameterSpec; 026 027import com.nimbusds.jose.JOSEException; 028import com.nimbusds.jose.JWSAlgorithm; 029import com.nimbusds.jose.jwk.Curve; 030 031 032/** 033 * Elliptic Curve Digital Signature Algorithm (ECDSA) functions and utilities. 034 * 035 * @author Vladimir Dzhuvinov 036 * @author Aleksei Doroganov 037 * @version 2018-03-28 038 */ 039public class ECDSA { 040 041 042 /** 043 * Resolves the matching EC DSA algorithm for the specified EC key 044 * (public or private). 045 * 046 * @param ecKey The EC key. Must not be {@code null}. 047 * 048 * @return The matching EC DSA algorithm. 049 * 050 * @throws JOSEException If the elliptic curve of key is not supported. 051 */ 052 public static JWSAlgorithm resolveAlgorithm(final ECKey ecKey) 053 throws JOSEException { 054 055 ECParameterSpec ecParameterSpec = ecKey.getParams(); 056 return resolveAlgorithm(Curve.forECParameterSpec(ecParameterSpec)); 057 } 058 059 060 /** 061 * Resolves the matching EC DSA algorithm for the specified elliptic 062 * curve. 063 * 064 * @param curve The elliptic curve. May be {@code null}. 065 * 066 * @return The matching EC DSA algorithm. 067 * 068 * @throws JOSEException If the elliptic curve of key is not supported. 069 */ 070 public static JWSAlgorithm resolveAlgorithm(final Curve curve) 071 throws JOSEException { 072 073 if (curve == null) { 074 throw new JOSEException("The EC key curve is not supported, must be P-256, P-384 or P-521"); 075 } else if (Curve.P_256.equals(curve)) { 076 return JWSAlgorithm.ES256; 077 } else if (Curve.P_256K.equals(curve)) { 078 return JWSAlgorithm.ES256K; 079 } else if (Curve.P_384.equals(curve)) { 080 return JWSAlgorithm.ES384; 081 } else if (Curve.P_521.equals(curve)) { 082 return JWSAlgorithm.ES512; 083 } else { 084 throw new JOSEException("Unexpected curve: " + curve); 085 } 086 } 087 088 089 /** 090 * Creates a new JCA signer / verifier for ECDSA. 091 * 092 * @param alg The ECDSA JWS algorithm. Must not be 093 * {@code null}. 094 * @param jcaProvider The JCA provider, {@code null} if not specified. 095 * 096 * @return The JCA signer / verifier instance. 097 * 098 * @throws JOSEException If a JCA signer / verifier couldn't be 099 * created. 100 */ 101 public static Signature getSignerAndVerifier(final JWSAlgorithm alg, 102 final Provider jcaProvider) 103 throws JOSEException { 104 105 String jcaAlg; 106 107 if (alg.equals(JWSAlgorithm.ES256)) { 108 jcaAlg = "SHA256withECDSA"; 109 } else if (alg.equals(JWSAlgorithm.ES256K)) { 110 jcaAlg = "SHA256withECDSA"; 111 } else if (alg.equals(JWSAlgorithm.ES384)) { 112 jcaAlg = "SHA384withECDSA"; 113 } else if (alg.equals(JWSAlgorithm.ES512)) { 114 jcaAlg = "SHA512withECDSA"; 115 } else { 116 throw new JOSEException( 117 AlgorithmSupportMessage.unsupportedJWSAlgorithm( 118 alg, 119 ECDSAProvider.SUPPORTED_ALGORITHMS)); 120 } 121 122 try { 123 if (jcaProvider != null) { 124 return Signature.getInstance(jcaAlg, jcaProvider); 125 } else { 126 return Signature.getInstance(jcaAlg); 127 } 128 } catch (NoSuchAlgorithmException e) { 129 throw new JOSEException("Unsupported ECDSA algorithm: " + e.getMessage(), e); 130 } 131 } 132 133 134 /** 135 * Returns the expected signature byte array length (R + S parts) for 136 * the specified ECDSA algorithm. 137 * 138 * @param alg The ECDSA algorithm. Must be supported and not 139 * {@code null}. 140 * 141 * @return The expected byte array length for the signature. 142 * 143 * @throws JOSEException If the algorithm is not supported. 144 */ 145 public static int getSignatureByteArrayLength(final JWSAlgorithm alg) 146 throws JOSEException { 147 148 if (alg.equals(JWSAlgorithm.ES256)) { 149 150 return 64; 151 152 } else if (alg.equals(JWSAlgorithm.ES256K)) { 153 154 return 64; 155 156 } else if (alg.equals(JWSAlgorithm.ES384)) { 157 158 return 96; 159 160 } else if (alg.equals(JWSAlgorithm.ES512)) { 161 162 return 132; 163 164 } else { 165 166 throw new JOSEException(AlgorithmSupportMessage.unsupportedJWSAlgorithm( 167 alg, 168 ECDSAProvider.SUPPORTED_ALGORITHMS)); 169 } 170 } 171 172 173 /** 174 * Transcodes the JCA ASN.1/DER-encoded signature into the concatenated 175 * R + S format expected by ECDSA JWS. 176 * 177 * @param derSignature The ASN1./DER-encoded. Must not be {@code null}. 178 * @param outputLength The expected length of the ECDSA JWS signature. 179 * 180 * @return The ECDSA JWS encoded signature. 181 * 182 * @throws JOSEException If the ASN.1/DER signature format is invalid. 183 */ 184 public static byte[] transcodeSignatureToConcat(final byte[] derSignature, int outputLength) 185 throws JOSEException { 186 187 if (derSignature.length < 8 || derSignature[0] != 48) { 188 throw new JOSEException("Invalid ECDSA signature format"); 189 } 190 191 int offset; 192 if (derSignature[1] > 0) { 193 offset = 2; 194 } else if (derSignature[1] == (byte) 0x81) { 195 offset = 3; 196 } else { 197 throw new JOSEException("Invalid ECDSA signature format"); 198 } 199 200 byte rLength = derSignature[offset + 1]; 201 202 int i; 203 for (i = rLength; (i > 0) && (derSignature[(offset + 2 + rLength) - i] == 0); i--) { 204 // do nothing 205 } 206 207 byte sLength = derSignature[offset + 2 + rLength + 1]; 208 209 int j; 210 for (j = sLength; (j > 0) && (derSignature[(offset + 2 + rLength + 2 + sLength) - j] == 0); j--) { 211 // do nothing 212 } 213 214 int rawLen = Math.max(i, j); 215 rawLen = Math.max(rawLen, outputLength / 2); 216 217 if ((derSignature[offset - 1] & 0xff) != derSignature.length - offset 218 || (derSignature[offset - 1] & 0xff) != 2 + rLength + 2 + sLength 219 || derSignature[offset] != 2 220 || derSignature[offset + 2 + rLength] != 2) { 221 throw new JOSEException("Invalid ECDSA signature format"); 222 } 223 224 final byte[] concatSignature = new byte[2 * rawLen]; 225 226 System.arraycopy(derSignature, (offset + 2 + rLength) - i, concatSignature, rawLen - i, i); 227 System.arraycopy(derSignature, (offset + 2 + rLength + 2 + sLength) - j, concatSignature, 2 * rawLen - j, j); 228 229 return concatSignature; 230 } 231 232 233 234 /** 235 * Transcodes the ECDSA JWS signature into ASN.1/DER format for use by 236 * the JCA verifier. 237 * 238 * @param jwsSignature The JWS signature, consisting of the 239 * concatenated R and S values. Must not be 240 * {@code null}. 241 * 242 * @return The ASN.1/DER encoded signature. 243 * 244 * @throws JOSEException If the ECDSA JWS signature format is invalid. 245 */ 246 public static byte[] transcodeSignatureToDER(byte[] jwsSignature) 247 throws JOSEException { 248 249 // Adapted from org.apache.xml.security.algorithms.implementations.SignatureECDSA 250 251 int rawLen = jwsSignature.length / 2; 252 253 int i; 254 255 for (i = rawLen; (i > 0) && (jwsSignature[rawLen - i] == 0); i--) { 256 // do nothing 257 } 258 259 int j = i; 260 261 if (jwsSignature[rawLen - i] < 0) { 262 j += 1; 263 } 264 265 int k; 266 267 for (k = rawLen; (k > 0) && (jwsSignature[2 * rawLen - k] == 0); k--) { 268 // do nothing 269 } 270 271 int l = k; 272 273 if (jwsSignature[2 * rawLen - k] < 0) { 274 l += 1; 275 } 276 277 int len = 2 + j + 2 + l; 278 279 if (len > 255) { 280 throw new JOSEException("Invalid ECDSA signature format"); 281 } 282 283 int offset; 284 285 final byte derSignature[]; 286 287 if (len < 128) { 288 derSignature = new byte[2 + 2 + j + 2 + l]; 289 offset = 1; 290 } else { 291 derSignature = new byte[3 + 2 + j + 2 + l]; 292 derSignature[1] = (byte) 0x81; 293 offset = 2; 294 } 295 296 derSignature[0] = 48; 297 derSignature[offset++] = (byte) len; 298 derSignature[offset++] = 2; 299 derSignature[offset++] = (byte) j; 300 301 System.arraycopy(jwsSignature, rawLen - i, derSignature, (offset + j) - i, i); 302 303 offset += j; 304 305 derSignature[offset++] = 2; 306 derSignature[offset++] = (byte) l; 307 308 System.arraycopy(jwsSignature, 2 * rawLen - k, derSignature, (offset + l) - k, k); 309 310 return derSignature; 311 } 312 313 314 /** 315 * Prevents public instantiation. 316 */ 317 private ECDSA() {} 318}