529 // writing out the contentInfo sequence |
528 // writing out the contentInfo sequence |
530 block.encode(out); |
529 block.encode(out); |
531 } |
530 } |
532 |
531 |
533 /** |
532 /** |
534 * Verifying signed data using an external chunked data source. |
|
535 */ |
|
536 public static class PKCS7Verifier { |
|
537 |
|
538 private final SignerInfo si; // Signer to verify |
|
539 private final MessageDigest md; // MessageDigest object for chunks |
|
540 private final Signature sig; // Signature object for chunks |
|
541 |
|
542 private PKCS7Verifier(SignerInfo si, MessageDigest md, Signature sig) { |
|
543 this.si = si; |
|
544 this.md = md; |
|
545 this.sig = sig; |
|
546 } |
|
547 |
|
548 public static PKCS7Verifier from(PKCS7 block, SignerInfo si) throws |
|
549 SignatureException, NoSuchAlgorithmException { |
|
550 |
|
551 try { |
|
552 MessageDigest md = null; |
|
553 Signature sig; |
|
554 |
|
555 ContentInfo content = block.getContentInfo(); |
|
556 String digestAlgname = si.getDigestAlgorithmId().getName(); |
|
557 |
|
558 // if there are authenticate attributes, feed data chunks to |
|
559 // the message digest. In this case, pv.md is not null |
|
560 if (si.authenticatedAttributes != null) { |
|
561 // first, check content type |
|
562 ObjectIdentifier contentType = (ObjectIdentifier) |
|
563 si.authenticatedAttributes.getAttributeValue( |
|
564 PKCS9Attribute.CONTENT_TYPE_OID); |
|
565 if (contentType == null || |
|
566 !contentType.equals(content.contentType)) |
|
567 return null; // contentType does not match, bad SignerInfo |
|
568 |
|
569 // now, check message digest |
|
570 byte[] messageDigest = (byte[]) |
|
571 si.authenticatedAttributes.getAttributeValue( |
|
572 PKCS9Attribute.MESSAGE_DIGEST_OID); |
|
573 |
|
574 if (messageDigest == null) // fail if there is no message digest |
|
575 return null; |
|
576 |
|
577 md = MessageDigest.getInstance(digestAlgname); |
|
578 } |
|
579 |
|
580 // put together digest algorithm and encryption algorithm |
|
581 // to form signing algorithm |
|
582 String encryptionAlgname = |
|
583 si.getDigestEncryptionAlgorithmId().getName(); |
|
584 |
|
585 // Workaround: sometimes the encryptionAlgname is actually |
|
586 // a signature name |
|
587 String tmp = AlgorithmId.getEncAlgFromSigAlg(encryptionAlgname); |
|
588 if (tmp != null) encryptionAlgname = tmp; |
|
589 String algname = AlgorithmId.makeSigAlg( |
|
590 digestAlgname, encryptionAlgname); |
|
591 |
|
592 sig = Signature.getInstance(algname); |
|
593 X509Certificate cert = si.getCertificate(block); |
|
594 |
|
595 if (cert == null) { |
|
596 return null; |
|
597 } |
|
598 if (cert.hasUnsupportedCriticalExtension()) { |
|
599 throw new SignatureException("Certificate has unsupported " |
|
600 + "critical extension(s)"); |
|
601 } |
|
602 |
|
603 // Make sure that if the usage of the key in the certificate is |
|
604 // restricted, it can be used for digital signatures. |
|
605 // XXX We may want to check for additional extensions in the |
|
606 // future. |
|
607 boolean[] keyUsageBits = cert.getKeyUsage(); |
|
608 if (keyUsageBits != null) { |
|
609 KeyUsageExtension keyUsage; |
|
610 try { |
|
611 // We don't care whether or not this extension was marked |
|
612 // critical in the certificate. |
|
613 // We're interested only in its value (i.e., the bits set) |
|
614 // and treat the extension as critical. |
|
615 keyUsage = new KeyUsageExtension(keyUsageBits); |
|
616 } catch (IOException ioe) { |
|
617 throw new SignatureException("Failed to parse keyUsage " |
|
618 + "extension"); |
|
619 } |
|
620 |
|
621 boolean digSigAllowed = ((Boolean)keyUsage.get( |
|
622 KeyUsageExtension.DIGITAL_SIGNATURE)).booleanValue(); |
|
623 |
|
624 boolean nonRepuAllowed = ((Boolean)keyUsage.get( |
|
625 KeyUsageExtension.NON_REPUDIATION)).booleanValue(); |
|
626 |
|
627 if (!digSigAllowed && !nonRepuAllowed) { |
|
628 throw new SignatureException("Key usage restricted: " |
|
629 + "cannot be used for " |
|
630 + "digital signatures"); |
|
631 } |
|
632 } |
|
633 |
|
634 PublicKey key = cert.getPublicKey(); |
|
635 sig.initVerify(key); |
|
636 return new PKCS7Verifier(si, md, sig); |
|
637 } catch (IOException e) { |
|
638 throw new SignatureException("IO error verifying signature:\n" + |
|
639 e.getMessage()); |
|
640 |
|
641 } catch (InvalidKeyException e) { |
|
642 throw new SignatureException("InvalidKey: " + e.getMessage()); |
|
643 |
|
644 } |
|
645 } |
|
646 |
|
647 public void update(byte[] data, int off, int end) |
|
648 throws SignatureException { |
|
649 if (md != null) { |
|
650 md.update(data, off, end-off); |
|
651 } else { |
|
652 sig.update(data, off, end-off); |
|
653 } |
|
654 } |
|
655 |
|
656 public SignerInfo verify() throws SignatureException { |
|
657 try { |
|
658 // if there are authenticate attributes, get the message |
|
659 // digest and compare it with the digest of data |
|
660 if (md != null) { |
|
661 // now, check message digest |
|
662 byte[] messageDigest = (byte[]) |
|
663 si.authenticatedAttributes.getAttributeValue( |
|
664 PKCS9Attribute.MESSAGE_DIGEST_OID); |
|
665 |
|
666 byte[] computedMessageDigest = md.digest(); |
|
667 |
|
668 if (!MessageDigest.isEqual( |
|
669 messageDigest, computedMessageDigest)) { |
|
670 return null; |
|
671 } |
|
672 |
|
673 // message digest attribute matched |
|
674 // digest of original data |
|
675 |
|
676 // the data actually signed is the DER encoding of |
|
677 // the authenticated attributes (tagged with |
|
678 // the "SET OF" tag, not 0xA0). |
|
679 byte[] dataSigned = si.authenticatedAttributes.getDerEncoding(); |
|
680 sig.update(dataSigned); |
|
681 } |
|
682 |
|
683 if (sig.verify(si.getEncryptedDigest())) { |
|
684 return si; |
|
685 } |
|
686 |
|
687 } catch (IOException e) { |
|
688 throw new SignatureException("IO error verifying signature:\n" + |
|
689 e.getMessage()); |
|
690 } |
|
691 return null; |
|
692 } |
|
693 } |
|
694 |
|
695 /** |
|
696 * This verifies a given SignerInfo. |
533 * This verifies a given SignerInfo. |
697 * |
534 * |
698 * @param info the signer information. |
535 * @param info the signer information. |
699 * @param bytes the DER encoded content information. |
536 * @param bytes the DER encoded content information. |
700 * |
537 * |
715 * @exception SignatureException on signature handling errors. |
552 * @exception SignatureException on signature handling errors. |
716 */ |
553 */ |
717 public SignerInfo[] verify(byte[] bytes) |
554 public SignerInfo[] verify(byte[] bytes) |
718 throws NoSuchAlgorithmException, SignatureException { |
555 throws NoSuchAlgorithmException, SignatureException { |
719 |
556 |
720 List<SignerInfo> intResult = new ArrayList<>(); |
557 Vector<SignerInfo> intResult = new Vector<SignerInfo>(); |
721 for (int i = 0; i < signerInfos.length; i++) { |
558 for (int i = 0; i < signerInfos.length; i++) { |
722 |
559 |
723 SignerInfo signerInfo = verify(signerInfos[i], bytes); |
560 SignerInfo signerInfo = verify(signerInfos[i], bytes); |
724 if (signerInfo != null) { |
561 if (signerInfo != null) { |
725 intResult.add(signerInfo); |
562 intResult.addElement(signerInfo); |
726 } |
563 } |
727 } |
564 } |
728 if (!intResult.isEmpty()) { |
565 if (intResult.size() != 0) { |
729 return intResult.toArray(new SignerInfo[intResult.size()]); |
566 |
|
567 SignerInfo[] result = new SignerInfo[intResult.size()]; |
|
568 intResult.copyInto(result); |
|
569 return result; |
730 } |
570 } |
731 return null; |
571 return null; |
732 } |
572 } |
733 |
573 |
734 /** |
574 /** |