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