001package com.nimbusds.jose.crypto; 002 003 004import java.security.InvalidKeyException; 005import java.security.Signature; 006import java.security.SignatureException; 007import java.security.interfaces.ECPrivateKey; 008 009import net.jcip.annotations.ThreadSafe; 010 011import com.nimbusds.jose.JOSEException; 012import com.nimbusds.jose.JWSAlgorithm; 013import com.nimbusds.jose.JWSHeader; 014import com.nimbusds.jose.JWSSigner; 015import com.nimbusds.jose.jwk.ECKey; 016import com.nimbusds.jose.util.Base64URL; 017 018 019/** 020 * Elliptic Curve Digital Signature Algorithm (ECDSA) signer of 021 * {@link com.nimbusds.jose.JWSObject JWS objects}. Expects a private EC key 022 * (with a P-256, P-384 or P-521 curve). 023 * 024 * <p>See RFC 7518 025 * <a href="https://tools.ietf.org/html/rfc7518#section-3.4">section 3.4</a> 026 * for more information. 027 * 028 * <p>This class is thread-safe. 029 * 030 * <p>Supports the following algorithms: 031 * 032 * <ul> 033 * <li>{@link com.nimbusds.jose.JWSAlgorithm#ES256} 034 * <li>{@link com.nimbusds.jose.JWSAlgorithm#ES384} 035 * <li>{@link com.nimbusds.jose.JWSAlgorithm#ES512} 036 * </ul> 037 * 038 * @author Axel Nennker 039 * @author Vladimir Dzhuvinov 040 * @version 2015-06-07 041 */ 042@ThreadSafe 043public class ECDSASigner extends ECDSAProvider implements JWSSigner { 044 045 046 /** 047 * The private EC key. 048 */ 049 private final ECPrivateKey privateKey; 050 051 052 /** 053 * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA) 054 * signer. 055 * 056 * @param privateKey The private EC key. Must not be {@code null}. 057 * 058 * @throws JOSEException If the elliptic curve of key is not supported. 059 */ 060 public ECDSASigner(final ECPrivateKey privateKey) 061 throws JOSEException { 062 063 super(ECDSA.resolveAlgorithm(privateKey)); 064 065 this.privateKey = privateKey; 066 } 067 068 069 /** 070 * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA) 071 * signer. 072 * 073 * @param ecJWK The EC JSON Web Key (JWK). Must contain a private part. 074 * Must not be {@code null}. 075 * 076 * @throws JOSEException If the EC JWK doesn't contain a private part, 077 * its extraction failed, or the elliptic curve 078 * is not supported. 079 */ 080 public ECDSASigner(final ECKey ecJWK) 081 throws JOSEException { 082 083 super(ECDSA.resolveAlgorithm(ecJWK.getCurve())); 084 085 if (! ecJWK.isPrivate()) { 086 throw new JOSEException("The EC JWK doesn't contain a private part"); 087 } 088 089 privateKey = ecJWK.toECPrivateKey(); 090 } 091 092 093 /** 094 * Returns the private EC key. 095 * 096 * @return The private EC key. 097 */ 098 public ECPrivateKey getPrivateKey() { 099 100 return privateKey; 101 } 102 103 104 @Override 105 public Base64URL sign(final JWSHeader header, final byte[] signingInput) 106 throws JOSEException { 107 108 final JWSAlgorithm alg = header.getAlgorithm(); 109 110 if (! supportedJWSAlgorithms().contains(alg)) { 111 throw new JOSEException(AlgorithmSupportMessage.unsupportedJWSAlgorithm(alg, supportedJWSAlgorithms())); 112 } 113 114 // DER-encoded signature, according to JCA spec 115 // (sequence of two integers - R + S) 116 final byte[] jcaSignature; 117 118 try { 119 Signature dsa = ECDSA.getSignerAndVerifier(alg, getJCAContext().getProvider()); 120 dsa.initSign(privateKey, getJCAContext().getSecureRandom()); 121 dsa.update(signingInput); 122 jcaSignature = dsa.sign(); 123 124 } catch (InvalidKeyException | SignatureException e) { 125 126 throw new JOSEException(e.getMessage(), e); 127 } 128 129 final int rsByteArrayLength = ECDSA.getSignatureByteArrayLength(header.getAlgorithm()); 130 final byte[] jwsSignature = ECDSA.transcodeSignatureToConcat(jcaSignature, rsByteArrayLength); 131 return Base64URL.encode(jwsSignature); 132 } 133}