001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2016, Connect2id Ltd. 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; 019 020 021import java.security.InvalidKeyException; 022import java.security.PrivateKey; 023import java.security.Signature; 024import java.security.SignatureException; 025import java.security.interfaces.ECPrivateKey; 026 027import net.jcip.annotations.ThreadSafe; 028 029import com.nimbusds.jose.JOSEException; 030import com.nimbusds.jose.JWSAlgorithm; 031import com.nimbusds.jose.JWSHeader; 032import com.nimbusds.jose.JWSSigner; 033import com.nimbusds.jose.crypto.impl.AlgorithmSupportMessage; 034import com.nimbusds.jose.crypto.impl.ECDSA; 035import com.nimbusds.jose.crypto.impl.ECDSAProvider; 036import com.nimbusds.jose.jwk.Curve; 037import com.nimbusds.jose.jwk.ECKey; 038import com.nimbusds.jose.util.Base64URL; 039 040 041/** 042 * Elliptic Curve Digital Signature Algorithm (ECDSA) signer of 043 * {@link com.nimbusds.jose.JWSObject JWS objects}. Expects a private EC key 044 * (with a P-256, P-384, P-521 or secp256k1 curve). 045 * 046 * <p>See RFC 7518 047 * <a href="https://tools.ietf.org/html/rfc7518#section-3.4">section 3.4</a> 048 * for more information. 049 * 050 * <p>This class is thread-safe. 051 * 052 * <p>Supports the following algorithms: 053 * 054 * <ul> 055 * <li>{@link com.nimbusds.jose.JWSAlgorithm#ES256} 056 * <li>{@link com.nimbusds.jose.JWSAlgorithm#ES256K} 057 * <li>{@link com.nimbusds.jose.JWSAlgorithm#ES384} 058 * <li>{@link com.nimbusds.jose.JWSAlgorithm#ES512} 059 * </ul> 060 * 061 * @author Axel Nennker 062 * @author Vladimir Dzhuvinov 063 * @version 2016-11-30 064 */ 065@ThreadSafe 066public class ECDSASigner extends ECDSAProvider implements JWSSigner { 067 068 069 /** 070 * The private EC key. Represented by generic private key interface to 071 * support key stores that prevent exposure of the private key 072 * parameters via the {@link java.security.interfaces.RSAPrivateKey} 073 * API. 074 * 075 * See https://bitbucket.org/connect2id/nimbus-jose-jwt/issues/169 076 */ 077 private final PrivateKey privateKey; 078 079 080 /** 081 * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA) 082 * signer. 083 * 084 * @param privateKey The private EC key. Must not be {@code null}. 085 * 086 * @throws JOSEException If the elliptic curve of key is not supported. 087 */ 088 public ECDSASigner(final ECPrivateKey privateKey) 089 throws JOSEException { 090 091 super(ECDSA.resolveAlgorithm(privateKey)); 092 093 this.privateKey = privateKey; 094 } 095 096 097 /** 098 * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA) 099 * signer. This constructor is intended for a private EC key located 100 * in a PKCS#11 store that doesn't expose the private key parameters 101 * (such as a smart card or HSM). 102 * 103 * @param privateKey The private EC key. Its algorithm must be "EC". 104 * Must not be {@code null}. 105 * @param curve The elliptic curve for the key. Must not be 106 * {@code null}. 107 * 108 * @throws JOSEException If the elliptic curve of key is not supported. 109 */ 110 public ECDSASigner(final PrivateKey privateKey, final Curve curve) 111 throws JOSEException { 112 113 super(ECDSA.resolveAlgorithm(curve)); 114 115 if (! "EC".equalsIgnoreCase(privateKey.getAlgorithm())) { 116 throw new IllegalArgumentException("The private key algorithm must be EC"); 117 } 118 119 this.privateKey = privateKey; 120 } 121 122 123 /** 124 * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA) 125 * signer. 126 * 127 * @param ecJWK The EC JSON Web Key (JWK). Must contain a private part. 128 * Must not be {@code null}. 129 * 130 * @throws JOSEException If the EC JWK doesn't contain a private part, 131 * its extraction failed, or the elliptic curve 132 * is not supported. 133 */ 134 public ECDSASigner(final ECKey ecJWK) 135 throws JOSEException { 136 137 super(ECDSA.resolveAlgorithm(ecJWK.getCurve())); 138 139 if (! ecJWK.isPrivate()) { 140 throw new JOSEException("The EC JWK doesn't contain a private part"); 141 } 142 143 privateKey = ecJWK.toPrivateKey(); 144 } 145 146 147 /** 148 * Gets the private EC key. 149 * 150 * @return The private EC key. Casting to 151 * {@link java.security.interfaces.ECPrivateKey} may not be 152 * possible if the key is located in a PKCS#11 store that 153 * doesn't expose the private key parameters. 154 */ 155 public PrivateKey getPrivateKey() { 156 157 return privateKey; 158 } 159 160 161 @Override 162 public Base64URL sign(final JWSHeader header, final byte[] signingInput) 163 throws JOSEException { 164 165 final JWSAlgorithm alg = header.getAlgorithm(); 166 167 if (! supportedJWSAlgorithms().contains(alg)) { 168 throw new JOSEException(AlgorithmSupportMessage.unsupportedJWSAlgorithm(alg, supportedJWSAlgorithms())); 169 } 170 171 // DER-encoded signature, according to JCA spec 172 final byte[] jcaSignature; 173 try { 174 Signature dsa = ECDSA.getSignerAndVerifier(alg, getJCAContext().getProvider()); 175 dsa.initSign(privateKey, getJCAContext().getSecureRandom()); 176 dsa.update(signingInput); 177 jcaSignature = dsa.sign(); 178 179 } catch (InvalidKeyException | SignatureException e) { 180 181 throw new JOSEException(e.getMessage(), e); 182 } 183 184 final int rsByteArrayLength = ECDSA.getSignatureByteArrayLength(header.getAlgorithm()); 185 final byte[] jwsSignature = ECDSA.transcodeSignatureToConcat(jcaSignature, rsByteArrayLength); 186 return Base64URL.encode(jwsSignature); 187 } 188}