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}. This class is thread-safe. 022 * 023 * <p>Supports the following algorithms: 024 * 025 * <ul> 026 * <li>{@link com.nimbusds.jose.JWSAlgorithm#ES256} 027 * <li>{@link com.nimbusds.jose.JWSAlgorithm#ES384} 028 * <li>{@link com.nimbusds.jose.JWSAlgorithm#ES512} 029 * </ul> 030 * 031 * @author Axel Nennker 032 * @author Vladimir Dzhuvinov 033 * @version 2015-06-07 034 */ 035@ThreadSafe 036public class ECDSASigner extends ECDSAProvider implements JWSSigner { 037 038 039 /** 040 * The private EC key. 041 */ 042 private final ECPrivateKey privateKey; 043 044 045 /** 046 * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA) 047 * signer. 048 * 049 * @param privateKey The private EC key. Must not be {@code null}. 050 * 051 * @throws JOSEException If the elliptic curve of key is not supported. 052 */ 053 public ECDSASigner(final ECPrivateKey privateKey) 054 throws JOSEException { 055 056 super(ECDSA.resolveAlgorithm(privateKey)); 057 058 this.privateKey = privateKey; 059 } 060 061 062 /** 063 * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA) 064 * signer. 065 * 066 * @param ecJWK The EC JSON Web Key (JWK). Must contain a private part. 067 * Must not be {@code null}. 068 * 069 * @throws JOSEException If the EC JWK doesn't contain a private part, 070 * its extraction failed, or the elliptic curve 071 * is not supported. 072 */ 073 public ECDSASigner(final ECKey ecJWK) 074 throws JOSEException { 075 076 super(ECDSA.resolveAlgorithm(ecJWK.getCurve())); 077 078 if (! ecJWK.isPrivate()) { 079 throw new JOSEException("The EC JWK doesn't contain a private part"); 080 } 081 082 privateKey = ecJWK.toECPrivateKey(); 083 } 084 085 086 /** 087 * Returns the private EC key. 088 * 089 * @return The private EC key. 090 */ 091 public ECPrivateKey getPrivateKey() { 092 093 return privateKey; 094 } 095 096 097 @Override 098 public Base64URL sign(final JWSHeader header, final byte[] signingInput) 099 throws JOSEException { 100 101 final JWSAlgorithm alg = header.getAlgorithm(); 102 103 if (! supportedJWSAlgorithms().contains(alg)) { 104 throw new JOSEException(AlgorithmSupportMessage.unsupportedJWSAlgorithm(alg, supportedJWSAlgorithms())); 105 } 106 107 // DER-encoded signature, according to JCA spec 108 // (sequence of two integers - R + S) 109 final byte[] jcaSignature; 110 111 try { 112 Signature dsa = ECDSA.getSignerAndVerifier(alg, getJCAContext().getProvider()); 113 dsa.initSign(privateKey, getJCAContext().getSecureRandom()); 114 dsa.update(signingInput); 115 jcaSignature = dsa.sign(); 116 117 } catch (InvalidKeyException | SignatureException e) { 118 119 throw new JOSEException(e.getMessage(), e); 120 } 121 122 final int rsByteArrayLength = ECDSA.getSignatureByteArrayLength(header.getAlgorithm()); 123 final byte[] jwsSignature = ECDSA.transcodeSignatureToConcat(jcaSignature, rsByteArrayLength); 124 return Base64URL.encode(jwsSignature); 125 } 126}