View smime certificate of signed messages

This commit is contained in:
Francis Lachapelle
2018-01-09 15:22:04 -05:00
parent a44118d68f
commit 02d1aa12f9
10 changed files with 242 additions and 64 deletions
+6 -2
View File
@@ -1,6 +1,6 @@
/* UIxMailPartSignedViewer.h - this file is part of SOGo
*
* Copyright (C) 2009-2017 Inverse inc.
* Copyright (C) 2009-2018 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -30,11 +30,15 @@
{
BOOL processed;
BOOL validSignature;
NSMutableString *validationMessage;
NSMutableArray *certificates;
NSString *validationMessage;
}
- (BOOL) validSignature;
- (NSString *) validationMessage;
- (NSArray *) smimeCertificates;
- (NSDictionary *) certificateForSubject: (NSString *) subject
andIssuer: (NSString *) issuer;
@end
+92 -47
View File
@@ -1,6 +1,6 @@
/* UIxMailPartSignedViewer.m - this file is part of SOGo
*
* Copyright (C) 2009-2017 Inverse inc.
* Copyright (C) 2009-2018 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -53,19 +53,19 @@
success = NO;
store = X509_STORE_new ();
OpenSSL_add_all_algorithms ();
store = X509_STORE_new();
OpenSSL_add_all_algorithms();
if (store)
{
lookup = X509_STORE_add_lookup (store, X509_LOOKUP_file());
lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
if (lookup)
{
X509_LOOKUP_load_file (lookup, NULL, X509_FILETYPE_DEFAULT);
lookup = X509_STORE_add_lookup (store, X509_LOOKUP_hash_dir());
X509_LOOKUP_load_file(lookup, NULL, X509_FILETYPE_DEFAULT);
lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
if (lookup)
{
X509_LOOKUP_add_dir (lookup, NULL, X509_FILETYPE_DEFAULT);
X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT);
ERR_clear_error();
success = YES;
}
@@ -86,18 +86,15 @@
- (void) _processMessage
{
NSString *issuer, *subject;
NSData *signedData;
STACK_OF(X509) *certs;
X509_STORE *x509Store;
BIO *msgBio, *inData;
char sslError[1024];
const char* sslError;
PKCS7 *p7;
int err, i;
memset(sslError, 0, 1024);
ERR_clear_error();
if ([[self decodedFlatContent] isKindOfClass: [NGMimeMultipartBody class]])
@@ -110,45 +107,50 @@
inData = NULL;
p7 = SMIME_read_PKCS7(msgBio, &inData);
subject = nil;
issuer = nil;
certs = NULL;
certificates = [NSMutableArray array];
validationMessage = nil;
if (p7)
{
i = OBJ_obj2nid(p7->type);
if (i == NID_pkcs7_signed)
if (OBJ_obj2nid(p7->type) == NID_pkcs7_signed)
{
NSString *subject, *issuer;
X509 *x;
certs=p7->d.sign->cert;
if (sk_X509_num(certs) > 0)
{
certs = p7->d.sign->cert;
for (i = 0; i < sk_X509_num(certs); i++)
{
BIO *buf;
char p[256];
memset(p, 0, 256);
x = sk_X509_value(certs,0);
char p[1024];
x = sk_X509_value(certs, i);
memset(p, 0, 1024);
buf = BIO_new(BIO_s_mem());
X509_NAME_print_ex(buf, X509_get_subject_name(x), 0, XN_FLAG_ONELINE & ~ASN1_STRFLGS_ESC_MSB);
BIO_gets(buf, p, 256);
X509_NAME_print_ex(buf, X509_get_subject_name(x), 0,
ASN1_STRFLGS_ESC_CTRL | XN_FLAG_SEP_MULTILINE | XN_FLAG_FN_LN);
BIO_read(buf, p, 1024);
subject = [NSString stringWithUTF8String: p];
memset(p, 0, 256);
X509_NAME_print_ex(buf, X509_get_issuer_name(x), 0, XN_FLAG_ONELINE & ~ASN1_STRFLGS_ESC_MSB);
BIO_gets(buf, p, 256);
issuer = [NSString stringWithUTF8String: p];
BIO_free(buf);
memset(p, 0, 1024);
buf = BIO_new(BIO_s_mem());
X509_NAME_print_ex(buf, X509_get_issuer_name(x), 0,
ASN1_STRFLGS_ESC_CTRL | XN_FLAG_SEP_MULTILINE | XN_FLAG_FN_LN);
BIO_read(buf, p, 1024);
issuer = [NSString stringWithUTF8String: p];
BIO_free(buf);
[certificates addObject: [self certificateForSubject: subject
andIssuer: issuer]];
}
}
err = ERR_get_error();
if (err)
{
ERR_error_string_n (err, sslError, 1023);
validSignature = NO;
}
else
@@ -158,30 +160,29 @@
NULL, PKCS7_DETACHED) == 1);
err = ERR_get_error();
if (err)
ERR_error_string_n(err, sslError, 1023);
if (x509Store)
X509_STORE_free (x509Store);
}
if (err)
{
ERR_load_crypto_strings();
sslError = ERR_reason_error_string(err);
validationMessage = [[self labelForKey: [NSString stringWithUTF8String: sslError]] retain];
}
}
BIO_free (msgBio);
if (inData)
BIO_free (inData);
validationMessage = [NSMutableString string];
if (validSignature)
validationMessage = [NSString stringWithString: [self labelForKey: @"Message is signed"]];
else if (!validationMessage)
validationMessage = [NSString stringWithString: [self labelForKey: @"Digital signature is not valid"]];
if (!validSignature)
[validationMessage appendString: [self labelForKey: @"Digital signature is not valid"]];
else
[validationMessage appendString: [self labelForKey: @"Message is signed"]];
if (issuer && subject)
[validationMessage appendFormat: @"\n%@: %@\n%@: %@",
[self labelForKey: @"Subject"], subject,
[self labelForKey: @"Issuer"], issuer];
processed = YES;
}
@@ -193,6 +194,41 @@
return validSignature;
}
- (NSArray *) componentsForDN: (NSString *) dn
{
NSArray *pair;
NSEnumerator *componentsEnum;
NSMutableArray *components;
NSString *pairString;
components = [NSMutableArray array];
componentsEnum = [[dn componentsSeparatedByString: @"\n"] objectEnumerator];
while (( pairString = [componentsEnum nextObject] ))
{
pair = [pairString componentsSeparatedByString: @"="];
if ([pair count] == 2)
[components addObject: [NSArray arrayWithObjects:
[self labelForKey: [pair objectAtIndex: 0]],
[pair objectAtIndex: 1], nil]];
}
return components;
}
- (NSDictionary *) certificateForSubject: (NSString *) subject
andIssuer: (NSString *) issuer
{
return [NSDictionary dictionaryWithObjectsAndKeys:
[self componentsForDN: subject], @"subject",
[self componentsForDN: issuer], @"issuer",
nil];
}
- (NSArray *) smimeCertificates
{
return certificates;
}
- (NSString *) validationMessage
{
if (!processed)
@@ -206,6 +242,11 @@
return NO;
}
- (NSArray *) smimeCertificates
{
return nil;
}
- (BOOL) validSignature
{
return NO;
@@ -251,12 +292,16 @@
[renderedParts addObject: [viewer renderedPart]];
}
if (!processed)
[self _processMessage];
return [NSDictionary dictionaryWithObjectsAndKeys:
[self className], @"type",
[NSNumber numberWithBool: [self supportsSMIME]], @"supports-smime",
[NSNumber numberWithBool: [self validSignature]], @"valid",
[self validationMessage], @"error",
renderedParts, @"content",
[self smimeCertificates], @"certificates",
[self validationMessage], @"error",
nil];
}
@@ -246,6 +246,19 @@
/* Number of selected messages in list */
"selected" = "selected";
/* SMIME Certificate field */
"countryName" = "Country";
"organizationName" = "Organization";
"organizationalUnitName" = "Organizational Unit";
"commonName" = "Common Name";
"emailAddress" = "Email Address";
/* OpenSSL certificate error - unknown issuer */
"certificate verify error" = "This certificate was signed by an unknown issuer";
/* OpenSSL certificate error - email mismatch */
"digest failure" = "This certificate doesn't match the sender email address";
"This Folder" = "This Folder";
/* Label popup menu */
+34 -8
View File
@@ -206,16 +206,42 @@
</div>
<!-- S/MIME Signature -->
<md-whiteframe class="md-whiteframe-z2 sg-no-print" layout="row" layout-align="space-between center"
ng-show="::viewer.message.$smime.message">
<div layout="row" layout-align="start center">
<div class="md-tile-left">
<md-icon ng-hide="::viewer.message.$smime.validSignature" class="md-warn">error</md-icon>
<md-icon ng-show="::viewer.message.$smime.validSignature" class="md-accent">check</md-icon>
<sg-block-toggle class="sg-no-print" layout="column"
ng-show="::viewer.message.$smime.message">
<md-divider><!-- divider --></md-divider>
<md-list-item class="sg-button-toggle">
<div>
<md-icon ng-hide="::viewer.message.$smime.validSignature"
class="md-warn"
rsrc:md-svg-src="img/certificate-off.svg"><!-- certificate --></md-icon>
<md-icon ng-show="::viewer.message.$smime.validSignature"
class="md-accent"
rsrc:md-svg-src="img/certificate.svg"><!-- certificate --></md-icon>
</div>
<p class="md-padding md-flex" ng-bind-html="::viewer.message.$smime.message"><!-- message --></p>
<md-icon class="sg-icon-toggle">expand_more</md-icon>
</md-list-item>
<div class="sg-block-toggle">
<div class="md-margin" md-whiteframe="3">
<div class="md-padding" layout="row">
<div flex="50">
<h6 class="md-subhead md-default-theme md-fg md-primary">Subject Name</h6>
<div ng-repeat="field in ::viewer.message.$smime.certificate.subject">
<div class="pseudo-input-label" ng-bind="field[0]"><!-- label --></div>
<div class="pseudo-input-field md-body-1" ng-bind="field[1]"><!-- value --></div>
</div>
</div>
<div flex="50">
<h6 class="md-subhead md-default-theme md-fg md-primary">Issuer</h6>
<div ng-repeat="field in ::viewer.message.$smime.certificate.issuer">
<div class="pseudo-input-label" ng-bind="field[0]"><!-- label --></div>
<div class="pseudo-input-field md-body-1" ng-bind="field[1]"><!-- value --></div>
</div>
</div>
</div>
</div>
<p ng-bind-html="::viewer.message.$smime.message"><!-- message --></p>
</div>
</md-whiteframe>
</sg-block-toggle>
<!-- Load external images -->
<md-whiteframe class="md-whiteframe-z2 sg-no-print" layout="row" layout-align="space-between center"
@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414"><path d="M4.859 4.251l.013-.004 14.881 14.881-.004.013 1.599 1.599-1.262 1.263-2.538-2.538-1.477-.42-.642 2.517c-.09.358-.317.59-.683.697a1.637 1.637 0 0 1-.255.027.884.884 0 0 1-.683-.295L12 20.143l-1.808 1.848c-.25.268-.563.357-.938.268a.896.896 0 0 1-.683-.697l-.642-2.517-2.491.709c-.367.108-.679.023-.938-.254-.277-.259-.362-.571-.254-.938l.709-2.491-2.517-.642c-.358-.09-.59-.317-.697-.683-.089-.375 0-.688.268-.938L3.857 12l-1.848-1.808c-.268-.25-.357-.563-.268-.938.107-.366.339-.593.697-.683l2.517-.642-.42-1.477-2.948-2.948L2.85 2.242l2.009 2.009zm3.106.563l.606-2.377a.87.87 0 0 1 .683-.683c.367-.107.679-.022.938.255L12 3.871l1.808-1.862c.259-.268.571-.353.938-.255a.87.87 0 0 1 .683.683l.642 2.518 2.492-.709c.366-.108.678-.023.937.254.277.259.362.571.254.937l-.709 2.492 2.517.642c.358.09.59.317.697.683.089.375 0 .688-.268.938L20.143 12l1.848 1.808c.268.25.357.563.268.938-.107.366-.339.593-.697.683l-2.376.606L7.965 4.814z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414"><path d="M20.143 12l1.848 1.808c.268.25.357.563.268.938-.107.366-.339.593-.697.683l-2.517.642.709 2.491c.108.367.023.679-.254.938-.259.277-.571.362-.938.254l-2.491-.709-.642 2.517c-.09.358-.317.59-.683.697a1.637 1.637 0 0 1-.255.027.884.884 0 0 1-.683-.295L12 20.143l-1.808 1.848c-.25.268-.563.357-.938.268a.896.896 0 0 1-.683-.697l-.642-2.517-2.492.709c-.366.108-.678.023-.937-.254-.277-.259-.362-.571-.254-.938l.709-2.491-2.518-.642c-.357-.09-.589-.317-.696-.683-.089-.375 0-.688.268-.938L3.857 12l-1.848-1.808c-.268-.25-.357-.563-.268-.938.107-.366.339-.593.697-.683l2.517-.642-.709-2.492c-.108-.366-.023-.678.254-.937.259-.277.571-.362.938-.254l2.491.709.642-2.518a.87.87 0 0 1 .683-.683c.367-.107.679-.022.938.255L12 3.871l1.808-1.862c.259-.268.571-.353.938-.255a.87.87 0 0 1 .683.684l.642 2.517 2.491-.709c.367-.108.679-.023.938.254.277.259.362.571.254.938l-.709 2.491 2.517.642c.358.09.59.317.697.683.089.375 0 .688-.268.938L20.143 12z" fill-rule="nonzero"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,75 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/*
* sgBlockToggle - expandable block, collapsed by default
* @memberof SOGo.Common
* @ngInject
* @example:
<sg-block-toggle>
<md-list-item class="sg-button-toggle">
<md-icon>warning</md-icon>
<p flex>{{ message }}</p>
<md-icon class="sg-icon-toggle">expand_more</md-icon>
</md-list-item>
<div class="sg-block-toggle">
<!-- block's content -->
</div>
*/
sgBlockToggle.$inject = ['$mdUtil', '$animateCss', '$$rAF'];
function sgBlockToggle($mdUtil, $animateCss, $$rAF) {
return {
link: link
};
function link($scope, $element) {
var button = $element[0].querySelector('.sg-button-toggle'),
icon = button.querySelector('.sg-icon-toggle'),
icon_rotate_class = 'md-rotate-180-ccw',
block = $element[0].querySelector('.sg-block-toggle'),
isOpen = false;
button.classList.add('md-clickable');
angular.element(button).on('click', toggle);
renderContent();
function renderContent() {
block.setAttribute('aria-hidden', !isOpen);
block.setAttribute('aria-expanded', isOpen);
if (!isOpen)
block.style.visibility = 'hidden';
}
function toggle() {
isOpen = !isOpen;
if (isOpen)
icon.classList.add(icon_rotate_class);
else
icon.classList.remove(icon_rotate_class);
if (isOpen)
block.style.visibility = 'visible';
$$rAF(function() {
var targetHeight = isOpen ? block.scrollHeight : 0;
$animateCss(angular.element(block), {
easing: 'cubic-bezier(0.35, 0, 0.25, 1)',
to: { height: targetHeight + 'px' },
duration: 0.75 // seconds
}).start().then(function() {
renderContent();
});
});
}
}
}
angular
.module('SOGo.Common')
.directive('sgBlockToggle', sgBlockToggle);
})();
@@ -0,0 +1,8 @@
/// block-toggle.scss -*- Mode: scss; indent-tabs-mode: nil; basic-offset: 2 -*-
.sg-block-toggle {
overflow: hidden;
position: relative;
z-index: 1;
height: 0;
};
@@ -35,13 +35,16 @@
.pseudo-input-label,
.button-label {
display: inline-block;
margin-top: $mg;
color: $colorGray;
font-weight: $sg-font-regular;
line-height: $sg-line-height-1;
transform: scale($input-label-float-scale);
}
.pseudo-input-label {
margin-top: $mg;
}
.pseudo-input-label {
@include rtl(transform-origin, left top, right top);
}
@@ -64,15 +67,16 @@
border-width: 0;
}
md-select.pseudo-input-field {
md-select.pseudo-input-field,
md-checkbox.pseudo-input-field {
margin-bottom: $mg;
padding: 0;
}
// The specs dimensions are too large to fit with angular-material
// Here's a modifier
.pseudo-input-container--compact {
.pseudo-input-label {
margin-top: 0;
}
}
// .pseudo-input-container--compact {
// .pseudo-input-label {
// margin-top: 0;
// }
// }
+1
View File
@@ -74,6 +74,7 @@
@import 'components/ripple/ripple';
@import 'components/timepicker/timepicker';
@import 'components/pseudo-input/pseudo-input';
@import 'components/block-toggle/block-toggle';
@import 'views/view';
@import 'core/print';