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